feate:update
This commit is contained in:
parent
b0baa509d9
commit
2978cf2db0
@ -16,8 +16,6 @@ import TextArea from "@/components/utils/FormCustom/components/TextArea";
|
|||||||
|
|
||||||
import SelectForm from "@/components/utils/FormCustom/components/SelectForm";
|
import SelectForm from "@/components/utils/FormCustom/components/SelectForm";
|
||||||
|
|
||||||
import UploadMultipleFile from "@/components/utils/UploadMultipleFile";
|
|
||||||
|
|
||||||
import { httpRequest } from "@/services";
|
import { httpRequest } from "@/services";
|
||||||
|
|
||||||
import prescriptionServices from "@/services/prescriptionServices";
|
import prescriptionServices from "@/services/prescriptionServices";
|
||||||
@ -31,6 +29,7 @@ import { QUERY_KEY } from "@/constant/config/enum";
|
|||||||
import { toastWarn } from "@/common/funcs/toast";
|
import { toastWarn } from "@/common/funcs/toast";
|
||||||
import { PATH } from "@/constant/config";
|
import { PATH } from "@/constant/config";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import UploadImage from "@/components/utils/UploadImage";
|
||||||
|
|
||||||
interface PrescriptionItem {
|
interface PrescriptionItem {
|
||||||
medicineName: string;
|
medicineName: string;
|
||||||
@ -43,7 +42,7 @@ interface PrescriptionItem {
|
|||||||
interface PrescriptionForm {
|
interface PrescriptionForm {
|
||||||
examinationSessionId: string;
|
examinationSessionId: string;
|
||||||
notes: string;
|
notes: string;
|
||||||
prescriptionImg: string;
|
PrescriptionFile: string;
|
||||||
items: PrescriptionItem[];
|
items: PrescriptionItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,14 +61,30 @@ const defaultMedicine: PrescriptionItem = {
|
|||||||
instructions: "",
|
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);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const CreatePrescription = () => {
|
const CreatePrescription = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [images, setImages] = useState<UploadImageItem[]>([]);
|
const [file, setFile] = useState<any>(null);
|
||||||
|
|
||||||
const [form, setForm] = useState<PrescriptionForm>({
|
const [form, setForm] = useState<PrescriptionForm>({
|
||||||
examinationSessionId: "",
|
examinationSessionId: "",
|
||||||
notes: "",
|
notes: "",
|
||||||
prescriptionImg: "",
|
PrescriptionFile: "",
|
||||||
items: [defaultMedicine],
|
items: [defaultMedicine],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -106,7 +121,7 @@ const CreatePrescription = () => {
|
|||||||
const examinationSessionOptions = examinationSessionQuery.data?.items || [];
|
const examinationSessionOptions = examinationSessionQuery.data?.items || [];
|
||||||
|
|
||||||
const createPrescriptionMutation = useMutation({
|
const createPrescriptionMutation = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async (body: { PrescriptionFile: string }) => {
|
||||||
return await httpRequest({
|
return await httpRequest({
|
||||||
showMessageFailed: true,
|
showMessageFailed: true,
|
||||||
showMessageSuccess: true,
|
showMessageSuccess: true,
|
||||||
@ -114,9 +129,20 @@ const CreatePrescription = () => {
|
|||||||
|
|
||||||
http: prescriptionServices.createPrescription({
|
http: prescriptionServices.createPrescription({
|
||||||
examinationSessionId: form.examinationSessionId,
|
examinationSessionId: form.examinationSessionId,
|
||||||
|
|
||||||
notes: form.notes,
|
notes: form.notes,
|
||||||
prescriptionImg: form.prescriptionImg,
|
|
||||||
items: form.items,
|
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,
|
||||||
|
})),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -193,11 +219,61 @@ const CreatePrescription = () => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
const handleSubmit = async () => {
|
||||||
* SUBMIT
|
try {
|
||||||
*/
|
/**
|
||||||
const handleSubmit = () => {
|
* VALIDATE
|
||||||
createPrescriptionMutation.mutate();
|
*/
|
||||||
|
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",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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",
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -207,7 +283,7 @@ const CreatePrescription = () => {
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-[28px] font-semibold text-[#111827]">
|
<h2 className="text-[28px] font-semibold text-[#111827]">
|
||||||
Thêm đơn thuốc
|
Tạo đơn thuốc
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p className="mt-1 text-sm text-[#697586]">
|
<p className="mt-1 text-sm text-[#697586]">
|
||||||
@ -221,8 +297,8 @@ const CreatePrescription = () => {
|
|||||||
disabled={createPrescriptionMutation.isPending}
|
disabled={createPrescriptionMutation.isPending}
|
||||||
>
|
>
|
||||||
{createPrescriptionMutation.isPending
|
{createPrescriptionMutation.isPending
|
||||||
? "Đang lưu..."
|
? "Đang tạo..."
|
||||||
: "Lưu đơn thuốc"}
|
: "Tạo đơn thuốc"}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -265,22 +341,16 @@ const CreatePrescription = () => {
|
|||||||
|
|
||||||
{/* IMAGE */}
|
{/* IMAGE */}
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<UploadMultipleFile
|
<UploadImage
|
||||||
images={images}
|
label={
|
||||||
setImages={(files: UploadImageItem[]) => {
|
<span>
|
||||||
setImages(files);
|
Hình ảnh đơn thuốc <span style={{ color: "red" }}>*</span>
|
||||||
|
</span>
|
||||||
const imagePath =
|
}
|
||||||
files?.[0]?.path ||
|
name="PrescriptionFile"
|
||||||
files?.[0]?.url ||
|
file={file}
|
||||||
files?.[0]?.img ||
|
setFile={setFile}
|
||||||
"";
|
path={""}
|
||||||
|
|
||||||
setForm((prev) => ({
|
|
||||||
...prev,
|
|
||||||
prescriptionImg: imagePath,
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -292,6 +362,12 @@ const CreatePrescription = () => {
|
|||||||
value={form.notes}
|
value={form.notes}
|
||||||
isBlur
|
isBlur
|
||||||
max={5000}
|
max={5000}
|
||||||
|
onChangeValue={(value) =>
|
||||||
|
setForm((prev) => ({
|
||||||
|
...prev,
|
||||||
|
notes: String(value),
|
||||||
|
}))
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -142,6 +142,8 @@ const MainPrescription = () => {
|
|||||||
<Image
|
<Image
|
||||||
src={item.prescriptionImg}
|
src={item.prescriptionImg}
|
||||||
alt="prescription"
|
alt="prescription"
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
className="h-10 w-10 rounded object-cover"
|
className="h-10 w-10 rounded object-cover"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -33,6 +33,7 @@ import type { PrescriptionDetail } from "@/services/prescriptionServices";
|
|||||||
========================= */
|
========================= */
|
||||||
|
|
||||||
interface PrescriptionItem {
|
interface PrescriptionItem {
|
||||||
|
id: string;
|
||||||
medicineName: string;
|
medicineName: string;
|
||||||
dosage: string;
|
dosage: string;
|
||||||
frequency: string;
|
frequency: string;
|
||||||
@ -58,6 +59,7 @@ interface UploadImageItem {
|
|||||||
========================= */
|
========================= */
|
||||||
|
|
||||||
const defaultMedicine: PrescriptionItem = {
|
const defaultMedicine: PrescriptionItem = {
|
||||||
|
id: "",
|
||||||
medicineName: "",
|
medicineName: "",
|
||||||
dosage: "",
|
dosage: "",
|
||||||
frequency: "",
|
frequency: "",
|
||||||
@ -171,6 +173,7 @@ const UpdatePrescription = () => {
|
|||||||
items:
|
items:
|
||||||
data.items && data.items.length > 0
|
data.items && data.items.length > 0
|
||||||
? data.items.map((i) => ({
|
? data.items.map((i) => ({
|
||||||
|
id: i.id,
|
||||||
medicineName: i.medicineName ?? "",
|
medicineName: i.medicineName ?? "",
|
||||||
dosage: i.dosage ?? "",
|
dosage: i.dosage ?? "",
|
||||||
frequency: i.frequency ?? "",
|
frequency: i.frequency ?? "",
|
||||||
@ -224,19 +227,32 @@ const UpdatePrescription = () => {
|
|||||||
throw new Error("Không tìm thấy ID đơn thuốc!");
|
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({
|
return await httpRequest({
|
||||||
showMessageFailed: true,
|
showMessageFailed: true,
|
||||||
|
|
||||||
showMessageSuccess: true,
|
showMessageSuccess: true,
|
||||||
|
|
||||||
msgSuccess: "Cập nhật đơn thuốc thành công!",
|
msgSuccess: "Cập nhật đơn thuốc thành công!",
|
||||||
|
|
||||||
http: prescriptionServices.putPrescription(id, {
|
http: prescriptionServices.putPrescription(id, payload),
|
||||||
// examinationSessionId: form.examinationSessionId,
|
|
||||||
notes: form.notes,
|
|
||||||
prescriptionImg: form.prescriptionImg,
|
|
||||||
items: form.items,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -245,8 +261,10 @@ const UpdatePrescription = () => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onError: (error: any) => {
|
onError: (error: any) => {
|
||||||
|
console.log("UPDATE ERROR:", error);
|
||||||
|
|
||||||
const message =
|
const message =
|
||||||
error?.message || error?.error?.message || "Có lỗi xảy ra";
|
error?.response?.data?.message || error?.message || "Có lỗi xảy ra";
|
||||||
|
|
||||||
toastWarn({
|
toastWarn({
|
||||||
msg: message,
|
msg: message,
|
||||||
@ -283,6 +301,7 @@ const UpdatePrescription = () => {
|
|||||||
items: [
|
items: [
|
||||||
...prev.items,
|
...prev.items,
|
||||||
{
|
{
|
||||||
|
id: "",
|
||||||
medicineName: "",
|
medicineName: "",
|
||||||
dosage: "",
|
dosage: "",
|
||||||
frequency: "",
|
frequency: "",
|
||||||
|
|||||||
@ -81,7 +81,7 @@ function SelectForm<OptionType>({
|
|||||||
onClick={() => !readOnly && setOpen((prev) => !prev)}
|
onClick={() => !readOnly && setOpen((prev) => !prev)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"flex items-center justify-between px-4 h-12 border rounded-full cursor-pointer transition",
|
"flex items-center justify-between px-4 h-12 border rounded-full cursor-pointer transition",
|
||||||
"border-gray-300 bg-gray-300 hover:border-blue-600",
|
"border-gray-300 bg-white hover:border-blue-600",
|
||||||
open && "border-blue-600",
|
open && "border-blue-600",
|
||||||
readOnly &&
|
readOnly &&
|
||||||
"bg-gray-100 border-gray-200 cursor-not-allowed hover:border-gray-200",
|
"bg-gray-100 border-gray-200 cursor-not-allowed hover:border-gray-200",
|
||||||
|
|||||||
243
src/components/utils/UploadImage/UploadImage.tsx
Normal file
243
src/components/utils/UploadImage/UploadImage.tsx
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
import { UploadCloud, X } from "lucide-react";
|
||||||
|
|
||||||
|
import { toastError, toastWarn } from "@/common/funcs/toast";
|
||||||
|
|
||||||
|
import { UploadImageProps } from "./interface";
|
||||||
|
|
||||||
|
const MAXIMUM_FILE = 10; // MB
|
||||||
|
|
||||||
|
const ACCEPT_TYPES = ["image/jpeg", "image/jpg", "image/png"];
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
COMPONENT
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
const UploadImage = ({
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
file,
|
||||||
|
setFile,
|
||||||
|
resetPath,
|
||||||
|
isWidthFull = true,
|
||||||
|
disabled = false,
|
||||||
|
}: UploadImageProps) => {
|
||||||
|
const [dragging, setDragging] = useState(false);
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
PREVIEW IMAGE
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
const imagePreview = useMemo(() => {
|
||||||
|
if (!file) return "";
|
||||||
|
|
||||||
|
return URL.createObjectURL(file);
|
||||||
|
}, [file]);
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
CLEANUP OBJECT URL
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (imagePreview) {
|
||||||
|
URL.revokeObjectURL(imagePreview);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [imagePreview]);
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
VALIDATE FILE
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
const validateFile = (selectedFile: File | null | undefined) => {
|
||||||
|
if (!selectedFile) return;
|
||||||
|
|
||||||
|
const { size, type } = selectedFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CHECK SIZE
|
||||||
|
*/
|
||||||
|
if (size / 1000000 > MAXIMUM_FILE) {
|
||||||
|
return toastError({
|
||||||
|
msg: `Kích thước tối đa của ảnh là ${MAXIMUM_FILE} MB`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CHECK TYPE
|
||||||
|
*/
|
||||||
|
if (!ACCEPT_TYPES.includes(type)) {
|
||||||
|
return toastWarn({
|
||||||
|
msg: "Định dạng không hợp lệ. Chỉ chấp nhận JPG, JPEG, PNG",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SET FILE
|
||||||
|
*/
|
||||||
|
setFile(selectedFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
DRAG EVENTS
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
const handleDragEnter = (e: React.DragEvent<HTMLLabelElement>): void => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (disabled) return;
|
||||||
|
|
||||||
|
setDragging(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragLeave = (): void => {
|
||||||
|
setDragging(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop = (e: React.DragEvent<HTMLLabelElement>): void => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (disabled) return;
|
||||||
|
|
||||||
|
setDragging(false);
|
||||||
|
|
||||||
|
const droppedFile = e.dataTransfer.files?.[0];
|
||||||
|
|
||||||
|
validateFile(droppedFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
SELECT FILE
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
const handleSelectImage = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
if (disabled) return;
|
||||||
|
|
||||||
|
const selectedFile = e.target.files?.[0];
|
||||||
|
|
||||||
|
validateFile(selectedFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
REMOVE IMAGE
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
const handleRemoveImage = (): void => {
|
||||||
|
setFile(null);
|
||||||
|
|
||||||
|
resetPath?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
IMAGE SOURCE
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
const imageSrc = imagePreview || path || "";
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
UI
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
{/* LABEL */}
|
||||||
|
{label && (
|
||||||
|
<div className="mb-2">
|
||||||
|
{typeof label === "string" ? (
|
||||||
|
<p className="text-sm font-medium text-gray-700">{label}</p>
|
||||||
|
) : (
|
||||||
|
label
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* PREVIEW */}
|
||||||
|
{imageSrc ? (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"relative overflow-hidden rounded-2xl border border-dashed border-blue-300 bg-blue-50/20",
|
||||||
|
"h-[240px]",
|
||||||
|
isWidthFull ? "w-full" : "w-[600px]",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={imageSrc}
|
||||||
|
alt="Preview image"
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
sizes="100vw"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* REMOVE BUTTON */}
|
||||||
|
{!disabled && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleRemoveImage}
|
||||||
|
className="absolute right-3 top-3 z-10 flex h-9 w-9 items-center justify-center rounded-full bg-white shadow-md transition hover:bg-gray-100 active:scale-95"
|
||||||
|
>
|
||||||
|
<X size={18} className="text-gray-700" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<label
|
||||||
|
htmlFor={`upload-image-${name}`}
|
||||||
|
onDragEnter={handleDragEnter}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDragOver={(e) => e.preventDefault()}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
className={clsx(
|
||||||
|
"flex h-[240px] flex-col items-center justify-center gap-4 rounded-2xl border border-dashed transition",
|
||||||
|
isWidthFull ? "w-full" : "w-[600px]",
|
||||||
|
|
||||||
|
disabled ? "cursor-not-allowed opacity-60" : "cursor-pointer",
|
||||||
|
|
||||||
|
dragging
|
||||||
|
? "border-blue-500 bg-white"
|
||||||
|
: "border-blue-300 bg-blue-50/20",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{/* ICON */}
|
||||||
|
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-white shadow-sm">
|
||||||
|
<UploadCloud className="h-8 w-8 text-blue-500" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TEXT */}
|
||||||
|
<div className="space-y-1 text-center">
|
||||||
|
<p className="text-sm font-medium text-gray-800">
|
||||||
|
Kéo & thả ảnh vào đây
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
Hỗ trợ JPG, JPEG, PNG (Tối đa {MAXIMUM_FILE}MB)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* INPUT */}
|
||||||
|
<input
|
||||||
|
hidden
|
||||||
|
id={`upload-image-${name}`}
|
||||||
|
type="file"
|
||||||
|
accept="image/png,image/jpeg,image/jpg"
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={handleSelectImage}
|
||||||
|
onClick={(e) => {
|
||||||
|
(e.target as HTMLInputElement).value = "";
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UploadImage;
|
||||||
1
src/components/utils/UploadImage/index.ts
Normal file
1
src/components/utils/UploadImage/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from "./UploadImage";
|
||||||
17
src/components/utils/UploadImage/interface/index.ts
Normal file
17
src/components/utils/UploadImage/interface/index.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export interface UploadImageProps {
|
||||||
|
isWidthFull?: boolean;
|
||||||
|
|
||||||
|
label?: string | React.ReactNode;
|
||||||
|
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
file: File | null;
|
||||||
|
|
||||||
|
setFile: (file: File | null) => void;
|
||||||
|
|
||||||
|
path?: string;
|
||||||
|
|
||||||
|
resetPath?: () => void;
|
||||||
|
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
@ -45,12 +45,12 @@ export const Menus: {
|
|||||||
path: PATH.CONSULTATION,
|
path: PATH.CONSULTATION,
|
||||||
pathActive: PATH.CONSULTATION,
|
pathActive: PATH.CONSULTATION,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: "Đơn thuốc",
|
// title: "Đơn thuốc",
|
||||||
icon: Pill,
|
// icon: Pill,
|
||||||
path: PATH.PRESCRIPTION,
|
// path: PATH.PRESCRIPTION,
|
||||||
pathActive: PATH.PRESCRIPTION,
|
// pathActive: PATH.PRESCRIPTION,
|
||||||
},
|
// },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -57,7 +57,7 @@ const prescriptionServices = {
|
|||||||
data: {
|
data: {
|
||||||
examinationSessionId: string;
|
examinationSessionId: string;
|
||||||
notes: string;
|
notes: string;
|
||||||
prescriptionImg: string;
|
PrescriptionFile: string;
|
||||||
items: {
|
items: {
|
||||||
medicineName: string;
|
medicineName: string;
|
||||||
dosage: string;
|
dosage: string;
|
||||||
@ -72,6 +72,7 @@ const prescriptionServices = {
|
|||||||
cancelToken: tokenAxios,
|
cancelToken: tokenAxios,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getPrescription: (params?: GetPrescriptionParams, tokenAxios?: any) => {
|
getPrescription: (params?: GetPrescriptionParams, tokenAxios?: any) => {
|
||||||
return axiosClient.get<PrescriptionResponse>(`/Prescription`, {
|
return axiosClient.get<PrescriptionResponse>(`/Prescription`, {
|
||||||
params,
|
params,
|
||||||
@ -104,6 +105,14 @@ const prescriptionServices = {
|
|||||||
cancelToken: tokenAxios,
|
cancelToken: tokenAxios,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
uploadImage: (body: FormData) => {
|
||||||
|
return axiosClient.post("/upload", body, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default prescriptionServices;
|
export default prescriptionServices;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user