feat:update

This commit is contained in:
TuanVT 2026-06-11 11:25:47 +07:00
parent b4568e1c8c
commit 42cb08f0d0
14 changed files with 361 additions and 89 deletions

View File

@ -108,6 +108,45 @@ export function getTextAddress(detailAddress: any, address?: string): string {
return parts.length ? parts.join(", ") : "---"; return parts.length ? parts.join(", ") : "---";
} }
export const formatDateOfBirth = (value: string) => {
const digits = value.replace(/\D/g, "").slice(0, 8);
if (digits.length <= 2) return digits;
if (digits.length <= 4) return `${digits.slice(0, 2)}/${digits.slice(2)}`;
return `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}`;
};
export const parseDateOfBirth = (value: string) => {
const match = value.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
if (!match) return null;
const day = Number(match[1]);
const month = Number(match[2]);
const year = Number(match[3]);
const parsedDate = new Date(year, month - 1, day);
if (
parsedDate.getFullYear() !== year ||
parsedDate.getMonth() !== month - 1 ||
parsedDate.getDate() !== day
) {
return null;
}
return parsedDate;
};
export const formatToISODate = (date: Date | null): string => {
if (!date) return "";
const year = date.getFullYear();
// Đảm bảo tháng và ngày luôn có 2 chữ số (VD: 02, 12)
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`; // Kết quả: "2001-02-12"
};
export function numberToWords(number: number) { export function numberToWords(number: number) {
if (typeof number !== "number" || isNaN(number)) { if (typeof number !== "number" || isNaN(number)) {
return "Không hợp lệ"; return "Không hợp lệ";

View File

@ -78,6 +78,11 @@ export default function MainLogin() {
// Kiểm tra nhanh điều kiện dữ liệu trước khi trigger API // Kiểm tra nhanh điều kiện dữ liệu trước khi trigger API
if (!form.username || !form.password) return; if (!form.username || !form.password) return;
// if (form.password.length < 6) {
// alert("Mật khẩu phải có ít nhất 6 ký tự");
// return;
// }
if (isRememberPassword) { if (isRememberPassword) {
store.dispatch( store.dispatch(
setDataLoginStorage({ setDataLoginStorage({
@ -136,6 +141,7 @@ export default function MainLogin() {
onClean onClean
isRequired isRequired
isBlur isBlur
min={6}
showDone showDone
icon={<ShieldPlus size={22} />} icon={<ShieldPlus size={22} />}
/> />

View File

@ -154,6 +154,7 @@ export default function MainRegister() {
onClean onClean
isRequired isRequired
isBlur isBlur
min={6}
showDone showDone
icon={<ShieldPlus size={22} />} icon={<ShieldPlus size={22} />}
/> />

View File

@ -1,26 +1,16 @@
"use client"; "use client";
import React, { useMemo, useState } from "react"; import React, { useMemo, useState } from "react";
import { X } from "lucide-react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import FormCustom from "@/components/utils/FormCustom/FormCustom"; import FormCustom from "@/components/utils/FormCustom/FormCustom";
import SelectForm from "@/components/utils/FormCustom/components/SelectForm"; import SelectForm from "@/components/utils/FormCustom/components/SelectForm";
import { httpRequest } from "@/services"; import { httpRequest } from "@/services";
import consultationServices from "@/services/consultationServices"; import consultationServices from "@/services/consultationServices";
import patientServices, { import patientServices, {
PatientItem, PatientItem,
PatientResponse, PatientResponse,
} from "@/services/patientServices"; } from "@/services/patientServices";
import { QUERY_KEY } from "@/constant/config/enum"; import { QUERY_KEY } from "@/constant/config/enum";
import CustomButton from "@/components/customs/custom-button"; import CustomButton from "@/components/customs/custom-button";
import { useRouter, useSearchParams } from "next/navigation"; import { useRouter, useSearchParams } from "next/navigation";
import InputForm from "@/components/utils/FormCustom/components/InputForm"; import InputForm from "@/components/utils/FormCustom/components/InputForm";
@ -137,6 +127,9 @@ const CreateConsultation = () => {
if (!form?.patientId) { if (!form?.patientId) {
return toastWarn({ msg: "Vui lòng chọn bệnh nhân" }); return toastWarn({ msg: "Vui lòng chọn bệnh nhân" });
} }
if (!form?.specialtyIds) {
return toastWarn({ msg: "Vui lòng chọn chuyên khoa" });
}
createConsultationMutation.mutate(); createConsultationMutation.mutate();
}; };
@ -174,7 +167,7 @@ const CreateConsultation = () => {
<div className="space-y-5 mb-2"> <div className="space-y-5 mb-2">
<SelectForm <SelectForm
label={ label={
<span className="text-[14px] font-medium text-[#374151]"> <span>
Bệnh nhân Bệnh nhân
<span className="text-red-500">*</span> <span className="text-red-500">*</span>
</span> </span>
@ -205,7 +198,11 @@ const CreateConsultation = () => {
<div className="space-y-5 mb-2"> <div className="space-y-5 mb-2">
<GridColumn col={3}> <GridColumn col={3}>
<InputForm <InputForm
label="Lý do khám" label={
<span>
do khám <span className="text-red-500">*</span>
</span>
}
name="chiefComplaint" name="chiefComplaint"
type="text" type="text"
value={form.chiefComplaint} value={form.chiefComplaint}
@ -217,7 +214,11 @@ const CreateConsultation = () => {
/> />
<InputForm <InputForm
type="text" type="text"
label="Triệu chứng" label={
<span>
Triệu chứng <span className="text-red-500">*</span>
</span>
}
name="symptomsText" name="symptomsText"
placeholder="Nhập triệu chứng" placeholder="Nhập triệu chứng"
isRequired isRequired
@ -229,7 +230,12 @@ const CreateConsultation = () => {
<InputForm <InputForm
type="text" type="text"
label="Chỉ số huyết áp, chỉ số mạch" label={
<span>
Chỉ số huyết áp, chỉ số mạch{" "}
<span className="text-red-500">*</span>
</span>
}
name="vitalSigns" name="vitalSigns"
placeholder="Nhập chỉ số huyết áp, chỉ số mạch" placeholder="Nhập chỉ số huyết áp, chỉ số mạch"
value={form.vitalSigns} value={form.vitalSigns}
@ -242,7 +248,12 @@ const CreateConsultation = () => {
</div> </div>
<div className="space-y-5 mb2"> <div className="space-y-5 mb2">
<SelectMany<SpecialtyItem> <SelectMany<SpecialtyItem>
label="Chuyên khoa" label={
<span>
Chuyên khoa
<span className="text-red-500">*</span>
</span>
}
text="chuyên khoa" text="chuyên khoa"
title="Chọn chuyên khoa" title="Chọn chuyên khoa"
placeholder="Chọn nhiều chuyên khoa..." placeholder="Chọn nhiều chuyên khoa..."

View File

@ -575,26 +575,34 @@ const DetailConsultation = () => {
<GridColumn col={3}> <GridColumn col={3}>
<div> <div>
<p className="text-sm text-[#697586]">CHUYÊN KHOA</p> <p className="text-sm text-[#697586]">CHUYÊN KHOA</p>
{/* Thêm class để bọc text */}
<p>{selectedSpecialty.specialtyName || "---"}</p> <p className="break-words overflow-hidden">
{selectedSpecialty.specialtyName || "---"}
</p>
</div> </div>
<div> <div>
<p className="text-sm text-[#697586]">CHẨN ĐOÁN</p> <p className="text-sm text-[#697586]">CHẨN ĐOÁN</p>
{/* Thêm class để bọc text */}
<p>{selectedSpecialty.diagnosis || "---"}</p> <p className="break-words overflow-hidden">
{selectedSpecialty.diagnosis || "---"}
</p>
</div> </div>
<div> <div>
<p className="text-sm text-[#697586]">GHI CHÚ KHÁM</p> <p className="text-sm text-[#697586]">GHI CHÚ KHÁM</p>
{/* Thêm class để bọc text */}
<p>{selectedSpecialty.specialtyNotes || "---"}</p> <p className="break-words overflow-hidden">
{selectedSpecialty.specialtyNotes || "---"}
</p>
</div> </div>
<div> <div>
<p className="text-sm text-[#697586]">THỦ THUẬT</p> <p className="text-sm text-[#697586]">THỦ THUẬT</p>
{/* Thêm class để bọc text */}
<p>{selectedSpecialty.procedureNotes || "---"}</p> <p className="break-words overflow-hidden">
{selectedSpecialty.procedureNotes || "---"}
</p>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">

View File

@ -352,7 +352,7 @@ const MainPageConsultation = () => {
}, },
{ {
uuid: TYPE_STATUS.PendingPrescription, uuid: TYPE_STATUS.PendingPrescription,
name: "Chờ nhận thuốc/kính", name: "Chờ nhận thuốc",
}, },
{ {
uuid: TYPE_STATUS.Completed, uuid: TYPE_STATUS.Completed,

View File

@ -18,6 +18,7 @@ import consultationServices, {
import uploadServices from "@/services/uploadServices"; import uploadServices from "@/services/uploadServices";
import UploadMultipleFile from "@/components/utils/UploadMultipleFile"; import UploadMultipleFile from "@/components/utils/UploadMultipleFile";
import TextArea from "@/components/utils/FormCustom/components/TextArea"; import TextArea from "@/components/utils/FormCustom/components/TextArea";
import { toastWarn } from "@/common/funcs/toast";
/* ========================= /* =========================
TYPES TYPES
@ -247,7 +248,11 @@ const UpdateConsultation = () => {
{/* GENERAL INFO (PATIENT) */} {/* GENERAL INFO (PATIENT) */}
<InputForm <InputForm
label="Bệnh nhân" label={
<span>
Bệnh nhân <span className="text-red-500">*</span>
</span>
}
name="patientInfo" name="patientInfo"
type="text" type="text"
// Hiển thị Tên bệnh nhân - SĐT hoặc CCCD dựa trên API detail trả về // Hiển thị Tên bệnh nhân - SĐT hoặc CCCD dựa trên API detail trả về
@ -268,7 +273,11 @@ const UpdateConsultation = () => {
<GridColumn col={3}> <GridColumn col={3}>
<InputForm <InputForm
label="Lý do khám" label={
<span>
do khám <span className="text-red-500">*</span>
</span>
}
name="chiefComplaint" name="chiefComplaint"
type="text" type="text"
value={form.chiefComplaint} value={form.chiefComplaint}
@ -280,7 +289,11 @@ const UpdateConsultation = () => {
/> />
<InputForm <InputForm
type="text" type="text"
label="Triệu chứng" label={
<span>
Triệu chứng <span className="text-red-500">*</span>
</span>
}
name="symptomsText" name="symptomsText"
placeholder="Nhập triệu chứng" placeholder="Nhập triệu chứng"
isRequired isRequired
@ -292,7 +305,12 @@ const UpdateConsultation = () => {
<InputForm <InputForm
type="text" type="text"
label="Chỉ số huyết áp, chỉ số mạch" label={
<span>
Chỉ số huyết áp, chỉ số mạch{" "}
<span className="text-red-500">*</span>
</span>
}
name="vitalSigns" name="vitalSigns"
placeholder="Nhập chỉ số huyết áp, chỉ số mạch" placeholder="Nhập chỉ số huyết áp, chỉ số mạch"
value={form.vitalSigns} value={form.vitalSigns}
@ -304,7 +322,11 @@ const UpdateConsultation = () => {
<InputForm <InputForm
type="text" type="text"
label="Chẩn đoán" label={
<span>
Chẩn đoán <span className="text-red-500">*</span>
</span>
}
name="diagnosis" name="diagnosis"
placeholder="Nhập chẩn đoán" placeholder="Nhập chẩn đoán"
value={form.diagnosis} value={form.diagnosis}
@ -316,7 +338,11 @@ const UpdateConsultation = () => {
<InputForm <InputForm
type="text" type="text"
label="Kế hoạch điều trị" label={
<span>
Kế hoạch điều trị <span className="text-red-500">*</span>
</span>
}
name="treatmentPlan" name="treatmentPlan"
placeholder="Nhập kế hoạch điều trị" placeholder="Nhập kế hoạch điều trị"
value={form.treatmentPlan} value={form.treatmentPlan}
@ -328,7 +354,11 @@ const UpdateConsultation = () => {
<InputForm <InputForm
type="text" type="text"
label="Ghi chú bác sĩ" label={
<span>
Ghi chú bác <span className="text-red-500">*</span>
</span>
}
name="doctorNotes" name="doctorNotes"
placeholder="Nhập ghi chú" placeholder="Nhập ghi chú"
value={form.doctorNotes} value={form.doctorNotes}
@ -372,7 +402,12 @@ const UpdateConsultation = () => {
<TextArea <TextArea
name="glassType" name="glassType"
placeholder="Nhập thông số chi tiết loại kính được chỉ định" placeholder="Nhập thông số chi tiết loại kính được chỉ định"
label="Thông số / Loại kính chỉ định" label={
<span>
Thông số / loại kính chỉ đnh
<span className="text-red-500">*</span>
</span>
}
value={form.glassType} value={form.glassType}
isRequired isRequired
readOnly readOnly
@ -408,10 +443,18 @@ const UpdateConsultation = () => {
Hủy bỏ Hủy bỏ
</CustomButton> </CustomButton>
<CustomButton <CustomButton
type="submit" onClick={handleSubmit}
variant="midnightBlue" variant="midnightBlue"
rounded="md" rounded="md"
disabled={isLoading || !id || id === "undefined"} disabled={
!form.chiefComplaint ||
!form.symptomsText ||
!form.vitalSigns ||
!form.diagnosis ||
!form.treatmentPlan ||
!form.doctorNotes ||
updateConsultationMutation.isPending
}
className="min-w-[140px]" className="min-w-[140px]"
> >
{updateConsultationMutation.isPending {updateConsultationMutation.isPending

View File

@ -1,5 +1,6 @@
"use client"; "use client";
import { toastWarn } from "@/common/funcs/toast";
import CustomButton from "@/components/customs/custom-button"; import CustomButton from "@/components/customs/custom-button";
import GridColumn from "@/components/layouts/GridColumn"; import GridColumn from "@/components/layouts/GridColumn";
import FormCustom from "@/components/utils/FormCustom"; import FormCustom from "@/components/utils/FormCustom";
@ -170,6 +171,16 @@ const UpdateSpecialtyClinicId = () => {
}); });
const handleSubmit = async () => { const handleSubmit = async () => {
if (!form.specialtyNotes || !form.diagnosis || !form.procedureNotes) {
return toastWarn({ msg: "Vui lòng nhập đầy đủ các trường!" });
}
if (isEyeClinic && form.glassRequired === true && !form.glassType) {
return toastWarn({
msg: "Vui lòng nhập thông số / Loại kính chỉ định!",
});
}
try { try {
const currentImage = images const currentImage = images
.filter((item) => !!item.img) .filter((item) => !!item.img)
@ -277,7 +288,12 @@ const UpdateSpecialtyClinicId = () => {
<GridColumn col={isEyeClinic ? 4 : 3}> <GridColumn col={isEyeClinic ? 4 : 3}>
<InputForm <InputForm
label="Ghi chú chuyên khoa" label={
<span>
Ghi chú chuyên khoa
<span className="text-red-500">*</span>
</span>
}
name="specialtyNotes" name="specialtyNotes"
type="text" type="text"
value={form.specialtyNotes} value={form.specialtyNotes}
@ -289,7 +305,12 @@ const UpdateSpecialtyClinicId = () => {
/> />
<InputForm <InputForm
type="text" type="text"
label="Chẩn đoán chuyên khoa" label={
<span>
Chẩn đoán chuyên khoa
<span className="text-red-500">*</span>
</span>
}
name="diagnosis" name="diagnosis"
placeholder="Nhập chẩn đoán chuyên khoa" placeholder="Nhập chẩn đoán chuyên khoa"
isRequired isRequired
@ -300,7 +321,12 @@ const UpdateSpecialtyClinicId = () => {
/> />
<InputForm <InputForm
type="text" type="text"
label="Ghi chú thủ thuật / can thiệp" label={
<span>
Ghi chú thủ thuật / can thiệp
<span className="text-red-500">*</span>
</span>
}
name="procedureNotes" name="procedureNotes"
placeholder="Nhập ghi chú thủ thuật / can thiệp" placeholder="Nhập ghi chú thủ thuật / can thiệp"
value={form.procedureNotes} value={form.procedureNotes}
@ -359,7 +385,12 @@ const UpdateSpecialtyClinicId = () => {
<TextArea <TextArea
name="glassType" name="glassType"
placeholder="Nhập thông số chi tiết loại kính được chỉ định (Cận, viễn, loạn, độ kính...)" placeholder="Nhập thông số chi tiết loại kính được chỉ định (Cận, viễn, loạn, độ kính...)"
label="Thông số / Loại kính chỉ định" label={
<span>
Thông số / Loại kính chỉ đnh
<span className="text-red-500">*</span>
</span>
}
value={form.glassType} value={form.glassType}
isRequired isRequired
isBlur isBlur
@ -432,10 +463,10 @@ const UpdateSpecialtyClinicId = () => {
Hủy bỏ Hủy bỏ
</CustomButton> </CustomButton>
<CustomButton <CustomButton
type="submit" onClick={handleSubmit}
variant="midnightBlue" variant="midnightBlue"
rounded="md" rounded="md"
disabled={isLoading || !consultationId} disabled={specialtyResultsMutation.isPending}
className="min-w-[140px]" className="min-w-[140px]"
> >
{specialtyResultsMutation.isPending {specialtyResultsMutation.isPending

View File

@ -225,7 +225,7 @@ const MainPagePatient = () => {
<Search <Search
keyword={_keyword ?? ""} keyword={_keyword ?? ""}
setKeyword={(value) => updateQuery("_keyword", value)} setKeyword={(value) => updateQuery("_keyword", value)}
placeholder="Tìm kiếm theo tên người dùng, ID" placeholder="Tìm kiếm theo tên bệnh nhân, ID"
/> />
</div> </div>
</div> </div>

View File

@ -15,7 +15,11 @@ import { httpRequest } from "@/services";
import patientServices from "@/services/patientServices"; import patientServices from "@/services/patientServices";
import { toastWarn } from "@/common/funcs/toast"; import { toastWarn } from "@/common/funcs/toast";
import { timeSubmit } from "@/common/funcs/optionConvert"; import {
formatDateOfBirth,
parseDateOfBirth,
formatToISODate,
} from "@/common/funcs/optionConvert";
export interface ICreatePatient { export interface ICreatePatient {
identificationNumber: string; identificationNumber: string;
@ -31,11 +35,6 @@ export interface PropsPopupCreatePatient {
onClose: () => void; onClose: () => void;
} }
/* QUERY KEY */
// export const QUERY_KEY = {
// table_list_patient: "table_list_patient",
// };
const PopupCreatePatient = ({ onClose }: PropsPopupCreatePatient) => { const PopupCreatePatient = ({ onClose }: PropsPopupCreatePatient) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -59,7 +58,8 @@ const PopupCreatePatient = ({ onClose }: PropsPopupCreatePatient) => {
http: patientServices.createPatient({ http: patientServices.createPatient({
identificationNumber: form.identificationNumber, identificationNumber: form.identificationNumber,
fullName: form.fullName, fullName: form.fullName,
dateOfBirth: form.dateOfBirth, dateOfBirth:
formatToISODate(parseDateOfBirth(form.dateOfBirth)) || "",
gender: form.gender, gender: form.gender,
phone: form.phone, phone: form.phone,
email: form.email, email: form.email,
@ -68,6 +68,14 @@ const PopupCreatePatient = ({ onClose }: PropsPopupCreatePatient) => {
}); });
}, },
onError(error: any) {
// Lấy message lỗi từ backend trả về
const message = error?.response?.data?.error?.message || error?.message;
// Bật thông báo Toast dạng cảnh báo
toastWarn({ msg: message });
},
onSuccess() { onSuccess() {
/* REFRESH LIST */ /* REFRESH LIST */
queryClient.invalidateQueries({ queryClient.invalidateQueries({
@ -79,15 +87,30 @@ const PopupCreatePatient = ({ onClose }: PropsPopupCreatePatient) => {
}); });
const handleSubmit = () => { const handleSubmit = () => {
if (!form.dateOfBirth) {
return toastWarn({ msg: "Vui lòng nhập ngày sinh!" });
}
const today = new Date();
today.setHours(0, 0, 0, 0);
const birthDay = parseDateOfBirth(form.dateOfBirth);
if (!birthDay) {
return toastWarn({
msg: "Ngày sinh phải theo định dạng ngày/tháng/năm!",
});
}
birthDay.setHours(0, 0, 0, 0);
if (birthDay.getTime() > today.getTime()) {
return toastWarn({ msg: "Ngày sinh không hợp lệ!" });
}
if (!form.gender) { if (!form.gender) {
return toastWarn({ msg: "Vui lòng chọn giới tính!" }); return toastWarn({ msg: "Vui lòng chọn giới tính!" });
} }
const today = new Date(timeSubmit(new Date())!);
const birthDay = new Date(form.dateOfBirth);
if (today < birthDay) {
return toastWarn({ msg: "Ngày sinh không hợp lệ!" });
}
createPatientMutation.mutate(); createPatientMutation.mutate();
}; };
@ -116,7 +139,7 @@ const PopupCreatePatient = ({ onClose }: PropsPopupCreatePatient) => {
absolute absolute
right-5 right-5
top-5 top-5
z-10 z-30
text-gray-400 text-gray-400
transition transition
hover:text-gray-600 hover:text-gray-600
@ -161,6 +184,8 @@ const PopupCreatePatient = ({ onClose }: PropsPopupCreatePatient) => {
name="identificationNumber" name="identificationNumber"
type="number" type="number"
isNumber isNumber
min={12}
max={12}
value={form.identificationNumber} value={form.identificationNumber}
isRequired isRequired
/> />
@ -238,11 +263,16 @@ const PopupCreatePatient = ({ onClose }: PropsPopupCreatePatient) => {
<span className="text-red-500"> *</span> <span className="text-red-500"> *</span>
</span> </span>
} }
placeholder="Ngày sinh" placeholder="DD/MM/YYYY"
name="dateOfBirth" name="dateOfBirth"
type="Date" type="text"
value={form.dateOfBirth} value={form.dateOfBirth}
isRequired onChangeValue={(value) =>
setForm((prev) => ({
...prev,
dateOfBirth: formatDateOfBirth(String(value)),
}))
}
/> />
<InputForm <InputForm
@ -254,8 +284,10 @@ const PopupCreatePatient = ({ onClose }: PropsPopupCreatePatient) => {
} }
placeholder="Số điện thoại" placeholder="Số điện thoại"
name="phone" name="phone"
type="phone" type="number"
isPhone isNumber
min={8}
max={11}
value={form.phone} value={form.phone}
isRequired isRequired
/> />

View File

@ -238,7 +238,9 @@ const PopupUpdatePatient = ({ onClose, id }: PopupUpdatePatientProps) => {
{/* GENDER */} {/* GENDER */}
<div> <div>
<label className="text-base font-medium">Giới tính</label> <label className="text-base font-medium">
Giới tính <span className="text-red-500">*</span>
</label>
<div className="mt-2 flex items-center gap-6"> <div className="mt-2 flex items-center gap-6">
{/* NAM */} {/* NAM */}
@ -293,7 +295,11 @@ const PopupUpdatePatient = ({ onClose, id }: PopupUpdatePatientProps) => {
/> />
<InputForm <InputForm
label={<span>Email</span>} label={
<span>
Email<span className="text-red-500"> *</span>
</span>
}
placeholder="Email" placeholder="Email"
name="email" name="email"
type="email" type="email"
@ -304,7 +310,12 @@ const PopupUpdatePatient = ({ onClose, id }: PopupUpdatePatientProps) => {
<TextArea <TextArea
name="address" name="address"
placeholder="Địa chỉ" placeholder="Địa chỉ"
label="Địa chỉ" label={
<span>
Đa chỉ
<span className="text-red-500"> *</span>
</span>
}
value={form.address} value={form.address}
isRequired isRequired
isBlur isBlur
@ -313,7 +324,12 @@ const PopupUpdatePatient = ({ onClose, id }: PopupUpdatePatientProps) => {
<InputForm <InputForm
isRequired isRequired
label="Người liên hệ khẩn cấp" label={
<span>
Người liên hệ khẩn cấp
<span className="text-red-500"> *</span>
</span>
}
placeholder="Người liên hệ khẩn cấp" placeholder="Người liên hệ khẩn cấp"
name="emergencyContactName" name="emergencyContactName"
type="text" type="text"
@ -321,19 +337,32 @@ const PopupUpdatePatient = ({ onClose, id }: PopupUpdatePatientProps) => {
/> />
<InputForm <InputForm
label="SĐT khẩn cấp" label={
<span>
SĐT khẩn cấp
<span className="text-red-500"> *</span>
</span>
}
placeholder="SĐT khẩn cấp" placeholder="SĐT khẩn cấp"
name="emergencyContactPhone" name="emergencyContactPhone"
type="phone" type="number"
min={8}
max={11}
isRequired isRequired
isPhone // isPhone
value={form.emergencyContactPhone} value={form.emergencyContactPhone}
/> />
<TextArea <TextArea
name="allergyNotes" name="allergyNotes"
label={
<span>
Dị ng
<span className="text-red-500"> *</span>
</span>
}
isRequired
placeholder="Dị ứng" placeholder="Dị ứng"
label="Dị ứng"
value={form.allergyNotes} value={form.allergyNotes}
isBlur isBlur
max={5000} max={5000}
@ -342,14 +371,25 @@ const PopupUpdatePatient = ({ onClose, id }: PopupUpdatePatientProps) => {
<TextArea <TextArea
name="chronicDiseaseNotes" name="chronicDiseaseNotes"
placeholder="Bệnh nền" placeholder="Bệnh nền"
label="Bệnh nền" label={
<span>
Bệnh nền
<span className="text-red-500"> *</span>
</span>
}
isRequired
value={form.chronicDiseaseNotes} value={form.chronicDiseaseNotes}
isBlur isBlur
max={5000} max={5000}
/> />
<InputForm <InputForm
label="Số bảo hiểm" label={
<span>
Số bảo hiểm
<span className="text-red-500"> *</span>
</span>
}
placeholder="Số bảo hiểm" placeholder="Số bảo hiểm"
name="insuranceNumber" name="insuranceNumber"
type="text" type="text"
@ -359,8 +399,8 @@ const PopupUpdatePatient = ({ onClose, id }: PopupUpdatePatientProps) => {
<TextArea <TextArea
name="notes" name="notes"
label={<span>Ghi chú</span>}
placeholder="Ghi chú" placeholder="Ghi chú"
label="Ghi chú"
value={form.notes} value={form.notes}
isBlur isBlur
max={5000} max={5000}

View File

@ -57,7 +57,6 @@ const CreatePrescription = () => {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const queryKeys = [QUERY_KEY.table_list_consultation]; const queryKeys = [QUERY_KEY.table_list_consultation];
const [file, setFile] = useState<File | null>(null); const [file, setFile] = useState<File | null>(null);
const [folderName, setFolderName] = useState("prescriptions");
const [form, setForm] = useState<PrescriptionForm>({ const [form, setForm] = useState<PrescriptionForm>({
examinationSessionSpecialtyId: "", examinationSessionSpecialtyId: "",
@ -393,11 +392,16 @@ const CreatePrescription = () => {
{/* NOTES */} {/* NOTES */}
<TextArea <TextArea
label="Ghi chú" label={
<span>
Ghi chú<span style={{ color: "red" }}>*</span>
</span>
}
name="notes" name="notes"
placeholder="Nhập ghi chú" placeholder="Nhập ghi chú"
value={form.notes} value={form.notes}
isBlur isBlur
isRequired
max={5000} max={5000}
onChangeValue={(value) => onChangeValue={(value) =>
setForm((prev) => ({ setForm((prev) => ({
@ -455,10 +459,15 @@ const CreatePrescription = () => {
{/* INPUTS */} {/* INPUTS */}
<div className="grid grid-cols-2 gap-5"> <div className="grid grid-cols-2 gap-5">
<InputForm <InputForm
label="Tên thuốc" label={
<span>
Tên thuốc<span style={{ color: "red" }}>*</span>
</span>
}
name={`items.${index}.medicineName`} name={`items.${index}.medicineName`}
placeholder="Nhập tên thuốc" placeholder="Nhập tên thuốc"
value={medicine.medicineName} value={medicine.medicineName}
isRequired
type="text" type="text"
onChangeValue={(value) => onChangeValue={(value) =>
handleChangeMedicine(index, "medicineName", String(value)) handleChangeMedicine(index, "medicineName", String(value))
@ -466,18 +475,28 @@ const CreatePrescription = () => {
/> />
<InputForm <InputForm
label="Liều lượng" label={
<span>
Liều lượng<span style={{ color: "red" }}>*</span>
</span>
}
name={`items.${index}.dosage`} name={`items.${index}.dosage`}
placeholder="Ví dụ: 500mg" placeholder="Ví dụ: 500mg"
value={medicine.dosage} value={medicine.dosage}
type="text" type="text"
isRequired
onChangeValue={(value) => onChangeValue={(value) =>
handleChangeMedicine(index, "dosage", String(value)) handleChangeMedicine(index, "dosage", String(value))
} }
/> />
<InputForm <InputForm
label="Tần suất" label={
<span>
Tần xuất<span style={{ color: "red" }}>*</span>
</span>
}
isRequired
name={`items.${index}.frequency`} name={`items.${index}.frequency`}
placeholder="Ví dụ: 2 lần/ngày" placeholder="Ví dụ: 2 lần/ngày"
value={medicine.frequency} value={medicine.frequency}
@ -488,7 +507,12 @@ const CreatePrescription = () => {
/> />
<InputForm <InputForm
label="Thời gian dùng" label={
<span>
Thời gian dùng<span style={{ color: "red" }}>*</span>
</span>
}
isRequired
name={`items.${index}.duration`} name={`items.${index}.duration`}
placeholder="Ví dụ: 7 ngày" placeholder="Ví dụ: 7 ngày"
value={medicine.duration} value={medicine.duration}
@ -502,7 +526,12 @@ const CreatePrescription = () => {
{/* INSTRUCTION */} {/* INSTRUCTION */}
<div className="mt-5"> <div className="mt-5">
<TextArea <TextArea
label="Hướng dẫn sử dụng" label={
<span>
Hướng dẫn sử dụng<span style={{ color: "red" }}>*</span>
</span>
}
isRequired
name={`items.${index}.instructions`} name={`items.${index}.instructions`}
placeholder="Nhập hướng dẫn sử dụng thuốc" placeholder="Nhập hướng dẫn sử dụng thuốc"
value={medicine.instructions} value={medicine.instructions}

View File

@ -25,7 +25,12 @@ import { ContextFormCustom } from "@/components/utils/FormCustom/contexts";
import InputForm from "@/components/utils/FormCustom/components/InputForm"; import InputForm from "@/components/utils/FormCustom/components/InputForm";
import FormCustom from "@/components/utils/FormCustom/FormCustom"; import FormCustom from "@/components/utils/FormCustom/FormCustom";
import moment from "moment"; import moment from "moment";
import { timeSubmit } from "@/common/funcs/optionConvert"; import {
formatDateOfBirth,
formatToISODate,
parseDateOfBirth,
timeSubmit,
} from "@/common/funcs/optionConvert";
import CustomButton from "@/components/customs/custom-button"; import CustomButton from "@/components/customs/custom-button";
function MainUpdateProfile() { function MainUpdateProfile() {
@ -91,7 +96,7 @@ function MainUpdateProfile() {
phone: currentUserProfile.phone || "", phone: currentUserProfile.phone || "",
// Định dạng input date HTML yêu cầu format YYYY-MM-DD // Định dạng input date HTML yêu cầu format YYYY-MM-DD
birthDate: currentUserProfile.birthDate birthDate: currentUserProfile.birthDate
? moment(currentUserProfile.birthDate).format("YYYY-MM-DD") ? moment(currentUserProfile.birthDate).format("DD/MM/YYYY")
: "", : "",
avatarUrl: currentUserProfile.avatarUrl || "", avatarUrl: currentUserProfile.avatarUrl || "",
}); });
@ -112,7 +117,7 @@ function MainUpdateProfile() {
fullName: form.fullName, fullName: form.fullName,
email: form.email, email: form.email,
phone: form.phone, phone: form.phone,
birthDate: form.birthDate, birthDate: formatToISODate(parseDateOfBirth(form.birthDate)),
avatarUrl: body.path || form.avatarUrl, avatarUrl: body.path || form.avatarUrl,
}), // Lưu ý: Hãy cập nhật đúng service update của user profile tại đây }), // Lưu ý: Hãy cập nhật đúng service update của user profile tại đây
}), }),
@ -243,7 +248,12 @@ function MainUpdateProfile() {
placeholder="Nhập họ và tên" placeholder="Nhập họ và tên"
name="fullName" name="fullName"
value={form.fullName} value={form.fullName}
label="Họ và tên" label={
<span>
Họ Tên
<span className="text-red-500">*</span>
</span>
}
isRequired isRequired
onChangeValue={(v) => onChangeValue={(v) =>
setForm((prev) => ({ ...prev, fullName: String(v) })) setForm((prev) => ({ ...prev, fullName: String(v) }))
@ -251,13 +261,22 @@ function MainUpdateProfile() {
/> />
<InputForm <InputForm
type="date" type="text"
placeholder="Nhập ngày sinh" placeholder="Nhập ngày sinh"
name="birthDate" name="birthDate"
label="Ngày sinh" isRequired
label={
<span>
Ngày sinh
<span className="text-red-500">*</span>
</span>
}
value={form.birthDate} value={form.birthDate}
onChangeValue={(v) => onChangeValue={(v) =>
setForm((prev) => ({ ...prev, birthDate: String(v) })) setForm((prev) => ({
...prev,
birthDate: formatDateOfBirth(String(v)),
}))
} }
/> />
</div> </div>
@ -268,8 +287,14 @@ function MainUpdateProfile() {
placeholder="Nhập email" placeholder="Nhập email"
name="email" name="email"
type="email" type="email"
isEmail
value={form.email} value={form.email}
label="Email" label={
<span>
Email
<span className="text-red-500">*</span>
</span>
}
isRequired isRequired
onChangeValue={(v) => onChangeValue={(v) =>
setForm((prev) => ({ ...prev, email: String(v) })) setForm((prev) => ({ ...prev, email: String(v) }))
@ -280,9 +305,16 @@ function MainUpdateProfile() {
placeholder="Nhập số điện thoại" placeholder="Nhập số điện thoại"
name="phone" name="phone"
type="number" type="number"
isNumber
value={form.phone} value={form.phone}
label="Số điện thoại" isRequired
min={8}
max={11}
label={
<span>
Số điện thoại
<span className="text-red-500">*</span>
</span>
}
onChangeValue={(v) => onChangeValue={(v) =>
setForm((prev) => ({ ...prev, phone: String(v) })) setForm((prev) => ({ ...prev, phone: String(v) }))
} }

View File

@ -38,7 +38,7 @@ export default function Providers({ children }: Props) {
<SplashScreen /> <SplashScreen />
<ToastContainer autoClose={3000} /> <ToastContainer autoClose={3000} style={{ zIndex: 10002 }} />
{children} {children}
</QueryClientProvider> </QueryClientProvider>