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 CustomButton from "@/components/customs/custom-button";
import Pagination from "@/components/customs/custom-pagination";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@radix-ui/react-tooltip";
const DetailConsultation = () => {
const params = useParams();
@ -178,7 +184,7 @@ const DetailConsultation = () => {
<GridColumn col={3}>
{/* ID PHIÊN KHÁM */}
<div className="space-y-1">
{/* <div className="space-y-1">
<p className="text-[14px] font-medium text-[#697586]">
ID PHIÊN KHÁM
</p>
@ -186,7 +192,7 @@ const DetailConsultation = () => {
<p className="text-[15px] font-medium text-[#202939]">
{consultation.id}
</p>
</div>
</div> */}
{/* MÃ PHIÊN KHÁM */}
<div className="space-y-1">
@ -330,14 +336,36 @@ const DetailConsultation = () => {
<p className="text-[14px] font-medium text-[#697586]">
FILE UPLOAD
</p>
{consultation.generalAttachmentUrls?.length > 0 ? (
<ul className="space-y-2">
{consultation.generalAttachmentUrls.map((item, index) => {
<TooltipProvider>
<Tooltip>
<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>
<TooltipContent
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>
<div className="max-h-[300px] space-y-2 overflow-y-auto">
{consultation.generalAttachmentUrls.map(
(item, index) => {
const file = getFileInfo(item, index);
const extension =
file.fileName.split(".").pop()?.toLowerCase() || "";
file.fileName
.split(".")
.pop()
?.toLowerCase() || "";
const isImage = [
"jpg",
@ -350,33 +378,43 @@ const DetailConsultation = () => {
].includes(extension);
return (
<li key={index}>
{isImage ? (
<a
key={index}
href={file.path}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
className="flex items-center justify-between rounded-lg border border-gray-200 p-3 transition hover:bg-gray-50"
>
🖼 {file.fileName}
</a>
) : (
<a
href={file.path}
download={file.fileName}
className="text-green-600 hover:underline"
>
📄 {file.fileName}
</a>
)}
</li>
);
})}
</ul>
) : (
<p className="text-gray-500">
Không file nào đưc tải lên.
<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>
) : (
<div>Không file nào đưc tải lên</div>
)}
</div>
@ -408,7 +446,7 @@ const DetailConsultation = () => {
},
{
state: TYPE_STATUS.PendingPrescription,
text: "Chờ nhận thuốc/kính",
text: "Chờ nhận thuốc",
textColor: "#9A3412",
backgroundColor: "#FED7AA",
},
@ -560,17 +598,37 @@ const DetailConsultation = () => {
</div>
<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
</p>
{selectedSpecialty.attachmentUrls?.length > 0 ? (
<ul className="space-y-2">
{selectedSpecialty.attachmentUrls.map((item, index) => {
<TooltipProvider>
<Tooltip>
<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>
<TooltipContent
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>
<div className="max-h-[300px] space-y-2 overflow-y-auto">
{selectedSpecialty.attachmentUrls.map(
(item, index) => {
const file = getFileInfo(item, index);
const extension =
file.fileName.split(".").pop()?.toLowerCase() || "";
file.fileName.split(".").pop()?.toLowerCase() ||
"";
const isImage = [
"jpg",
@ -583,31 +641,43 @@ const DetailConsultation = () => {
].includes(extension);
return (
<li key={index}>
{isImage ? (
<a
key={index}
href={file.path}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline"
className="flex items-center justify-between rounded-lg border border-gray-200 p-3 transition hover:bg-gray-50"
>
🖼 {file.fileName}
<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>
) : (
<a
href={file.path}
download={file.fileName}
className="text-green-600 hover:underline"
>
📄 {file.fileName}
</a>
)}
</li>
);
})}
</ul>
},
)}
</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>
@ -624,6 +694,20 @@ const DetailConsultation = () => {
<p>{selectedSpecialty.glassType || "---"}</p>
</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>

View File

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

View File

@ -36,6 +36,7 @@ const UpdateSpecialtyClinicId = () => {
procedureNotes: string;
glassRequired: boolean;
glassType: string;
isGlassesDelivered: boolean;
fileUrls: string[];
}>({
specialtyAssignmentId: "",
@ -44,6 +45,7 @@ const UpdateSpecialtyClinicId = () => {
procedureNotes: "",
glassRequired: false,
glassType: "",
isGlassesDelivered: false,
fileUrls: [],
});
@ -113,6 +115,7 @@ const UpdateSpecialtyClinicId = () => {
procedureNotes: specialtyClinic.procedureNotes || "",
glassRequired: specialtyClinic.glassRequired || false,
glassType: specialtyClinic.glassType || "",
isGlassesDelivered: specialtyClinic.isGlassesDelivered || false,
fileUrls: specialtyClinic.attachmentUrls || [],
});
setImages(
@ -145,6 +148,7 @@ const UpdateSpecialtyClinicId = () => {
procedureNotes: form.procedureNotes,
glassRequired: isEyeClinic ? form.glassRequired : false,
glassType: isEyeClinic ? form.glassType : "",
isGlassesDelivered: isEyeClinic ? form.isGlassesDelivered : false,
fileUrls: body.paths,
},
),
@ -360,6 +364,7 @@ const UpdateSpecialtyClinicId = () => {
</div>
{form.glassRequired && (
<div>
<div className="mt-2 animate-fadeIn ">
<TextArea
name="glassType"
@ -371,6 +376,45 @@ const UpdateSpecialtyClinicId = () => {
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>

View File

@ -3,7 +3,7 @@
import React, { useMemo, useState } from "react";
import moment from "moment";
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 DataWrapper from "@/components/customs/DataWrapper";
@ -116,6 +116,18 @@ const MainPageStatistical = () => {
{report?.totalPrescriptionsIssued || 0}
</h3>
</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>
{/* BẢNG THỐNG KÊ */}
@ -177,10 +189,10 @@ const MainPageStatistical = () => {
<strong> {report?.totalPrescriptionsIssued || 0}</strong> đơn.
</p>
{/* <p>
Số suất quà đưc phát:
<strong> {report?.totalGiftPackagesIssued || 0}</strong> suất.
</p> */}
<p>
Tổng số kính đã phát:
<strong> {report?.totalGlassesDelivered || 0}</strong> cái.
</p>
</div>
</div>
</div>

View File

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