"use client"; import React, { useState } from "react"; import { Plus, Trash2 } from "lucide-react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useRouter } 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 UploadImage from "@/components/utils/UploadImage"; import { httpRequest } from "@/services"; import prescriptionServices from "@/services/prescriptionServices"; import consultationServices, { ConsultationResponse, } from "@/services/consultationServices"; import { QUERY_KEY } from "@/constant/config/enum"; import { toastWarn } from "@/common/funcs/toast"; /* ========================= TYPES ========================= */ interface PrescriptionItem { medicineName: string; dosage: string; frequency: string; duration: string; instructions: string; } interface PrescriptionForm { examinationSessionSpecialtyId: string; notes: string; prescriptionImg: string; items: PrescriptionItem[]; } /* ========================= DEFAULT ========================= */ const defaultMedicine: PrescriptionItem = { medicineName: "", dosage: "", frequency: "", duration: "", instructions: "", }; /* ========================= COMPONENT ========================= */ const CreatePrescription = () => { const router = useRouter(); const queryClient = useQueryClient(); const [loading, setLoading] = useState(false); const queryKeys = [QUERY_KEY.table_list_consultation]; const [file, setFile] = useState(null); const [form, setForm] = useState({ examinationSessionSpecialtyId: "", notes: "", prescriptionImg: "", items: [defaultMedicine], }); /* ========================= 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 ?.filter((session) => { const isWaitingPrescription = session.status === 4; const allSpecialtyCompleted = session.specialtyClinics?.every( (specialty) => specialty.specialtyStatus === 3, ); return isWaitingPrescription && allSpecialtyCompleted; }) .flatMap((session) => session.specialtyClinics .filter( (specialty) => specialty.specialtyStatus === 3 && specialty.sharedItems.length === 0, ) .map((specialty) => ({ id: specialty.id, specialtyName: specialty.specialtyName, sessionCode: session.sessionCode, patientName: session.patientName, })), ) || []; /* ========================= CREATE MUTATION ========================= */ const funcCreatePrescription = useMutation({ mutationFn: (payload: any) => { console.log("=== GỬI PAYLOAD LÊN API TẠO ĐƠN THUỐC ===", payload); return httpRequest({ showMessageFailed: true, showMessageSuccess: true, msgSuccess: "Tạo đơn thuốc thành công!", http: prescriptionServices.createPrescription(payload), }); }, onSuccess(data) { console.log("TẠO ĐƠN THUỐC THÀNH CÔNG:", data); queryKeys.forEach((key) => { queryClient.invalidateQueries({ queryKey: [key], }); }); setForm({ examinationSessionSpecialtyId: "", notes: "", prescriptionImg: "", items: [defaultMedicine], }); setFile(null); // Điều hướng quay lại danh sách router.back(); }, onError(error: any) { console.error("LỖI HỆ THỐNG API TẠO ĐƠN THUỐC:", error); }, }); /* ========================= CHANGE MEDICINE FIELD ========================= */ const handleChangeMedicine = ( index: number, key: keyof PrescriptionItem, value: string, ) => { const cloneItems = [...form.items]; cloneItems[index] = { ...cloneItems[index], [key]: value, }; setForm((prev) => ({ ...prev, items: cloneItems, })); }; /* ========================= ADD MEDICINE ========================= */ const handleAddMedicine = () => { setForm((prev) => ({ ...prev, items: [...prev.items, { ...defaultMedicine }], })); }; /* ========================= REMOVE MEDICINE ========================= */ const handleRemoveMedicine = (index: number) => { const newItems = form.items.filter((_, i) => i !== index); setForm((prev) => ({ ...prev, items: newItems, })); }; /* ========================= SUBMIT VALIDATION ========================= */ const handleSubmit = async () => { try { // Kiểm tra dữ liệu đầu vào phía client if (!form.examinationSessionSpecialtyId) { return toastWarn({ msg: "Vui lòng chọn phiên khám chuyên khoa", }); } if (!file) { return toastWarn({ msg: "Vui lòng chọn ảnh đơn thuốc", }); } const validMedicineList = form.items.filter( (item) => item.medicineName && item.medicineName.trim() !== "", ); if (validMedicineList.length === 0) { return toastWarn({ msg: "Vui lòng nhập ít nhất 1 loại thuốc hợp lệ", }); } // 1. Thực hiện gọi API upload ảnh setLoading(true); const uploadResponse: any = await prescriptionServices.uploadImage( file, "prescriptions", ); 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 let imageUrl = ""; // Trường hợp 1: axiosClient trả về thẳng dữ liệu (hoặc chuỗi URL) từ server if (typeof uploadResponse === "string") { imageUrl = uploadResponse; } else if (uploadResponse?.fileUrl) { imageUrl = uploadResponse.fileUrl; } else if (uploadResponse?.url) { imageUrl = uploadResponse.url; } // 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; if (typeof innerData === "string") { imageUrl = innerData; } else { imageUrl = innerData?.fileUrl || innerData?.url || innerData?.data?.fileUrl || innerData?.data?.url || ""; } } console.log("ĐƯỜNG DẪN ẢNH CUỐI CÙNG TRÍCH XUẤT ĐƯỢC:", imageUrl); if (!imageUrl) { setLoading(false); return toastWarn({ msg: "Không tìm thấy đường dẫn ảnh từ phản hồi của máy chủ", }); } // 2. Thiết lập cấu trúc dữ liệu Payload chuẩn xác theo interface của createPrescription const finalPayload = { examinationSessionSpecialtyId: form.examinationSessionSpecialtyId, notes: form.notes || "", prescriptionImg: imageUrl, items: validMedicineList.map((item) => ({ medicineName: item.medicineName.trim(), dosage: item.dosage || "", frequency: item.frequency || "", duration: item.duration || "", instructions: item.instructions || "", })), }; // 3. Kích hoạt gọi Mutation gửi yêu cầu tạo đơn thuốc mới lên Server funcCreatePrescription.mutate(finalPayload); } catch (error) { console.error("LỖI XỬ LÝ SUBMIT HOẶC UPLOAD ĐƠN THUỐC:", error); toastWarn({ msg: "Có lỗi xảy ra trong quá trình xử lý tải ảnh lên hoặc lưu đơn thuốc", }); } finally { setLoading(false); } }; return (
{/* HEADER */}

Tạo đơn thuốc

Tạo mới đơn thuốc cho bệnh nhân

{funcCreatePrescription.isPending || loading ? "Đang tạo đơn..." : "Tạo đơn thuốc"}
{/* GENERAL INFO */}

Thông tin chung

{/* EXAMINATION SESSION */} Phiên khám chuyên khoa * } placeholder="Chọn phiên khám chuyên khoa" value={form.examinationSessionSpecialtyId} options={examinationSessionOptions} onSelect={(item: any) => setForm((prev) => ({ ...prev, examinationSessionSpecialtyId: item.id, })) } onClean={() => setForm((prev) => ({ ...prev, examinationSessionSpecialtyId: "", })) } getOptionLabel={(item: any) => `${item.sessionCode} - ${item.specialtyName} (${item.patientName})` } getOptionValue={(item: any) => item.id} /> {/* IMAGE UPLOAD */}
Hình ảnh đơn thuốc * } name="prescriptionImg" file={file} setFile={setFile} path={""} />
{/* NOTES */}