feat:update push

This commit is contained in:
TuanVT
2026-05-26 09:50:32 +07:00
parent 251854694e
commit bce004352c
17 changed files with 997 additions and 659 deletions
@@ -6,6 +6,8 @@ import { Plus, Trash2 } from "lucide-react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useRouter } from "next/navigation";
import CustomButton from "@/components/customs/custom-button";
import FormCustom from "@/components/utils/FormCustom";
@@ -16,6 +18,8 @@ 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";
@@ -26,10 +30,14 @@ import consultationServices, {
} from "@/services/consultationServices";
import { QUERY_KEY } from "@/constant/config/enum";
import { toastWarn } from "@/common/funcs/toast";
import { PATH } from "@/constant/config";
import { useRouter } from "next/navigation";
import UploadImage from "@/components/utils/UploadImage";
/* =========================
TYPES
========================= */
interface PrescriptionItem {
medicineName: string;
@@ -42,16 +50,12 @@ interface PrescriptionItem {
interface PrescriptionForm {
examinationSessionId: string;
notes: string;
PrescriptionFile: string;
items: PrescriptionItem[];
}
interface UploadImageItem {
url?: string;
path?: string;
file?: File;
img?: string;
}
/* =========================
DEFAULT
========================= */
const defaultMedicine: PrescriptionItem = {
medicineName: "",
@@ -61,36 +65,25 @@ const defaultMedicine: PrescriptionItem = {
instructions: "",
};
const convertFileToBase64 = (file: File): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
resolve(reader.result as string);
};
reader.onerror = (error) => {
reject(error);
};
});
};
/* =========================
COMPONENT
========================= */
const CreatePrescription = () => {
const router = useRouter();
const [file, setFile] = useState<any>(null);
const [file, setFile] = useState<File | null>(null);
const [form, setForm] = useState<PrescriptionForm>({
examinationSessionId: "",
notes: "",
PrescriptionFile: "",
items: [defaultMedicine],
});
/**
* GET EXAMINATION SESSION
*/
/* =========================
GET EXAMINATION SESSION
========================= */
const examinationSessionQuery = useQuery<ConsultationResponse>({
queryKey: [QUERY_KEY.table_list_consultation],
@@ -120,30 +113,50 @@ const CreatePrescription = () => {
const examinationSessionOptions = examinationSessionQuery.data?.items || [];
/* =========================
CREATE MUTATION
========================= */
const createPrescriptionMutation = useMutation({
mutationFn: async (body: { PrescriptionFile: string }) => {
mutationFn: async () => {
const formData = new FormData();
formData.append("examinationSessionId", form.examinationSessionId);
formData.append("notes", form.notes);
/**
* FILE
*/
if (file) {
formData.append("PrescriptionFile", file);
}
/**
* MEDICINE ITEMS
*/
form.items
.filter((x) => x.medicineName.trim() !== "")
.forEach((item, index) => {
formData.append(`items[${index}].medicineName`, item.medicineName);
formData.append(`items[${index}].dosage`, item.dosage);
formData.append(`items[${index}].frequency`, item.frequency);
formData.append(`items[${index}].duration`, item.duration);
formData.append(`items[${index}].instructions`, item.instructions);
});
return await httpRequest({
showMessageFailed: true,
showMessageSuccess: true,
msgSuccess: "Tạo đơn thuốc thành công!",
http: prescriptionServices.createPrescription({
examinationSessionId: form.examinationSessionId,
notes: form.notes,
PrescriptionFile: body.PrescriptionFile,
items: form.items
.filter((x) => x.medicineName.trim() !== "")
.map((x) => ({
medicineName: x.medicineName,
dosage: x.dosage,
frequency: x.frequency,
duration: x.duration,
instructions: x.instructions,
})),
}),
http: prescriptionServices.createPrescription(formData),
});
},
@@ -152,23 +165,21 @@ const CreatePrescription = () => {
},
onError: (error: any) => {
// 👇 BACKEND ERROR HERE
const message =
error?.message || error?.error?.message || "Có lỗi xảy ra";
const code = error?.code || error?.error?.code;
error?.response?.data?.message || error?.message || "Có lỗi xảy ra";
toastWarn({
msg: message,
});
console.log("ERROR CODE:", code);
console.log("CREATE PRESCRIPTION ERROR:", error);
},
});
/**
* CHANGE MEDICINE FIELD
*/
/* =========================
CHANGE MEDICINE FIELD
========================= */
const handleChangeMedicine = (
index: number,
key: keyof PrescriptionItem,
@@ -187,9 +198,10 @@ const CreatePrescription = () => {
}));
};
/**
* ADD MEDICINE
*/
/* =========================
ADD MEDICINE
========================= */
const handleAddMedicine = () => {
setForm((prev) => ({
...prev,
@@ -207,9 +219,10 @@ const CreatePrescription = () => {
}));
};
/**
* REMOVE MEDICINE
*/
/* =========================
REMOVE MEDICINE
========================= */
const handleRemoveMedicine = (index: number) => {
const newItems = form.items.filter((_, i) => i !== index);
@@ -219,63 +232,30 @@ const CreatePrescription = () => {
}));
};
const handleSubmit = async () => {
try {
/**
* VALIDATE
*/
if (!form.examinationSessionId) {
return toastWarn({
msg: "Vui lòng chọn phiên khám",
});
}
/* =========================
SUBMIT
========================= */
if (!file) {
return toastWarn({
msg: "Vui lòng chọn ảnh đơn thuốc",
});
}
/**
* UPLOAD IMAGE
*/
let uploadedImage = "";
if (file) {
const formData = new FormData();
formData.append("file", file);
/**
* API UPLOAD
* SỬA uploadServices theo project của bạn
*/
const uploadRes: any = await httpRequest({
showMessageFailed: true,
http: prescriptionServices.uploadImage(formData),
});
/**
* BACKEND RESPONSE
*/
uploadedImage =
uploadRes?.url || uploadRes?.path || uploadRes?.data?.url || "";
}
/**
* CREATE PRESCRIPTION
*/
createPrescriptionMutation.mutate({
PrescriptionFile: uploadedImage,
});
} catch (error: any) {
toastWarn({
msg: error?.message || "Upload ảnh thất bại",
const handleSubmit = () => {
if (!form.examinationSessionId) {
return toastWarn({
msg: "Vui lòng chọn phiên khám",
});
}
if (!file) {
return toastWarn({
msg: "Vui lòng chọn ảnh đơn thuốc",
});
}
createPrescriptionMutation.mutate();
};
/* =========================
UI
========================= */
return (
<FormCustom form={form} setForm={setForm} onSubmit={handleSubmit}>
<div className="flex flex-col gap-6 rounded-2xl bg-white p-6 shadow">
@@ -290,11 +270,13 @@ const CreatePrescription = () => {
Tạo mới đơn thuốc cho bệnh nhân
</p>
</div>
<div>
<CustomButton
type="submit"
variant="midnightBlue"
disabled={createPrescriptionMutation.isPending}
className="min-w-[180px]"
>
{createPrescriptionMutation.isPending
? "Đang tạo..."
@@ -333,9 +315,7 @@ const CreatePrescription = () => {
examinationSessionId: "",
}))
}
getOptionLabel={(item: ConsultationItem) =>
`${item.sessionCode} `
}
getOptionLabel={(item: ConsultationItem) => `${item.sessionCode}`}
getOptionValue={(item: ConsultationItem) => item.id}
/>
@@ -350,7 +330,7 @@ const CreatePrescription = () => {
name="PrescriptionFile"
file={file}
setFile={setFile}
path={""}
path=""
/>
</div>
@@ -378,6 +358,7 @@ const CreatePrescription = () => {
<h3 className="text-lg font-semibold text-[#111827]">
Danh sách thuốc
</h3>
<div>
<CustomButton
type="button"
@@ -413,7 +394,7 @@ const CreatePrescription = () => {
)}
</div>
{/* FORM */}
{/* INPUTS */}
<div className="grid grid-cols-2 gap-5">
<InputForm
label="Tên thuốc"
@@ -460,6 +441,7 @@ const CreatePrescription = () => {
/>
</div>
{/* INSTRUCTION */}
<div className="mt-5">
<TextArea
label="Hướng dẫn sử dụng"