417 lines
14 KiB
TypeScript
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]">
|
|
Mã 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]">
|
|
Mã đị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;
|