- {infoUser?.fullname || "User admin"}
+ {currentUserProfile?.fullName || "User admin"}
{
- {/* CHANGE PASSWORD */}
- {/*
-
-
-
Đổi mật khẩu
-
Thay đổi mật khẩu
-
-
- */}
-
{/* LOGOUT */}
{
diff --git a/src/components/page/auth/MainLogin/MainLogin.tsx b/src/components/page/auth/MainLogin/MainLogin.tsx
index 10d5c2e..215b899 100644
--- a/src/components/page/auth/MainLogin/MainLogin.tsx
+++ b/src/components/page/auth/MainLogin/MainLogin.tsx
@@ -1,97 +1,94 @@
"use client";
-import React, { useEffect, useState } from "react";
-
+import React, { useMemo, useState } from "react";
import { useRouter } from "next/navigation";
import { useMutation } from "@tanstack/react-query";
-import { LockKeyhole, ShieldPlus, User } from "lucide-react";
+import { ShieldPlus, User } from "lucide-react";
import { useSelector } from "react-redux";
import { RootState, store } from "@/redux/store";
-
import {
setDataLoginStorage,
setStateLogin,
setToken,
} from "@/redux/reducer/auth";
-
import { setRememberPassword } from "@/redux/reducer/site";
import { setInfoUser } from "@/redux/reducer/user";
-
-import authServices from "@/services/authServices";
+import authServices, { LoginResponse } from "@/services/authServices";
import { httpRequest } from "@/services";
-
import { PATH } from "@/constant/config";
import FormCustom from "@/components/utils/FormCustom";
import InputForm from "@/components/utils/FormCustom/components/InputForm";
import CustomLoading from "@/components/customs/custom-loading";
import CustomButton from "@/components/customs/custom-button";
+import { ContextFormCustom } from "@/components/utils/FormCustom/contexts";
export default function MainLogin() {
const router = useRouter();
const { dataLoginStorage } = useSelector((state: RootState) => state.auth);
-
const { isRememberPassword } = useSelector((state: RootState) => state.site);
- const [form, setForm] = useState({
+ // Khởi tạo state form từ data lưu trữ
+ const [form, setForm] = useState(() => ({
username: isRememberPassword ? dataLoginStorage?.usernameStorage || "" : "",
-
password: isRememberPassword ? dataLoginStorage?.passwordStorage || "" : "",
+ }));
- deviceId: isRememberPassword ? dataLoginStorage?.deviceIdStorage || "" : "",
- });
-
- const loginMutation = useMutation({
+ const loginMutation = useMutation
({
mutationFn: async () => {
- return httpRequest({
- showMessageFailed: true,
- showMessageSuccess: true,
- msgSuccess: "Đăng nhập thành công!",
+ return (await httpRequest({
http: authServices.login({
username: form.username,
password: form.password,
- deviceId: form.username,
+ deviceId: "Browser-Chrome-Windowns",
}),
- });
+ })) as LoginResponse;
},
-
- onSuccess(data: any) {
- if (!data) return;
-
- store.dispatch(setStateLogin(true));
-
- store.dispatch(setToken(data.token));
-
- store.dispatch(
- setInfoUser({
- accessToken: data?.token || "",
- refreshToken: data?.refreshToken || "",
- accessExpiresAt: data?.accessExpiresAt || "",
- refreshExpiresAt: data?.refreshExpiresAt || "",
- avatar: data?.avatar || "",
- fullname: data?.fullname || "",
- }),
- );
-
- if (isRememberPassword) {
+ onSuccess(data) {
+ if (data) {
+ store.dispatch(setToken(data?.token));
store.dispatch(
- setDataLoginStorage({
- usernameStorage: form.username,
- passwordStorage: form.password,
- deviceIdStorage: form.deviceId,
+ setInfoUser({
+ accessToken: data?.token || "",
+ refreshToken: data?.refreshToken || "",
+ userId: data?.userId || "",
}),
);
- } else {
- store.dispatch(setDataLoginStorage(null));
- }
+ store.dispatch(setStateLogin(true));
- router.replace(PATH.HOME);
+ if (isRememberPassword) {
+ store.dispatch(
+ setDataLoginStorage({
+ usernameStorage: form.username,
+ passwordStorage: form.password,
+ }),
+ );
+ } else {
+ store.dispatch(setDataLoginStorage(null));
+ }
+
+ router.replace(PATH.HOME, undefined);
+ }
},
});
const handleLogin = () => {
+ // Kiểm tra nhanh điều kiện dữ liệu trước khi trigger API
+ if (!form.username || !form.password) return;
+
+ if (isRememberPassword) {
+ store.dispatch(
+ setDataLoginStorage({
+ usernameStorage: form.username,
+ passwordStorage: form.password,
+ }),
+ );
+ } else {
+ store.dispatch(setDataLoginStorage(null));
+ }
+
loginMutation.mutate();
};
@@ -101,24 +98,8 @@ export default function MainLogin() {
{/* TITLE */}
-
- Đăng nhập
-
-
-
+
Đăng nhập
+
Chào mừng bạn đến với hệ thống quản lý
@@ -128,8 +109,7 @@ export default function MainLogin() {
- Tài khoản
- *
+ Tài khoản *
}
placeholder="Tài khoản"
@@ -142,32 +122,12 @@ export default function MainLogin() {
icon={}
/>
- {/*
-
- deviceId
- *
-
- }
- placeholder="deviceId"
- type="text"
- name="deviceId"
- onClean
- isRequired
- isBlur
- showDone
- icon={}
- />
-
*/}
-
{/* PASSWORD */}
- Mật khẩu
- *
+ Mật khẩu *
}
placeholder="Mật khẩu"
@@ -182,15 +142,7 @@ export default function MainLogin() {
{/* REMEMBER PASSWORD */}
-
+
store.dispatch(setRememberPassword(!isRememberPassword))
}
- className="
- h-5
- w-5
- cursor-pointer
- accent-[#0011AB]
- "
+ className="h-5 w-5 cursor-pointer accent-[#0011AB]"
/>
-
@@ -223,64 +163,40 @@ export default function MainLogin() {
{/* BUTTONS */}
- {/* LOGIN */}
-
- Đăng nhập
-
- {/* REGISTER */}
+ {/* LOGIN BUTTON */}
+
+ {({ isDone }) => {
+ // Tự kiểm tra xem form hiện tại đã có sẵn dữ liệu hợp lệ chưa
+ const hasRememberedData = !!(form.username && form.password);
+
+ // Sáng nút nếu FormCustom báo Done HOẶC form đã có sẵn dữ liệu từ Redux
+ const canSubmit = isDone || hasRememberedData;
+
+ return (
+
+ Đăng nhập
+
+ );
+ }}
+
+
+ {/* REGISTER BUTTON */}
router.push(PATH.REGISTER)}
>
Đăng ký
-
- {/* LINE */}
- {/*
*/}
-
- {/* FORGOT PASSWORD */}
- {/*
*/}
diff --git a/src/components/page/consultation/DetailConsultation/DetailConsultation.tsx b/src/components/page/consultation/DetailConsultation/DetailConsultation.tsx
index 11692a4..964c1b5 100644
--- a/src/components/page/consultation/DetailConsultation/DetailConsultation.tsx
+++ b/src/components/page/consultation/DetailConsultation/DetailConsultation.tsx
@@ -120,15 +120,29 @@ 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;
+ // };
+
const getFileInfo = (file: AttachmentFile | string, index: number) => {
if (typeof file === "string") {
return {
fileName: file.split("/").pop() || `File ${index + 1}`,
- path: file,
+ path: `${process.env.NEXT_PUBLIC_IMAGE}${file}`,
};
}
- return file;
+ return {
+ ...file,
+ path: `${process.env.NEXT_PUBLIC_IMAGE}${file.path}`,
+ };
};
return (
@@ -282,7 +296,7 @@ const DetailConsultation = () => {
- {consultation.createdBy || "---"}
+ {consultation.createdByName || "---"}
diff --git a/src/components/page/consultation/UpdateConsultation/UpdateConsultation.tsx b/src/components/page/consultation/UpdateConsultation/UpdateConsultation.tsx
index 8652e4b..0b63318 100644
--- a/src/components/page/consultation/UpdateConsultation/UpdateConsultation.tsx
+++ b/src/components/page/consultation/UpdateConsultation/UpdateConsultation.tsx
@@ -17,6 +17,7 @@ import consultationServices, {
} from "@/services/consultationServices";
import uploadServices from "@/services/uploadServices";
import UploadMultipleFile from "@/components/utils/UploadMultipleFile";
+import TextArea from "@/components/utils/FormCustom/components/TextArea";
/* =========================
TYPES
@@ -29,6 +30,7 @@ interface IUpdateConsul {
treatmentPlan: string;
doctorNotes: string;
isGlassesDelivered: boolean;
+ glassType: string;
generalAttachmentUrls: string[];
}
@@ -43,6 +45,7 @@ const defaultForm: IUpdateConsul = {
treatmentPlan: "",
doctorNotes: "",
isGlassesDelivered: false,
+ glassType: "",
generalAttachmentUrls: [],
};
@@ -101,9 +104,14 @@ const UpdateConsultation = () => {
/* =========================
MAP & SET FORM
========================= */
+ const consul = consulDetailQuery.data;
useEffect(() => {
- const consul = consulDetailQuery.data;
if (!consul) return;
+ const eyeClinic = consul?.specialtyClinics?.find(
+ (item) =>
+ item.specialtyName?.toLowerCase().includes("mắt") &&
+ item.glassRequired === true,
+ );
setForm({
chiefComplaint: consul.chiefComplaint || "",
@@ -112,7 +120,8 @@ const UpdateConsultation = () => {
diagnosis: consul.diagnosis || "",
treatmentPlan: consul.treatmentPlan || "",
doctorNotes: consul.doctorNotes || "",
- isGlassesDelivered: consul.isGlassesDelivered,
+ isGlassesDelivered: !!eyeClinic,
+ glassType: eyeClinic?.glassType || "",
generalAttachmentUrls: consul.generalAttachmentUrls || [],
});
@@ -212,6 +221,11 @@ const UpdateConsultation = () => {
);
}
+ const eyeClinic = consul?.specialtyClinics?.find(
+ (item) =>
+ item.specialtyName?.toLowerCase().includes("mắt") &&
+ item.glassRequired === true,
+ );
/* =========================
MAIN UI
@@ -324,6 +338,56 @@ const UpdateConsultation = () => {
}
/>
+
+ {eyeClinic && (
+
+
+ Chỉ định thị lực (Khoa Mắt)
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
diff --git a/src/components/page/consultation/UpdateSpecialtyClinicId/UpdateSpecialtyClinicId.tsx b/src/components/page/consultation/UpdateSpecialtyClinicId/UpdateSpecialtyClinicId.tsx
index 88971b4..37ebbee 100644
--- a/src/components/page/consultation/UpdateSpecialtyClinicId/UpdateSpecialtyClinicId.tsx
+++ b/src/components/page/consultation/UpdateSpecialtyClinicId/UpdateSpecialtyClinicId.tsx
@@ -14,6 +14,7 @@ import consultationServices, {
} from "@/services/consultationServices";
import uploadServices from "@/services/uploadServices";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+import moment from "moment";
import { useParams, useRouter, useSearchParams } from "next/navigation";
import React, { useEffect, useState } from "react";
@@ -62,6 +63,7 @@ const UpdateSpecialtyClinicId = () => {
res || {
id: "",
patientId: "",
+ patientName: "",
sessionCode: "",
visitDate: "",
status: 1,
@@ -71,50 +73,34 @@ const UpdateSpecialtyClinicId = () => {
diagnosis: null,
treatmentPlan: null,
doctorNotes: null,
+ isGlassesDelivered: false,
createdBy: "",
+ createdByName: "",
isDeleted: false,
- specialtyClinics: [
- {
- id: "",
- specialtyId: "",
- specialtyName: "",
- specialtyNotes: null,
- diagnosis: null,
- procedureNotes: null,
- glassRequired: null,
- glassType: null,
- examinerName: "",
- specialtyStatus: 1,
- attachmentUrls: [],
- sharedItems: [],
- },
- ],
+ generalAttachmentUrls: [],
+ specialtyClinics: [],
aggregatedPrescriptionItems: [],
}
);
},
});
- // Tìm chuyên khoa hiện tại từ data trả về
- const selectedSpecialtyClinic =
- consulDetailQuery.data?.specialtyClinics?.find(
- (item) => item.id === specialtyClinicId,
- );
+ // Khai báo dữ liệu lấy từ Query phục vụ hiển thị
+ const generalData = consulDetailQuery.data;
+
+ // Tìm chuyên khoa hiện tại từ data trả về
+ const selectedSpecialtyClinic = generalData?.specialtyClinics?.find(
+ (item) => item.id === specialtyClinicId,
+ );
- /**
- * ĐIỀU KIỆN KIỂM TRA KHOA MẮT
- * Bạn hãy thay đổi điều kiện này tùy thuộc vào database của bạn.
- * Ví dụ: selectedSpecialtyClinic?.specialtyId === "MẮT" hoặc kiểm tra theo tên.
- */
const isEyeClinic =
selectedSpecialtyClinic?.specialtyName?.toLowerCase().includes("mắt") ||
selectedSpecialtyClinic?.specialtyId?.toLowerCase().includes("mat");
useEffect(() => {
- const consul = consulDetailQuery.data;
- if (!consul) return;
+ if (!generalData) return;
- const specialtyClinic = consul.specialtyClinics?.find(
+ const specialtyClinic = generalData.specialtyClinics?.find(
(item) => item.id === specialtyClinicId,
);
@@ -130,18 +116,17 @@ const UpdateSpecialtyClinicId = () => {
fileUrls: specialtyClinic.attachmentUrls || [],
});
setImages(
- (consul.generalAttachmentUrls || []).map((url) => ({
+ (specialtyClinic.attachmentUrls || []).map((url) => ({
file: null,
img: url,
path: url,
})),
);
- }, [consulDetailQuery.data, specialtyClinicId]);
+ }, [generalData, specialtyClinicId]);
/* =========================
UPDATE MUTATION
========================= */
-
const specialtyResultsMutation = useMutation({
mutationFn: async (body: { paths: string[] }) => {
if (!consultationId) {
@@ -151,14 +136,13 @@ const UpdateSpecialtyClinicId = () => {
return httpRequest({
showMessageFailed: true,
showMessageSuccess: true,
- msgSuccess: "Cập nhật phiên khám thành công!",
+ msgSuccess: "Cập nhật kết quả khám chuyên khoa thành công!",
http: consultationServices.specialtyResults(
form.specialtyAssignmentId,
{
specialtyNotes: form.specialtyNotes,
diagnosis: form.diagnosis,
procedureNotes: form.procedureNotes,
- // 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,
@@ -177,7 +161,6 @@ const UpdateSpecialtyClinicId = () => {
});
router.back();
-
router.push(PATH.CONSULTATION || "/consultation");
},
});
@@ -195,12 +178,9 @@ const UpdateSpecialtyClinicId = () => {
if (files.length > 0) {
const uploadResult: any = await uploadServices.uploadImage({
Files: files,
- SessionCode: consulDetailQuery.data?.sessionCode,
+ SessionCode: generalData?.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 || [];
@@ -243,26 +223,64 @@ const UpdateSpecialtyClinicId = () => {