diff --git a/src/app/prescription/update/page.tsx b/src/app/prescription/update/page.tsx deleted file mode 100644 index 615386c..0000000 --- a/src/app/prescription/update/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -"use client"; -import BaseLayout from "@/components/layouts/BaseLayout"; -import UpdatePrescription from "@/components/page/prescription/UpdatePrescription"; -import React from "react"; - -const page = () => { - return ( - - - - ); -}; - -export default page; diff --git a/src/common/funcs/selectData.ts b/src/common/funcs/selectData.ts index 459cd88..88cb2d6 100644 --- a/src/common/funcs/selectData.ts +++ b/src/common/funcs/selectData.ts @@ -8,9 +8,6 @@ export function getDateRange(range: number): { today.setHours(0, 0, 0, 0); switch (range) { - case TYPE_DATE.ALL: - return { from: null, to: null }; - // Tuần này case TYPE_DATE.TODAY: return { from: new Date(), to: new Date() }; diff --git a/src/components/customs/FilterMany.tsx b/src/components/customs/FilterMany.tsx index b289c66..8f94976 100644 --- a/src/components/customs/FilterMany.tsx +++ b/src/components/customs/FilterMany.tsx @@ -11,6 +11,7 @@ import clsx from "clsx"; import { Check, ChevronDown } from "lucide-react"; import Button from "./custom-button"; import { removeVietnameseTones } from "@/common/funcs/optionConvert"; +import CustomButton from "./custom-button"; interface OptionItem { uuid: T; @@ -177,15 +178,15 @@ function FilterMany({ {/* ACTION */}
- + - +
)} diff --git a/src/components/page/Home/MainPageHome/MainPageHome.tsx b/src/components/page/Home/MainPageHome/MainPageHome.tsx index def620f..497b8f4 100644 --- a/src/components/page/Home/MainPageHome/MainPageHome.tsx +++ b/src/components/page/Home/MainPageHome/MainPageHome.tsx @@ -55,7 +55,7 @@ const MainPageHome = () => { const res = await httpRequest({ showMessageFailed: true, http: consultationServices.getConsultationsReport({ - FormDate: today, + FromDate: today, ToDate: today, }), }); diff --git a/src/components/page/consultation/DetailConsultation/DetailConsultation.tsx b/src/components/page/consultation/DetailConsultation/DetailConsultation.tsx index 385ccd0..11692a4 100644 --- a/src/components/page/consultation/DetailConsultation/DetailConsultation.tsx +++ b/src/components/page/consultation/DetailConsultation/DetailConsultation.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useEffect, useMemo } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useQuery } from "@tanstack/react-query"; @@ -10,6 +10,7 @@ import { QUERY_KEY, TYPE_STATUS } from "@/constant/config/enum"; import { ArrowLeft, Pencil } from "lucide-react"; import consultationServices, { + AttachmentFile, ConsultationDetail, } from "@/services/consultationServices"; @@ -20,6 +21,7 @@ import GridColumn from "@/components/layouts/GridColumn"; import DataWrapper from "@/components/customs/DataWrapper"; import Table from "@/components/customs/custom-table"; import CustomButton from "@/components/customs/custom-button"; +import Pagination from "@/components/customs/custom-pagination"; const DetailConsultation = () => { const params = useParams(); @@ -29,6 +31,9 @@ const DetailConsultation = () => { const consultationId = params?.id as string; + const [prescriptionPage, setPrescriptionPage] = useState(1); + const [prescriptionPageSize, setPrescriptionPageSize] = useState(10); + const consulDetailQuery = useQuery({ queryKey: [QUERY_KEY.chi_tiet_phien_kham, consultationId], @@ -77,6 +82,17 @@ const DetailConsultation = () => { })) || [] ); }, [consultation?.specialtyClinics, consultationId]); + // Tính toán cắt mảng dữ liệu thuốc để hiển thị phân trang trên Client + const paginatedPrescriptionItems = useMemo(() => { + const items = consultation?.aggregatedPrescriptionItems || []; + const startIndex = (prescriptionPage - 1) * prescriptionPageSize; + const endIndex = startIndex + prescriptionPageSize; + return items.slice(startIndex, endIndex); + }, [ + consultation?.aggregatedPrescriptionItems, + prescriptionPage, + prescriptionPageSize, + ]); const specialtyClinics = consultation?.specialtyClinics; const selectedSpecialty = useMemo(() => { @@ -104,6 +120,17 @@ const DetailConsultation = () => { type PrescriptionItem = ConsultationDetail["aggregatedPrescriptionItems"][number]; + const getFileInfo = (file: AttachmentFile | string, index: number) => { + if (typeof file === "string") { + return { + fileName: file.split("/").pop() || `File ${index + 1}`, + path: file, + }; + } + + return file; + }; + return (
@@ -122,16 +149,17 @@ const DetailConsultation = () => {
- {consultation.status === TYPE_STATUS.PendingPrescription && canUpdateConsultation && ( - } - href={`/consultation/update?_id=${consultation.id}`} - > - Cập nhật tổng quát - - )} + {consultation.status === TYPE_STATUS.PendingPrescription && + canUpdateConsultation && ( + } + href={`/consultation/update?_id=${consultation.id}`} + > + Cập nhật tổng quát + + )}
@@ -283,6 +311,61 @@ const DetailConsultation = () => { .join(", ") || "---"}

+ +
+

+ FILE UPLOAD +

+ + {consultation.generalAttachmentUrls?.length > 0 ? ( +
    + {consultation.generalAttachmentUrls.map((item, index) => { + const file = getFileInfo(item, index); + + const extension = + file.fileName.split(".").pop()?.toLowerCase() || ""; + + const isImage = [ + "jpg", + "jpeg", + "png", + "gif", + "webp", + "bmp", + "svg", + ].includes(extension); + + return ( +
  • + {isImage ? ( + + 🖼️ {file.fileName} + + ) : ( + + 📄 {file.fileName} + + )} +
  • + ); + })} +
+ ) : ( +

+ Không có file nào được tải lên. +

+ )} +
+

TRẠNG THÁI @@ -341,6 +424,7 @@ const DetailConsultation = () => { Danh sách đơn thuốc

+ { note="Vui lòng thử lại sau" > item.id} - data={consultation.aggregatedPrescriptionItems} + // Nếu item không có thuộc tính id, bạn có thể đổi thành (item, index) => String(index) + rowKey={(item: PrescriptionItem) => { + // Tìm vị trí của item hiện tại trong mảng paginatedPrescriptionItems + const idx = paginatedPrescriptionItems.indexOf(item); + return item.id || String(idx); + }} + data={paginatedPrescriptionItems} // Hiển thị dữ liệu trang hiện tại column={[ { title: "Tên thuốc", render: (item: PrescriptionItem) => ( - {item.medicineName} + + {item.medicineName} + ), }, { @@ -378,12 +469,29 @@ const DetailConsultation = () => { { title: "Hướng dẫn", render: (item: PrescriptionItem) => ( - {item.instructions || "---"} + + {item.instructions || "---"} + ), }, ]} /> + + {/* Chỉ hiển thị phân trang khi tổng số lượng thuốc lớn hơn 10 phần tử */} + {(consultation.aggregatedPrescriptionItems?.length || 0) > 10 && ( + setPrescriptionPage(value)} + onSetPageSize={(value) => { + setPrescriptionPageSize(value); + setPrescriptionPage(1); // Reset về trang 1 khi thay đổi kích thước hiển thị hàng + }} + dependencies={[prescriptionPage, prescriptionPageSize]} + /> + )} @@ -437,6 +545,58 @@ const DetailConsultation = () => {

{selectedSpecialty.procedureNotes || "---"}

+
+

+ FILE UPLOAD +

+ + {selectedSpecialty.attachmentUrls?.length > 0 ? ( +
    + {selectedSpecialty.attachmentUrls.map((item, index) => { + const file = getFileInfo(item, index); + + const extension = + file.fileName.split(".").pop()?.toLowerCase() || ""; + + const isImage = [ + "jpg", + "jpeg", + "png", + "gif", + "webp", + "bmp", + "svg", + ].includes(extension); + + return ( +
  • + {isImage ? ( + + 🖼️ {file.fileName} + + ) : ( + + 📄 {file.fileName} + + )} +
  • + ); + })} +
+ ) : ( +

Không có file nào được tải lên.

+ )} +
+ {selectedSpecialty.glassRequired && ( <>
diff --git a/src/components/page/consultation/UpdateConsultation/UpdateConsultation.tsx b/src/components/page/consultation/UpdateConsultation/UpdateConsultation.tsx index 624d912..8652e4b 100644 --- a/src/components/page/consultation/UpdateConsultation/UpdateConsultation.tsx +++ b/src/components/page/consultation/UpdateConsultation/UpdateConsultation.tsx @@ -7,7 +7,6 @@ import { useParams, useRouter, useSearchParams } from "next/navigation"; import CustomButton from "@/components/customs/custom-button"; import FormCustom from "@/components/utils/FormCustom"; import InputForm from "@/components/utils/FormCustom/components/InputForm"; -import TextArea from "@/components/utils/FormCustom/components/TextArea"; import GridColumn from "@/components/layouts/GridColumn"; import { QUERY_KEY } from "@/constant/config/enum"; @@ -16,6 +15,8 @@ import { httpRequest } from "@/services"; import consultationServices, { ConsultationDetail, } from "@/services/consultationServices"; +import uploadServices from "@/services/uploadServices"; +import UploadMultipleFile from "@/components/utils/UploadMultipleFile"; /* ========================= TYPES @@ -28,6 +29,7 @@ interface IUpdateConsul { treatmentPlan: string; doctorNotes: string; isGlassesDelivered: boolean; + generalAttachmentUrls: string[]; } /* ========================= @@ -41,6 +43,7 @@ const defaultForm: IUpdateConsul = { treatmentPlan: "", doctorNotes: "", isGlassesDelivered: false, + generalAttachmentUrls: [], }; /* ========================= @@ -57,6 +60,9 @@ const UpdateConsultation = () => { const idFromSearch = searchParams?.get("_id") ?? undefined; const id = idFromParams || idFromSearch || ""; + const [images, setImages] = useState([]); + const [uploading, setUploading] = useState(false); + const [form, setForm] = useState(defaultForm); /* ========================= @@ -86,6 +92,7 @@ const UpdateConsultation = () => { doctorNotes: null, createdBy: "", isDeleted: false, + generalAttachmentUrls: [], } ); }, @@ -106,18 +113,25 @@ const UpdateConsultation = () => { treatmentPlan: consul.treatmentPlan || "", doctorNotes: consul.doctorNotes || "", isGlassesDelivered: consul.isGlassesDelivered, + generalAttachmentUrls: consul.generalAttachmentUrls || [], }); + + setImages( + (consul.generalAttachmentUrls || []).map((url) => ({ + file: null, + img: url, + path: url, + fileName: url.split("/").pop(), + })), + ); }, [consulDetailQuery.data]); /* ========================= UPDATE MUTATION ========================= */ - const updateConsultationMutation = useMutation({ - mutationFn: async () => { - if (!id || id === "undefined") { - throw new Error("Không tìm thấy ID phiên khám!"); - } + const updateConsultationMutation = useMutation({ + mutationFn: async (body: { paths: string[] }) => { return httpRequest({ showMessageFailed: true, showMessageSuccess: true, @@ -129,31 +143,60 @@ const UpdateConsultation = () => { diagnosis: form.diagnosis, treatmentPlan: form.treatmentPlan, doctorNotes: form.doctorNotes, - isGlassesDelivered: form?.isGlassesDelivered, + isGlassesDelivered: form.isGlassesDelivered, + fileUrls: body.paths, }), }); }, - onSuccess(data) { - if (!data) return; - - // Làm mới danh sách và chi tiết phiên khám trong cache + onSuccess() { queryClient.invalidateQueries({ queryKey: [QUERY_KEY.table_list_consultation], }); + queryClient.invalidateQueries({ queryKey: [QUERY_KEY.chi_tiet_phien_kham, id], }); router.back(); - // Quay lại trang quản lý danh sách phiên khám - router.push(PATH.CONSULTATION || "/consultation"); + router.push(PATH.CONSULTATION); }, }); - const handleSubmit = () => { - updateConsultationMutation.mutate(); + const handleSubmit = async () => { + try { + const currentImage = images + .filter((item) => !!item.img) + .map((item) => item.img); + + const files = images + .filter((item) => !!item.file) + .map((item) => item.file); + + if (files.length > 0) { + const uploadResult: any = await uploadServices.uploadImage({ + Files: files, + SessionCode: consulDetailQuery.data?.sessionCode, + }); + + console.log("Cấu trúc log kiểm tra uploadResult:", uploadResult); + + // CẬP NHẬT Ở ĐÂY: Lấy trực tiếp từ uploadResult hoặc bóc tách đúng tầng data + const uploadedUrls = + uploadResult?.fileUrls || uploadResult?.data?.fileUrls || []; + + return updateConsultationMutation.mutate({ + paths: [...currentImage, ...uploadedUrls], + }); + } + + return updateConsultationMutation.mutate({ + paths: currentImage, + }); + } catch (error) { + console.error(error); + } }; const isLoading = @@ -281,6 +324,15 @@ const UpdateConsultation = () => { } /> +
+ + + + + {uploading && ( +

Đang upload file...

+ )} +
{ const consultationId = searchParams.get("_id") || ""; const specialtyClinicId = searchParams.get("specialtyClinicId") || ""; + const [images, setImages] = useState([]); + const [uploading, setUploading] = useState(false); const [form, setForm] = useState<{ specialtyAssignmentId: string; @@ -31,6 +35,7 @@ const UpdateSpecialtyClinicId = () => { procedureNotes: string; glassRequired: boolean; glassType: string; + fileUrls: string[]; }>({ specialtyAssignmentId: "", specialtyNotes: "", @@ -38,6 +43,7 @@ const UpdateSpecialtyClinicId = () => { procedureNotes: "", glassRequired: false, glassType: "", + fileUrls: [], }); /* ========================= @@ -79,6 +85,7 @@ const UpdateSpecialtyClinicId = () => { glassType: null, examinerName: "", specialtyStatus: 1, + attachmentUrls: [], sharedItems: [], }, ], @@ -120,14 +127,23 @@ const UpdateSpecialtyClinicId = () => { procedureNotes: specialtyClinic.procedureNotes || "", glassRequired: specialtyClinic.glassRequired || false, glassType: specialtyClinic.glassType || "", + fileUrls: specialtyClinic.attachmentUrls || [], }); + setImages( + (consul.generalAttachmentUrls || []).map((url) => ({ + file: null, + img: url, + path: url, + })), + ); }, [consulDetailQuery.data, specialtyClinicId]); /* ========================= UPDATE MUTATION ========================= */ + const specialtyResultsMutation = useMutation({ - mutationFn: async () => { + mutationFn: async (body: { paths: string[] }) => { if (!consultationId) { throw new Error("Không tìm thấy ID phiên khám!"); } @@ -145,6 +161,7 @@ const UpdateSpecialtyClinicId = () => { // Nếu không phải khoa mắt, gán mặc định false và chuỗi rỗng khi gửi API glassRequired: isEyeClinic ? form.glassRequired : false, glassType: isEyeClinic ? form.glassType : "", + fileUrls: body.paths, }, ), }); @@ -165,8 +182,39 @@ const UpdateSpecialtyClinicId = () => { }, }); - const handleSubmit = () => { - specialtyResultsMutation.mutate(); + const handleSubmit = async () => { + try { + const currentImage = images + .filter((item) => !!item.img) + .map((item) => item.img); + + const files = images + .filter((item) => !!item.file) + .map((item) => item.file); + + if (files.length > 0) { + const uploadResult: any = await uploadServices.uploadImage({ + Files: files, + SessionCode: consulDetailQuery.data?.sessionCode, + }); + + console.log("Cấu trúc log kiểm tra uploadResult:", uploadResult); + + // CẬP NHẬT Ở ĐÂY: Lấy trực tiếp từ uploadResult hoặc bóc tách đúng tầng data + const uploadedUrls = + uploadResult?.fileUrls || uploadResult?.data?.fileUrls || []; + + return specialtyResultsMutation.mutate({ + paths: [...currentImage, ...uploadedUrls], + }); + } + + return specialtyResultsMutation.mutate({ + paths: currentImage, + }); + } catch (error) { + console.error(error); + } }; const isLoading = @@ -311,6 +359,15 @@ const UpdateSpecialtyClinicId = () => {
)} +
+ + + + + {uploading && ( +

Đang upload file...

+ )} +
{/* BUTTON ACTION */}
diff --git a/src/components/page/prescription/CreatePrescription/CreatePrescription.tsx b/src/components/page/prescription/CreatePrescription/CreatePrescription.tsx index 919e77d..dc33c2f 100644 --- a/src/components/page/prescription/CreatePrescription/CreatePrescription.tsx +++ b/src/components/page/prescription/CreatePrescription/CreatePrescription.tsx @@ -17,6 +17,7 @@ import consultationServices, { } from "@/services/consultationServices"; import { QUERY_KEY } from "@/constant/config/enum"; import { toastWarn } from "@/common/funcs/toast"; +import uploadServices from "@/services/uploadServices"; /* ========================= TYPES @@ -200,6 +201,9 @@ const CreatePrescription = () => { /* ========================= SUBMIT VALIDATION ========================= */ + /* ========================= + SUBMIT VALIDATION +========================= */ const handleSubmit = async () => { try { // Kiểm tra dữ liệu đầu vào phía client @@ -225,13 +229,20 @@ const CreatePrescription = () => { }); } - // 1. Thực hiện gọi API upload ảnh - setLoading(true); - const uploadResponse: any = await prescriptionServices.uploadImage( - file, - folderName, + // Lấy thông tin phiên khám đang được chọn để lấy SessionCode (Mã phiên khám) + const selectedSession = examinationSessionOptions.find( + (item) => item.id === form.examinationSessionSpecialtyId, ); + // 1. Thực hiện gọi API upload ảnh với cấu trúc chính xác + setLoading(true); + + // SỬA TẠI ĐÂY: Truyền đúng định dạng Object chứa mảng Files giống như trang cập nhật + const uploadResponse: any = await uploadServices.uploadImage({ + Files: [file], // Bọc file đơn lẻ vào mảng vì API yêu cầu danh sách file + SessionCode: selectedSession?.sessionCode || "", // Truyền mã phiên khám tương ứng + }); + console.log("KẾT QUẢ PHẢN HỒI GỐC TỪ API UPLOAD:", uploadResponse); // Trích xuất URL linh hoạt tùy thuộc vào bộ bọc interceptor của axiosClient @@ -245,6 +256,10 @@ const CreatePrescription = () => { } else if (uploadResponse?.url) { imageUrl = uploadResponse.url; } + // Hỗ trợ trường hợp API trả về mảng fileUrls giống như kết quả POST /api/Media/upload bạn cung cấp + else if (uploadResponse?.fileUrls && uploadResponse.fileUrls.length > 0) { + imageUrl = uploadResponse.fileUrls[0]; + } // Trường hợp 2: axiosClient trả về nguyên bản cấu trúc AxiosResponse chứa thuộc tính .data else if (uploadResponse?.data) { const innerData = uploadResponse.data; @@ -252,8 +267,10 @@ const CreatePrescription = () => { imageUrl = innerData; } else { imageUrl = + innerData?.fileUrls?.[0] || // Đọc mảng fileUrls bên trong data innerData?.fileUrl || innerData?.url || + innerData?.data?.fileUrls?.[0] || innerData?.data?.fileUrl || innerData?.data?.url || ""; diff --git a/src/components/page/prescription/UpdatePrescription/UpdatePrescription.tsx b/src/components/page/prescription/UpdatePrescription/UpdatePrescription.tsx deleted file mode 100644 index 0e9d1b4..0000000 --- a/src/components/page/prescription/UpdatePrescription/UpdatePrescription.tsx +++ /dev/null @@ -1,533 +0,0 @@ -"use client"; - -import React, { useEffect, useMemo, useState } from "react"; - -import { Plus, Trash2 } from "lucide-react"; -import { useMutation, useQuery } from "@tanstack/react-query"; -import { useParams, useRouter, useSearchParams } from "next/navigation"; - -import CustomButton from "@/components/customs/custom-button"; -import FormCustom from "@/components/utils/FormCustom"; -import InputForm from "@/components/utils/FormCustom/components/InputForm"; -import TextArea from "@/components/utils/FormCustom/components/TextArea"; -import SelectForm from "@/components/utils/FormCustom/components/SelectForm"; -import UploadMultipleFile from "@/components/utils/UploadMultipleFile"; - -import { httpRequest } from "@/services"; -import prescriptionServices from "@/services/prescriptionServices"; - -import consultationServices, { - ConsultationItem, - ConsultationResponse, -} from "@/services/consultationServices"; - -import { QUERY_KEY } from "@/constant/config/enum"; -import { PATH } from "@/constant/config"; - -import { toastWarn } from "@/common/funcs/toast"; - -import type { PrescriptionDetail } from "@/services/prescriptionServices"; - -/* ========================= - TYPES -========================= */ - -interface PrescriptionItem { - id: string; - medicineName: string; - dosage: string; - frequency: string; - duration: string; - instructions: string; -} - -interface PrescriptionForm { - examinationSessionId: string; - notes: string; - prescriptionImg: string; - items: PrescriptionItem[]; -} - -interface UploadImageItem { - url?: string; - path?: string; - img?: string; -} - -/* ========================= - DEFAULT -========================= */ - -const defaultMedicine: PrescriptionItem = { - id: "", - medicineName: "", - dosage: "", - frequency: "", - duration: "", - instructions: "", -}; - -/* ========================= - COMPONENT -========================= */ - -const UpdatePrescription = () => { - const router = useRouter(); - - const params = useParams(); - - const searchParams = useSearchParams(); - - const idFromParams = params?.id as string | undefined; - - const idFromSearch = searchParams?.get("_id") ?? undefined; - - const id = idFromParams || idFromSearch || ""; - - /* ========================= - GET EXAMINATION SESSION - ========================= */ - - const examinationSessionQuery = useQuery({ - queryKey: [QUERY_KEY.table_list_consultation], - - queryFn: async () => { - const res = await httpRequest({ - showMessageFailed: true, - - http: consultationServices.getConsultations({ - Page: 1, - PageSize: 100, - SortBy: "id", - Desc: true, - }), - }); - - return ( - res || { - items: [], - page: 1, - pageSize: 10, - total: 0, - totalPages: 0, - } - ); - }, - }); - - const examinationSessionOptions = examinationSessionQuery.data?.items || []; - - /* ========================= - GET DETAIL PRESCRIPTION - ========================= */ - - const detailQuery = useQuery({ - queryKey: [QUERY_KEY.chi_tiet_don_thuoc, id], - - enabled: !!id, - - queryFn: async () => { - const res = await httpRequest({ - showMessageFailed: true, - - http: prescriptionServices.detailPrescription(id), - }); - - return ( - res || { - id: "", - examinationSessionId: "", - prescriptionCode: "", - prescriptionImg: null, - notes: "", - createdBy: "", - items: [], - } - ); - }, - }); - - /* ========================= - MAP DATA - ========================= */ - - const mappedForm: PrescriptionForm = useMemo(() => { - const data = detailQuery.data; - - if (!data) { - return { - examinationSessionId: "", - notes: "", - prescriptionImg: "", - items: [defaultMedicine], - }; - } - - return { - examinationSessionId: data.examinationSessionId ?? "", - - notes: data.notes ?? "", - - prescriptionImg: data.prescriptionImg ?? "", - - items: - data.items && data.items.length > 0 - ? data.items.map((i) => ({ - id: i.id, - medicineName: i.medicineName ?? "", - dosage: i.dosage ?? "", - frequency: i.frequency ?? "", - duration: i.duration ?? "", - instructions: i.instructions ?? "", - })) - : [defaultMedicine], - }; - }, [detailQuery.data]); - - /* ========================= - FORM STATE - ========================= */ - - const [form, setForm] = useState({ - examinationSessionId: "", - notes: "", - prescriptionImg: "", - items: [defaultMedicine], - }); - - const [images, setImages] = useState([]); - - const [initialized, setInitialized] = useState(false); - - /* ========================= - INIT FORM - ========================= */ - - useEffect(() => { - if (!detailQuery.data || initialized) return; - - queueMicrotask(() => { - setForm(mappedForm); - - setImages( - mappedForm.prescriptionImg ? [{ url: mappedForm.prescriptionImg }] : [], - ); - - setInitialized(true); - }); - }, [detailQuery.data, initialized, mappedForm]); - - /* ========================= - UPDATE MUTATION - ========================= */ - - const updateMutation = useMutation({ - mutationFn: async () => { - if (!id || id === "undefined") { - throw new Error("Không tìm thấy ID đơn thuốc!"); - } - - const payload = { - examinationSessionId: form.examinationSessionId, - notes: form.notes, - prescriptionImg: form.prescriptionImg, - - items: form.items - .filter((item) => item.medicineName.trim() !== "") - .map((item) => ({ - ...(item.id ? { id: item.id } : {}), - - medicineName: item.medicineName, - dosage: item.dosage, - frequency: item.frequency, - duration: item.duration, - instructions: item.instructions, - })), - }; - - console.log("PAYLOAD UPDATE:", payload); - - return await httpRequest({ - showMessageFailed: true, - showMessageSuccess: true, - msgSuccess: "Cập nhật đơn thuốc thành công!", - - http: prescriptionServices.putPrescription(id, payload), - }); - }, - - onSuccess: () => { - router.push(PATH.PRESCRIPTION); - }, - - onError: (error: any) => { - console.log("UPDATE ERROR:", error); - - const message = - error?.response?.data?.message || error?.message || "Có lỗi xảy ra"; - - toastWarn({ - msg: message, - }); - }, - }); - - /* ========================= - HANDLERS - ========================= */ - - const handleChangeMedicine = ( - index: number, - key: keyof PrescriptionItem, - value: string, - ) => { - const clone = [...form.items]; - - clone[index] = { - ...clone[index], - [key]: value, - }; - - setForm((prev) => ({ - ...prev, - items: clone, - })); - }; - - const handleAddMedicine = () => { - setForm((prev) => ({ - ...prev, - - items: [ - ...prev.items, - { - id: "", - medicineName: "", - dosage: "", - frequency: "", - duration: "", - instructions: "", - }, - ], - })); - }; - - const handleRemoveMedicine = (index: number) => { - setForm((prev) => ({ - ...prev, - - items: prev.items.filter((_, i) => i !== index), - })); - }; - - const handleSubmit = () => { - updateMutation.mutate(); - }; - - const isLoading = updateMutation.isPending || detailQuery.isLoading; - - /* ========================= - UI - ========================= */ - - return ( - -
- {/* HEADER */} -
-
-

- Cập nhật đơn thuốc -

- -

- Chỉnh sửa đơn thuốc cho bệnh nhân -

-
-
- - {isLoading ? "Đang cập nhật..." : "Lưu đơn thuốc"} - -
-
- - {/* GENERAL INFO */} -
-

- Thông tin chung -

- -
- {/* EXAM SESSION */} - - Phiên khám * - - } - placeholder="Chọn phiên khám" - value={form.examinationSessionId} - readOnly - options={examinationSessionOptions} - onSelect={(item: ConsultationItem) => - setForm((prev) => ({ - ...prev, - examinationSessionId: item.id, - })) - } - onClean={() => - setForm((prev) => ({ - ...prev, - examinationSessionId: "", - })) - } - getOptionLabel={(item: ConsultationItem) => item.sessionCode} - getOptionValue={(item: ConsultationItem) => item.id} - /> - - {/* IMAGE */} - { - setImages(files); - - const imagePath = - files?.[0]?.path || files?.[0]?.url || files?.[0]?.img || ""; - - setForm((prev) => ({ - ...prev, - prescriptionImg: imagePath, - })); - }} - /> - - {/* NOTES */} -