From c282b1825efa8519841a435e6316c03743e5e6f7 Mon Sep 17 00:00:00 2001 From: Kailasdevdas Date: Tue, 14 Apr 2026 17:33:21 +0530 Subject: [PATCH 1/3] feat: add Bytescale image uploads --- .../migration.sql | 2 + .../migration.sql | 2 + .../20260414105925_news_media/migration.sql | 12 + backend/prisma/schema.prisma | 19 +- .../src/controllers/department.controller.js | 18 +- backend/src/controllers/doctor.controller.js | 16 +- .../src/controllers/newsMedia.controller.js | 45 ++- frontend/package-lock.json | 26 +- frontend/package.json | 1 + frontend/src/api/department.ts | 1 + frontend/src/api/doctor.ts | 1 + .../BytescaleUploader/BytescaleUploader.tsx | 109 +++++++ frontend/src/pages/Department.tsx | 12 + frontend/src/pages/Doctor.tsx | 296 ++++++++++-------- frontend/src/pages/newsMedia.tsx | 135 ++++++-- 15 files changed, 498 insertions(+), 197 deletions(-) create mode 100644 backend/prisma/migrations/20260414074145_doctor_image_field/migration.sql create mode 100644 backend/prisma/migrations/20260414102430_department_image/migration.sql create mode 100644 backend/prisma/migrations/20260414105925_news_media/migration.sql create mode 100644 frontend/src/components/BytescaleUploader/BytescaleUploader.tsx diff --git a/backend/prisma/migrations/20260414074145_doctor_image_field/migration.sql b/backend/prisma/migrations/20260414074145_doctor_image_field/migration.sql new file mode 100644 index 0000000..21ce1fc --- /dev/null +++ b/backend/prisma/migrations/20260414074145_doctor_image_field/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Doctor" ADD COLUMN "image" TEXT; diff --git a/backend/prisma/migrations/20260414102430_department_image/migration.sql b/backend/prisma/migrations/20260414102430_department_image/migration.sql new file mode 100644 index 0000000..a6be709 --- /dev/null +++ b/backend/prisma/migrations/20260414102430_department_image/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Department" ADD COLUMN "image" TEXT; diff --git a/backend/prisma/migrations/20260414105925_news_media/migration.sql b/backend/prisma/migrations/20260414105925_news_media/migration.sql new file mode 100644 index 0000000..f75efe7 --- /dev/null +++ b/backend/prisma/migrations/20260414105925_news_media/migration.sql @@ -0,0 +1,12 @@ +-- CreateTable +CREATE TABLE "NewsImage" ( + "id" SERIAL NOT NULL, + "url" TEXT NOT NULL, + "newsMediaId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "NewsImage_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "NewsImage" ADD CONSTRAINT "NewsImage_newsMediaId_fkey" FOREIGN KEY ("newsMediaId") REFERENCES "NewsMedia"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index f78df85..9c68a4b 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -21,6 +21,7 @@ model Doctor { id Int @id @default(autoincrement()) doctorId String @unique name String + image String? designation String? workingStatus String? qualification String? @@ -36,6 +37,8 @@ model Department { id Int @id @default(autoincrement()) departmentId String @unique name String + image String? + para1 String? para2 String? @@ -188,17 +191,25 @@ model EmailConfig { } model NewsMedia { - id Int @id @default(autoincrement()) - + id Int @id @default(autoincrement()) headline String content String? firstPara String? secondPara String? author String? date DateTime? + images NewsImage[] isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt +model NewsImage { + id Int @id @default(autoincrement()) + url String + newsMediaId Int + newsMedia NewsMedia @relation(fields: [newsMediaId], references: [id], onDelete: Cascade) + + createdAt DateTime @default(now()) } \ No newline at end of file diff --git a/backend/src/controllers/department.controller.js b/backend/src/controllers/department.controller.js index 17c199b..4ff80c2 100644 --- a/backend/src/controllers/department.controller.js +++ b/backend/src/controllers/department.controller.js @@ -9,6 +9,7 @@ export const getAllDepartments = async (req, res) => { const response = departments.map((dep) => ({ departmentId: dep.departmentId, name: dep.name, + image: dep.image ?? "", para1: dep.para1 ?? "", para2: dep.para2 ?? "", para3: dep.para3 ?? "", @@ -56,6 +57,7 @@ export const getDepartmentByName = async (req, res) => { const response = { departmentId: department.departmentId, name: department.name, + image: department.image ?? "", para1: department.para1 ?? "", para2: department.para2 ?? "", para3: department.para3 ?? "", @@ -78,8 +80,16 @@ export const getDepartmentByName = async (req, res) => { export async function createDepartment(req, res) { try { - const {departmentId, name, para1, para2, para3, facilities, services} = - req.body; + const { + departmentId, + name, + image, + para1, + para2, + para3, + facilities, + services, + } = req.body; if (!departmentId || !name) { return res @@ -91,6 +101,7 @@ export async function createDepartment(req, res) { data: { departmentId, name, + image, para1, para2, para3, @@ -116,12 +127,13 @@ export const updateDepartment = async (req, res) => { try { const {departmentId} = req.params; - const {name, para1, para2, para3, facilities, services} = req.body; + const {name, image, para1, para2, para3, facilities, services} = req.body; const department = await prisma.department.update({ where: {departmentId}, data: { name, + image, para1, para2, para3, diff --git a/backend/src/controllers/doctor.controller.js b/backend/src/controllers/doctor.controller.js index dde079f..48efb74 100644 --- a/backend/src/controllers/doctor.controller.js +++ b/backend/src/controllers/doctor.controller.js @@ -20,6 +20,7 @@ export const getAllDoctors = async (req, res) => { SL_NO: String(index + 1), doctorId: doc.doctorId, name: doc.name, + image: doc.image ?? "", designation: doc.designation, workingStatus: doc.workingStatus, qualification: doc.qualification, @@ -87,6 +88,7 @@ export const getDoctorByDoctorId = async (req, res) => { const response = { doctorId: doctor.doctorId, name: doctor.name, + image: doctor.image ?? "", designation: doctor.designation, workingStatus: doctor.workingStatus, qualification: doctor.qualification, @@ -164,6 +166,7 @@ export const createDoctor = async (req, res) => { const { doctorId, name, + image, designation, workingStatus, qualification, @@ -174,6 +177,7 @@ export const createDoctor = async (req, res) => { data: { doctorId, name, + image, designation, workingStatus, qualification, @@ -221,8 +225,14 @@ export const createDoctor = async (req, res) => { export const updateDoctor = async (req, res) => { try { const {doctorId} = req.params; - const {name, designation, workingStatus, qualification, departments} = - req.body; + const { + name, + designation, + image, + workingStatus, + qualification, + departments, + } = req.body; const doctor = await prisma.doctor.findUnique({ where: {doctorId}, @@ -236,7 +246,7 @@ export const updateDoctor = async (req, res) => { await prisma.doctor.update({ where: {id: doctor.id}, - data: {name, designation, workingStatus, qualification}, + data: {name, designation, image, workingStatus, qualification}, }); const oldRelations = await prisma.doctorDepartment.findMany({ diff --git a/backend/src/controllers/newsMedia.controller.js b/backend/src/controllers/newsMedia.controller.js index 32f8d20..473da8f 100644 --- a/backend/src/controllers/newsMedia.controller.js +++ b/backend/src/controllers/newsMedia.controller.js @@ -7,8 +7,13 @@ export const getAllNews = async (req, res) => { const page = parseInt(req.query.page); const limit = parseInt(req.query.limit); + const includeImages = { + images: true, + }; + if (!page && !limit) { const news = await prisma.newsMedia.findMany({ + include: includeImages, orderBy: { createdAt: "desc" }, }); @@ -20,6 +25,10 @@ export const getAllNews = async (req, res) => { SecondPara: n.secondPara, Date: n.date, Author: n.author, + Images: n.images.map((img) => ({ + id: img.id, + image: img.url, + })), })); return res.status(200).json({ @@ -36,6 +45,7 @@ export const getAllNews = async (req, res) => { const [news, total] = await Promise.all([ prisma.newsMedia.findMany({ + include: includeImages, orderBy: { createdAt: "desc" }, skip, take: currentLimit, @@ -51,6 +61,10 @@ export const getAllNews = async (req, res) => { SecondPara: n.secondPara, Date: n.date, Author: n.author, + Images: n.images.map((img) => ({ + id: img.id, + image: img.url, + })), })); return res.status(200).json({ @@ -80,6 +94,7 @@ export const getNewsById = async (req, res) => { const n = await prisma.newsMedia.findUnique({ where: { id: Number(id) }, + include: { images: true }, }); if (!n) { @@ -97,6 +112,10 @@ export const getNewsById = async (req, res) => { SecondPara: n.secondPara, Date: n.date, Author: n.author, + Images: n.images.map((img) => ({ + id: img.id, + image: img.url, + })), }; return res.status(200).json({ @@ -116,7 +135,15 @@ export const getNewsById = async (req, res) => { export const createNews = async (req, res) => { try { - const { headline, content, firstPara, secondPara, date, author } = req.body; + const { + headline, + content, + firstPara, + secondPara, + date, + author, + imageUrls, + } = req.body; if (!headline) { return res.status(400).json({ @@ -133,7 +160,13 @@ export const createNews = async (req, res) => { secondPara, date: date ? new Date(date) : null, author, + images: imageUrls + ? { + create: imageUrls.map((url) => ({ url })), + } + : undefined, }, + include: { images: true }, }); return res.status(201).json({ @@ -155,13 +188,21 @@ export const createNews = async (req, res) => { export const updateNews = async (req, res) => { try { const { id } = req.params; + const { imageUrls, ...otherData } = req.body; const news = await prisma.newsMedia.update({ where: { id: Number(id) }, data: { - ...req.body, + ...otherData, date: req.body.date ? new Date(req.body.date) : undefined, + images: imageUrls + ? { + deleteMany: {}, + create: imageUrls.map((url) => ({ url })), + } + : undefined, }, + include: { images: true }, }); return res.status(200).json({ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e1bb47c..9e8ff38 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@bytescale/sdk": "^3.53.0", "@editorjs/code": "^2.9.4", "@editorjs/delimiter": "^1.4.2", "@editorjs/editorjs": "^2.31.5", @@ -115,7 +116,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -525,6 +525,12 @@ "node": ">=6.9.0" } }, + "node_modules/@bytescale/sdk": { + "version": "3.53.0", + "resolved": "https://registry.npmjs.org/@bytescale/sdk/-/sdk-3.53.0.tgz", + "integrity": "sha512-qCeNup3pSjaklXuBrO9JeKbozZEs/PjQEvuqCiOAWLBRl6lDjd0V9gRVYqyttPimXYFoV+J/7dmPWtK6RfOABQ==", + "license": "MIT" + }, "node_modules/@codexteam/icons": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.3.3.tgz", @@ -1760,7 +1766,6 @@ "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", "license": "MIT", - "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -4093,7 +4098,6 @@ "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -4104,7 +4108,6 @@ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -4115,7 +4118,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -4177,7 +4179,6 @@ "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", @@ -4468,7 +4469,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4728,7 +4728,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5558,7 +5557,6 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5811,7 +5809,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -6466,7 +6463,6 @@ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz", "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -8131,7 +8127,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -8141,7 +8136,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -8905,8 +8899,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tailwindcss-animate": { "version": "1.0.7", @@ -9113,7 +9106,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9314,7 +9306,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -9659,7 +9650,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/frontend/package.json b/frontend/package.json index a97269e..79b6ac9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@bytescale/sdk": "^3.53.0", "@editorjs/code": "^2.9.4", "@editorjs/delimiter": "^1.4.2", "@editorjs/editorjs": "^2.31.5", diff --git a/frontend/src/api/department.ts b/frontend/src/api/department.ts index 86fc40e..7de2e4c 100644 --- a/frontend/src/api/department.ts +++ b/frontend/src/api/department.ts @@ -3,6 +3,7 @@ import apiClient from "@/api/client"; export interface Department { departmentId: string; name: string; + image?: string; para1: string; para2: string; para3: string; diff --git a/frontend/src/api/doctor.ts b/frontend/src/api/doctor.ts index 18c0014..82d2065 100644 --- a/frontend/src/api/doctor.ts +++ b/frontend/src/api/doctor.ts @@ -3,6 +3,7 @@ import apiClient from "@/api/client"; export interface Doctor { doctorId: string; name: string; + image?: string; designation?: string; workingStatus?: string; qualification?: string; diff --git a/frontend/src/components/BytescaleUploader/BytescaleUploader.tsx b/frontend/src/components/BytescaleUploader/BytescaleUploader.tsx new file mode 100644 index 0000000..7636aa7 --- /dev/null +++ b/frontend/src/components/BytescaleUploader/BytescaleUploader.tsx @@ -0,0 +1,109 @@ +import { useState, useRef } from "react"; +import * as Bytescale from "@bytescale/sdk"; +import { Button } from "@/components/ui/button"; +import { User, X, Loader2 } from "lucide-react"; + +interface BytescaleUploaderProps { + value: string; + onChange: (url: string) => void; + folderPath: "/doctors" | "/departments" | "/news"; +} + +const uploadManager = new Bytescale.UploadManager({ + apiKey: "public_223k2cv3MyaAxBeyALCfJa8EMLek", +}); + +export function BytescaleUploader({ value, onChange }: BytescaleUploaderProps) { + const [isUploading, setIsUploading] = useState(false); + const fileInputRef = useRef(null); + + const onFileSelected = async (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file) return; + + setIsUploading(true); + try { + const { fileUrl } = await uploadManager.upload({ + data: file, + path: { + folderPath: "/doctors", + }, + }); + + onChange(fileUrl); + } catch (e: any) { + console.error("Upload Error:", e); + alert(`Error:\n${e.message}`); + } finally { + setIsUploading(false); + if (fileInputRef.current) fileInputRef.current.value = ""; + } + }; + + return ( +
+
+
+ {value ? ( + <> + Doctor Profile + + + ) : ( +
+ {isUploading ? ( + + ) : ( + + )} +
+ )} +
+ + + + +
+ + {value && ( +

+ ⚠️ Make sure to save the changes by clicking the "Save Changes" button + at the bottom. +

+ )} +
+ ); +} diff --git a/frontend/src/pages/Department.tsx b/frontend/src/pages/Department.tsx index e56ac07..2e119b2 100644 --- a/frontend/src/pages/Department.tsx +++ b/frontend/src/pages/Department.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useCallback } from "react"; import { AxiosError } from "axios"; +import { BytescaleUploader } from "@/components/BytescaleUploader/BytescaleUploader"; import { getDepartmentsApi, @@ -45,6 +46,7 @@ import { interface Department { departmentId: string; name: string; + image?: string; para1: string; para2: string; para3: string; @@ -71,6 +73,7 @@ export default function DepartmentPage() { const [form, setForm] = useState({ departmentId: "", name: "", + image: "", para1: "", para2: "", para3: "", @@ -132,6 +135,7 @@ export default function DepartmentPage() { setForm({ departmentId: "", name: "", + image: "", para1: "", para2: "", para3: "", @@ -393,6 +397,14 @@ export default function DepartmentPage() {
+
+ + setForm({ ...form, image: url })} + /> +
({ doctorId: "", name: "", + image: "", designation: "", workingStatus: "", qualification: "", @@ -161,6 +166,7 @@ export default function DoctorPage() { setForm({ doctorId: "", name: "", + image: "", designation: "", workingStatus: "", qualification: "", @@ -177,6 +183,7 @@ export default function DoctorPage() { setForm({ doctorId: doc.doctorId, name: doc.name, + image: doc.image || "", designation: doc.designation, workingStatus: doc.workingStatus, qualification: doc.qualification, @@ -439,155 +446,172 @@ export default function DoctorPage() { - - + + {editing ? "Edit Doctor" : "Add Doctor"} -
-
-

- Basic Information -

-
-
- - +
+
+
+

+ Basic Information +

+
+
+ + setForm({ ...form, image: url })} + /> +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
-
- - -
-
- - -
-
- - -
-
- - + +
+

Assign Departments

+
+ {departments.map((dep) => { + const isSelected = form.departments.some( + (d: any) => d.departmentId === dep.departmentId, + ); + return ( + + ); + })} +
-
-

Assign Departments

-
- {departments.map((dep) => { - const isSelected = form.departments.some( - (d: any) => d.departmentId === dep.departmentId, - ); - return ( - - ); - })} -
+
+

+ Working Hours / Timing +

+ {form.departments.length === 0 ? ( +
+ Select a department to configure timing slots +
+ ) : ( +
+ {form.departments.map((dep: any) => { + const depName = departments.find( + (d) => d.departmentId === dep.departmentId, + )?.name; + return ( +
+
+

+ {depName} +

+ + Timing Slot + +
+
+ {DAYS.map((day) => ( +
+ + + handleTimingChange( + dep.departmentId, + day, + e.target.value, + ) + } + /> +
+ ))} +
+
+ ); + })} +
+ )}
- -
-

- Working Hours / Timing -

- {form.departments.length === 0 ? ( -
- Select a department to configure timing slots -
- ) : ( -
- {form.departments.map((dep: any) => { - const depName = departments.find( - (d) => d.departmentId === dep.departmentId, - )?.name; - return ( -
-
-

- {depName} -

- - Timing Slot - -
-
- {DAYS.map((day) => ( -
- - - handleTimingChange( - dep.departmentId, - day, - e.target.value, - ) - } - /> -
- ))} -
-
- ); - })} -
- )} -
- +