feat: Update glasses

This commit is contained in:
TuanVT 2026-06-08 11:24:57 +07:00
parent 568458e098
commit 4fb39ab9fa
5 changed files with 245 additions and 102 deletions

View File

@ -22,6 +22,12 @@ import DataWrapper from "@/components/customs/DataWrapper";
import Table from "@/components/customs/custom-table"; import Table from "@/components/customs/custom-table";
import CustomButton from "@/components/customs/custom-button"; import CustomButton from "@/components/customs/custom-button";
import Pagination from "@/components/customs/custom-pagination"; import Pagination from "@/components/customs/custom-pagination";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@radix-ui/react-tooltip";
const DetailConsultation = () => { const DetailConsultation = () => {
const params = useParams(); const params = useParams();
@ -178,7 +184,7 @@ const DetailConsultation = () => {
<GridColumn col={3}> <GridColumn col={3}>
{/* ID PHIÊN KHÁM */} {/* ID PHIÊN KHÁM */}
<div className="space-y-1"> {/* <div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]"> <p className="text-[14px] font-medium text-[#697586]">
ID PHIÊN KHÁM ID PHIÊN KHÁM
</p> </p>
@ -186,7 +192,7 @@ const DetailConsultation = () => {
<p className="text-[15px] font-medium text-[#202939]"> <p className="text-[15px] font-medium text-[#202939]">
{consultation.id} {consultation.id}
</p> </p>
</div> </div> */}
{/* MÃ PHIÊN KHÁM */} {/* MÃ PHIÊN KHÁM */}
<div className="space-y-1"> <div className="space-y-1">
@ -330,53 +336,85 @@ const DetailConsultation = () => {
<p className="text-[14px] font-medium text-[#697586]"> <p className="text-[14px] font-medium text-[#697586]">
FILE UPLOAD FILE UPLOAD
</p> </p>
{consultation.generalAttachmentUrls?.length > 0 ? ( {consultation.generalAttachmentUrls?.length > 0 ? (
<ul className="space-y-2"> <TooltipProvider>
{consultation.generalAttachmentUrls.map((item, index) => { <Tooltip>
const file = getFileInfo(item, index); <TooltipTrigger asChild>
<div className="inline-flex cursor-pointer items-center gap-2 rounded-lg border border-blue-100 bg-blue-50 px-3 py-2 text-sm font-medium text-blue-600 transition hover:bg-blue-100">
<span>📎</span>
<span>Chi tiết các file</span>
</div>
</TooltipTrigger>
const extension = <TooltipContent
file.fileName.split(".").pop()?.toLowerCase() || ""; side="bottom"
align="start"
className="w-[350px] rounded-xl border bg-white p-4 shadow-xl"
>
<div className="space-y-3">
<h4 className="text-sm font-semibold text-gray-800">
Danh sách file đính kèm
</h4>
const isImage = [ <div className="max-h-[300px] space-y-2 overflow-y-auto">
"jpg", {consultation.generalAttachmentUrls.map(
"jpeg", (item, index) => {
"png", const file = getFileInfo(item, index);
"gif",
"webp",
"bmp",
"svg",
].includes(extension);
return ( const extension =
<li key={index}> file.fileName
{isImage ? ( .split(".")
<a .pop()
href={file.path} ?.toLowerCase() || "";
target="_blank"
rel="noopener noreferrer" const isImage = [
className="text-blue-500 hover:underline" "jpg",
> "jpeg",
🖼 {file.fileName} "png",
</a> "gif",
) : ( "webp",
<a "bmp",
href={file.path} "svg",
download={file.fileName} ].includes(extension);
className="text-green-600 hover:underline"
> return (
📄 {file.fileName} <a
</a> key={index}
)} href={file.path}
</li> target="_blank"
); rel="noopener noreferrer"
})} className="flex items-center justify-between rounded-lg border border-gray-200 p-3 transition hover:bg-gray-50"
</ul> >
<div className="flex min-w-0 items-center gap-3">
<span className="text-xl">
{isImage ? "🖼️" : "📄"}
</span>
<div className="min-w-0">
<p className="truncate text-sm font-medium text-gray-800">
{file.fileName}
</p>
<p className="text-xs text-gray-500 uppercase">
{extension || "FILE"}
</p>
</div>
</div>
<span className="text-xs text-blue-500">
Mở
</span>
</a>
);
},
)}
</div>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : ( ) : (
<p className="text-gray-500"> <div>Không file nào đưc tải lên</div>
Không file nào đưc tải lên.
</p>
)} )}
</div> </div>
@ -408,7 +446,7 @@ const DetailConsultation = () => {
}, },
{ {
state: TYPE_STATUS.PendingPrescription, state: TYPE_STATUS.PendingPrescription,
text: "Chờ nhận thuốc/kính", text: "Chờ nhận thuốc",
textColor: "#9A3412", textColor: "#9A3412",
backgroundColor: "#FED7AA", backgroundColor: "#FED7AA",
}, },
@ -560,54 +598,86 @@ const DetailConsultation = () => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]"> <p className="text-sm font-medium uppercase tracking-wide text-[#697586]">
FILE UPLOAD FILE UPLOAD
</p> </p>
{selectedSpecialty.attachmentUrls?.length > 0 ? ( {selectedSpecialty.attachmentUrls?.length > 0 ? (
<ul className="space-y-2"> <TooltipProvider>
{selectedSpecialty.attachmentUrls.map((item, index) => { <Tooltip>
const file = getFileInfo(item, index); <TooltipTrigger asChild>
<div className="inline-flex cursor-pointer items-center gap-2 rounded-lg border border-blue-100 bg-blue-50 px-3 py-2 text-sm font-medium text-blue-600 transition hover:bg-blue-100">
<span>📎</span>
<span>Chi tiết các file</span>
</div>
</TooltipTrigger>
const extension = <TooltipContent
file.fileName.split(".").pop()?.toLowerCase() || ""; side="bottom"
align="start"
className="w-[350px] rounded-xl border bg-white p-4 shadow-xl"
>
<div className="space-y-3">
<h4 className="text-sm font-semibold text-gray-800">
Danh sách file đính kèm
</h4>
const isImage = [ <div className="max-h-[300px] space-y-2 overflow-y-auto">
"jpg", {selectedSpecialty.attachmentUrls.map(
"jpeg", (item, index) => {
"png", const file = getFileInfo(item, index);
"gif",
"webp",
"bmp",
"svg",
].includes(extension);
return ( const extension =
<li key={index}> file.fileName.split(".").pop()?.toLowerCase() ||
{isImage ? ( "";
<a
href={file.path} const isImage = [
target="_blank" "jpg",
rel="noopener noreferrer" "jpeg",
className="text-blue-500 hover:underline" "png",
> "gif",
🖼 {file.fileName} "webp",
</a> "bmp",
) : ( "svg",
<a ].includes(extension);
href={file.path}
download={file.fileName} return (
className="text-green-600 hover:underline" <a
> key={index}
📄 {file.fileName} href={file.path}
</a> target="_blank"
)} rel="noopener noreferrer"
</li> className="flex items-center justify-between rounded-lg border border-gray-200 p-3 transition hover:bg-gray-50"
); >
})} <div className="flex min-w-0 items-center gap-3">
</ul> <span className="text-xl">
{isImage ? "🖼️" : "📄"}
</span>
<div className="min-w-0">
<p className="truncate text-sm font-medium text-gray-800">
{file.fileName}
</p>
<p className="text-xs text-gray-500 uppercase">
{extension || "FILE"}
</p>
</div>
</div>
<span className="text-xs text-blue-500">
Mở
</span>
</a>
);
},
)}
</div>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : ( ) : (
<p className="text-gray-500">Không file nào đưc tải lên.</p> <div>Không file nào đưc tải lên</div>
)} )}
</div> </div>
@ -624,6 +694,20 @@ const DetailConsultation = () => {
<p>{selectedSpecialty.glassType || "---"}</p> <p>{selectedSpecialty.glassType || "---"}</p>
</div> </div>
<div>
<p className="text-sm text-[#697586]">ĐÃ PHÁT KÍNH CHƯA</p>
{selectedSpecialty.isGlassesDelivered ? (
<p className="bg-[#DCFCE7] text-[#166534] p-2 border rounded-full w-fit">
Đã phát kính
</p>
) : (
<p className="bg-[#fee2e2] text-[#991B1B] p-2 border rounded-full w-fit">
Chưa phát kính
</p>
)}
</div>
</> </>
)} )}
</GridColumn> </GridColumn>

View File

@ -542,7 +542,7 @@ const MainPageConsultation = () => {
}, },
{ {
state: TYPE_STATUS.PendingPrescription, state: TYPE_STATUS.PendingPrescription,
text: "Chờ nhận thuốc/kính", text: "Chờ nhận thuốc",
textColor: "#9A3412", textColor: "#9A3412",
backgroundColor: "#FED7AA", backgroundColor: "#FED7AA",
}, },

View File

@ -36,6 +36,7 @@ const UpdateSpecialtyClinicId = () => {
procedureNotes: string; procedureNotes: string;
glassRequired: boolean; glassRequired: boolean;
glassType: string; glassType: string;
isGlassesDelivered: boolean;
fileUrls: string[]; fileUrls: string[];
}>({ }>({
specialtyAssignmentId: "", specialtyAssignmentId: "",
@ -44,6 +45,7 @@ const UpdateSpecialtyClinicId = () => {
procedureNotes: "", procedureNotes: "",
glassRequired: false, glassRequired: false,
glassType: "", glassType: "",
isGlassesDelivered: false,
fileUrls: [], fileUrls: [],
}); });
@ -113,6 +115,7 @@ const UpdateSpecialtyClinicId = () => {
procedureNotes: specialtyClinic.procedureNotes || "", procedureNotes: specialtyClinic.procedureNotes || "",
glassRequired: specialtyClinic.glassRequired || false, glassRequired: specialtyClinic.glassRequired || false,
glassType: specialtyClinic.glassType || "", glassType: specialtyClinic.glassType || "",
isGlassesDelivered: specialtyClinic.isGlassesDelivered || false,
fileUrls: specialtyClinic.attachmentUrls || [], fileUrls: specialtyClinic.attachmentUrls || [],
}); });
setImages( setImages(
@ -145,6 +148,7 @@ const UpdateSpecialtyClinicId = () => {
procedureNotes: form.procedureNotes, procedureNotes: form.procedureNotes,
glassRequired: isEyeClinic ? form.glassRequired : false, glassRequired: isEyeClinic ? form.glassRequired : false,
glassType: isEyeClinic ? form.glassType : "", glassType: isEyeClinic ? form.glassType : "",
isGlassesDelivered: isEyeClinic ? form.isGlassesDelivered : false,
fileUrls: body.paths, fileUrls: body.paths,
}, },
), ),
@ -360,16 +364,56 @@ const UpdateSpecialtyClinicId = () => {
</div> </div>
{form.glassRequired && ( {form.glassRequired && (
<div className="mt-2 animate-fadeIn "> <div>
<TextArea <div className="mt-2 animate-fadeIn ">
name="glassType" <TextArea
placeholder="Nhập thông số chi tiết loại kính được chỉ định (Cận, viễn, loạn, độ kính...)" name="glassType"
label="Thông số / Loại kính chỉ đị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...)"
value={form.glassType} label="Thông số / Loại kính chỉ định"
isRequired value={form.glassType}
isBlur isRequired
max={5000} isBlur
/> max={5000}
/>
</div>
<div>
<label className="text-sm font-medium text-gray-700">
Đã phát kính chưa
</label>
<div className="mt-2.5 flex items-center gap-8">
<label className="flex cursor-pointer items-center gap-2 text-sm font-medium text-gray-900">
<input
type="radio"
name="isGlassesDelivered"
className="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500"
checked={form.isGlassesDelivered === true}
onChange={() =>
setForm((prev) => ({
...prev,
isGlassesDelivered: true,
}))
}
/>
Đã phát kính
</label>
<label className="flex cursor-pointer items-center gap-2 text-sm font-medium text-gray-900">
<input
type="radio"
name="isGlassesDelivered"
className="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500"
checked={form.isGlassesDelivered === false}
onChange={() =>
setForm((prev) => ({
...prev,
isGlassesDelivered: false,
}))
}
/>
Chưa phát kính
</label>
</div>
</div>
</div> </div>
)} )}
</div> </div>

View File

@ -3,7 +3,7 @@
import React, { useMemo, useState } from "react"; import React, { useMemo, useState } from "react";
import moment from "moment"; import moment from "moment";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { Users, Stethoscope, Pill } from "lucide-react"; import { Users, Stethoscope, Pill, Glasses } from "lucide-react";
import Table from "@/components/customs/custom-table"; import Table from "@/components/customs/custom-table";
import DataWrapper from "@/components/customs/DataWrapper"; import DataWrapper from "@/components/customs/DataWrapper";
@ -116,6 +116,18 @@ const MainPageStatistical = () => {
{report?.totalPrescriptionsIssued || 0} {report?.totalPrescriptionsIssued || 0}
</h3> </h3>
</div> </div>
<div className="rounded-xl bg-white p-5 shadow">
<div className="mb-3 flex items-center justify-between">
<Glasses size={22} />
</div>
<p className="text-sm text-gray-500">Số kính đã phát</p>
<h3 className="mt-2 text-3xl font-bold">
{report?.totalGlassesDelivered || 0}
</h3>
</div>
</div> </div>
{/* BẢNG THỐNG KÊ */} {/* BẢNG THỐNG KÊ */}
@ -177,10 +189,10 @@ const MainPageStatistical = () => {
<strong> {report?.totalPrescriptionsIssued || 0}</strong> đơn. <strong> {report?.totalPrescriptionsIssued || 0}</strong> đơn.
</p> </p>
{/* <p> <p>
Số suất quà đưc phát: Tổng số kính đã phát:
<strong> {report?.totalGiftPackagesIssued || 0}</strong> suất. <strong> {report?.totalGlassesDelivered || 0}</strong> cái.
</p> */} </p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -89,6 +89,7 @@ export interface ConsultationDetail {
procedureNotes: string | null; procedureNotes: string | null;
glassRequired: boolean | null; glassRequired: boolean | null;
glassType: string | null; glassType: string | null;
isGlassesDelivered: boolean;
examinerName: string; examinerName: string;
specialtyStatus: string | number | undefined; specialtyStatus: string | number | undefined;
attachmentUrls: string[]; attachmentUrls: string[];
@ -133,6 +134,7 @@ export interface ReportResponse {
totalCases: number; totalCases: number;
}[]; }[];
totalPrescriptionsIssued: number; totalPrescriptionsIssued: number;
totalGlassesDelivered: number;
} }
const consultationServices = { const consultationServices = {
@ -220,6 +222,7 @@ const consultationServices = {
procedureNotes: string; procedureNotes: string;
glassRequired: boolean; glassRequired: boolean;
glassType: string; glassType: string;
isGlassesDelivered: boolean;
fileUrls: string[]; fileUrls: string[];
}, },
tokenAxios?: any, tokenAxios?: any,