metms_fe/src/components/page/patient/DetailPatient/DetailPatient.tsx
2026-05-26 09:50:32 +07:00

417 lines
14 KiB
TypeScript

"use client";
import React, { useState } from "react";
import { useParams, useRouter } from "next/navigation";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import Link from "next/link";
import { ArrowLeft, ClipboardPlus } from "lucide-react";
import patientServices, { PatientDetail } from "@/services/patientServices";
import consultationServices, {
ConsultationItem,
ConsultationResponse,
} from "@/services/consultationServices";
import { httpRequest } from "@/services";
import { QUERY_KEY, TYPE_STATUS } from "@/constant/config/enum";
import { PATH } from "@/constant/config";
import CustomButton from "@/components/customs/custom-button";
import StateActive from "@/components/customs/StateActive";
import DataWrapper from "@/components/customs/DataWrapper";
import Table from "@/components/customs/custom-table";
import CustomDialog from "@/components/customs/custom-dialog";
const DetailPatient = () => {
const params = useParams();
const router = useRouter();
const queryClient = useQueryClient();
const patientId = params?.id as string;
// State quản lý đóng/mở Dialog xác nhận tạo phiên khám
const [openCreateDialog, setOpenCreateDialog] = useState<boolean>(false);
/* =========================
GET DETAIL PATIENT
========================= */
const patientDetailQuery = useQuery<PatientDetail>({
queryKey: [QUERY_KEY.chi_tiet_benh_nhan, patientId],
enabled: !!patientId,
queryFn: async () => {
const res = await httpRequest<PatientDetail>({
showMessageFailed: true,
http: patientServices.detailPatient(patientId),
});
return (
res || {
id: "",
patientCode: "",
identificationNumber: "",
fullName: "",
dateOfBirth: "",
gender: "",
phone: "",
email: "",
address: "",
emergencyContactName: null,
emergencyContactPhone: null,
allergyNotes: null,
chronicDiseaseNotes: null,
insuranceNumber: null,
notes: null,
isActive: false,
}
);
},
});
/* =========================
GET CONSULTATION HISTORY
========================= */
const consultationQuery = useQuery<ConsultationResponse>({
queryKey: [QUERY_KEY.table_list_consultation, patientId],
enabled: !!patientId,
queryFn: async () => {
const res = await httpRequest<ConsultationResponse>({
showMessageFailed: true,
http: consultationServices.getConsultations({
Page: 1,
PageSize: 5,
PatientId: patientId,
}),
});
return (
res || {
items: [],
page: 1,
pageSize: 10,
total: 0,
totalPages: 0,
}
);
},
});
/* =========================
CREATE MUTATION
========================= */
const createConsultationMutation = useMutation({
mutationFn: async () => {
return httpRequest({
showMessageFailed: true,
showMessageSuccess: true,
msgSuccess: "Tạo phiên khám mới thành công!",
// Gọi API với payload chứa patientId của bệnh nhân hiện tại
http: consultationServices.createConsultations({
patientId: patientId,
}),
});
},
onSuccess: (res: any) => {
// Đóng dialog
setOpenCreateDialog(false);
// Làm mới danh sách lịch sử phiên khám của bệnh nhân này
queryClient.invalidateQueries({
queryKey: [QUERY_KEY.table_list_consultation, patientId],
});
// Nếu API trả về thông tin phiên khám vừa tạo (có chứa id), chuyển thẳng tới trang update
if (res?.id) {
router.push(`${PATH.CONSULTATION}/update/${res.id}`);
}
},
});
const patient = patientDetailQuery.data;
const consultationData = consultationQuery.data?.items || [];
/* =========================
HANDLERS DIALOG
========================= */
const handleOpenCreate = () => {
setOpenCreateDialog(true);
};
const handleCloseDialog = () => {
setOpenCreateDialog(false);
};
const handleCreateDialog = () => {
createConsultationMutation.mutate();
};
if (patientDetailQuery.isLoading) {
return (
<div className="flex items-center justify-center p-10 font-medium text-gray-500">
Đang tải dữ liệu...
</div>
);
}
return (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-4">
<div className="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm">
<div className="flex flex-col gap-5">
<div className="flex items-center justify-between">
<h2 className="text-[28px] flex items-center gap-2 font-semibold text-[#111827]">
<ArrowLeft
onClick={() => router.back()}
className="cursor-pointer"
/>{" "}
{patient?.fullName}
</h2>
<div>
<CustomButton
variant="midnightBlue"
fullWidth={false}
icon={<ClipboardPlus size={18} />}
onClick={handleOpenCreate}
disabled={createConsultationMutation.isPending}
>
Tạo phiên khám
</CustomButton>
</div>
</div>
{/* Thông tin chi tiết bệnh nhân */}
<div className="grid grid-cols-2 gap-5">
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">
bệnh nhân
</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.patientCode}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">Họ tên</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.fullName}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">
đnh danh
</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.identificationNumber}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">
Ngày sinh
</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.dateOfBirth}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">
Giới tính
</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.gender}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">
Số điện thoại
</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.phone}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">Email</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.email}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">
Đa chỉ
</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.address || "-"}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">
Người liên hệ khẩn cấp
</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.emergencyContactName || "-"}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">
SĐT khẩn cấp
</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.emergencyContactPhone || "-"}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">Dị ng</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.allergyNotes || "-"}
</p>
</div>
<div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">
Bệnh nền
</p>
<p className="text-[15px] font-medium text-[#202939]">
{patient?.chronicDiseaseNotes || "-"}
</p>
</div>
</div>
</div>
</div>
</div>
{/* Lịch sử phiên khám */}
<div className="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm">
<div className="flex items-center justify-between">
<h2 className="text-xl font-semibold text-black">
Lịch sử phiên khám
</h2>
<Link
href={`/consultation`}
className="text-blue-500 hover:underline"
>
Tất cả
</Link>
</div>
<DataWrapper
data={consultationData}
loading={consultationQuery.isLoading}
title="Không có dữ liệu"
note="Bệnh nhân chưa có lịch sử khám"
>
<Table
rowKey={(item) => item.id}
data={consultationData}
column={[
{
fixedLeft: true,
title: "ID PHIÊN KHÁM",
render: (item: ConsultationItem) => (
<Link
href={`/consultation/${item.id}`}
className="text-blue-500 hover:underline"
>
{item.id}
</Link>
),
},
{
title: "MÃ PHIÊN KHÁM",
render: (item: ConsultationItem) => (
<span>{item.sessionCode}</span>
),
},
{
title: "NGÀY KHÁM",
render: (item: ConsultationItem) => (
<span>{item.visitDate}</span>
),
},
{
title: "DẤU HIỆU",
render: (item: ConsultationItem) => (
<span>{item.symptomsText || "---"}</span>
),
},
{
title: "CHUẨN ĐOÁN",
render: (item: ConsultationItem) => (
<span>{item.diagnosis || "---"}</span>
),
},
{
title: "KẾ HOẠCH ĐIỀU TRỊ",
render: (item: ConsultationItem) => (
<span>{item.treatmentPlan || "---"}</span>
),
},
{
title: "GHI CHÚ BÁC SĨ",
render: (item: ConsultationItem) => (
<span>{item.doctorNotes || "---"}</span>
),
},
{
title: "TRẠNG THÁI",
render: (item: ConsultationItem) => (
<StateActive
stateActive={item.status}
listState={[
{
state: TYPE_STATUS.Draft,
text: "Đang chờ",
textColor: "#000",
backgroundColor: "#FFE4C4",
},
{
state: TYPE_STATUS.InProgress,
text: "Đang thực hiện",
textColor: "#000",
backgroundColor: "#ff3300",
},
{
state: TYPE_STATUS.Completed,
text: "Hoàn thành",
textColor: "#000",
backgroundColor: "#3d69eb",
},
{
state: TYPE_STATUS.Cancelled,
text: "Đã hủy",
textColor: "#000",
backgroundColor: "#cccc",
},
]}
/>
),
},
]}
/>
</DataWrapper>
</div>
{/* DIALOG XÁC NHẬN */}
<CustomDialog
open={openCreateDialog}
title="Tạo phiên khám"
note={`Bạn có chắc chắn muốn tạo phiên khám cho bệnh nhân ${patient?.fullName} không?`}
type="successPlay" // Giữ nguyên type của bạn, hoặc đổi thành "warning"/"info" tùy UI component quy định
onClose={handleCloseDialog}
onSubmit={handleCreateDialog}
titleCancel="Đóng"
titleSubmit={
createConsultationMutation.isPending ? "Đang xử lý..." : "Xác nhận"
}
/>
</div>
);
};
export default DetailPatient;