321 lines
13 KiB
TypeScript
321 lines
13 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useState } from "react";
|
|
import { useQueries } from "@tanstack/react-query";
|
|
import { useTabStore as useGridTabStore } from "@rsone/components";
|
|
|
|
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
|
|
import type { VerificationResultReportItem } from "@/entities/statistics/model/statistics-interface";
|
|
import { useAlertStore, useMenuStore } from "@/shared/lib";
|
|
import {
|
|
findMenuItemByUrlPath,
|
|
resolveInputPageNavigationFromStatsCd,
|
|
} from "@/shared/lib/stats-cd-input-navigation";
|
|
import { useStatisticsInputFocusStore } from "@/store/statistics-input-focus-store";
|
|
import { useTabStore } from "@/store/tab-store";
|
|
import dayjs from "dayjs";
|
|
|
|
import {
|
|
Badge,
|
|
type BadgeProps,
|
|
LoadingWithAnimation,
|
|
Tabs,
|
|
TabsContent,
|
|
TabsList,
|
|
TabsTrigger,
|
|
} from "@common/components";
|
|
|
|
type VerificationReportTabKey = "NV" | "IMR" | "HB";
|
|
|
|
const VERIFICATION_REPORT_TABS: { value: VerificationReportTabKey; label: string }[] = [
|
|
{ value: "NV", label: "일반" },
|
|
{ value: "IMR", label: "I-MR" },
|
|
{ value: "HB", label: "HB" },
|
|
];
|
|
|
|
const TYPE_CODE_COLOR_MAP: Record<string, BadgeProps["color"]> = {
|
|
NV: "gray",
|
|
IMR: "red",
|
|
HB: "blue",
|
|
};
|
|
|
|
const TYPE_CODE_NAME_MAP: Record<string, string> = {
|
|
NV: "일반",
|
|
IMR: "I-MR",
|
|
HB: "HB",
|
|
};
|
|
|
|
type VerificationSelectionReportProps = {
|
|
onClose?: () => void;
|
|
};
|
|
|
|
const VerificationSelectionReport = ({
|
|
onClose,
|
|
}: VerificationSelectionReportProps) => {
|
|
const menuList = useTabStore((s) => s.menuList);
|
|
const addTab = useTabStore((s) => s.addTab);
|
|
const setCurrentMenuInfo = useMenuStore((s) => s.setCurrentMenuInfo);
|
|
const addTabForGrid = useGridTabStore((s) => s.addTab);
|
|
const setPendingInputTab = useStatisticsInputFocusStore(
|
|
(s) => s.setPendingInputTab,
|
|
);
|
|
const alert = useAlertStore((s) => s.setMessage);
|
|
const [activeTab, setActiveTab] = useState<VerificationReportTabKey>("NV");
|
|
|
|
const statsYr = String(dayjs().year() - 1);
|
|
|
|
const baseRequest = {
|
|
statsYr,
|
|
pbcpAprvYn: "N" as const,
|
|
page: 1,
|
|
size: 10000,
|
|
prcsYnList: "0",
|
|
};
|
|
|
|
const [nvQuery, imrQuery, hbQuery] = useQueries({
|
|
queries: [
|
|
{
|
|
queryKey: [
|
|
"verificationResultList",
|
|
statsYr,
|
|
"NV",
|
|
baseRequest.pbcpAprvYn,
|
|
baseRequest.page,
|
|
baseRequest.size,
|
|
baseRequest.prcsYnList,
|
|
],
|
|
queryFn: () =>
|
|
statisticsApi.getVerificationResultList({
|
|
...baseRequest,
|
|
vrfcMthdCd: "NV",
|
|
}),
|
|
},
|
|
{
|
|
queryKey: [
|
|
"verificationResultList",
|
|
statsYr,
|
|
"IMR",
|
|
baseRequest.pbcpAprvYn,
|
|
baseRequest.page,
|
|
baseRequest.size,
|
|
baseRequest.prcsYnList,
|
|
],
|
|
queryFn: () =>
|
|
statisticsApi.getVerificationResultList({
|
|
...baseRequest,
|
|
vrfcMthdCd: "IMR",
|
|
}),
|
|
},
|
|
{
|
|
queryKey: [
|
|
"verificationResultList",
|
|
statsYr,
|
|
"HB",
|
|
baseRequest.pbcpAprvYn,
|
|
baseRequest.page,
|
|
baseRequest.size,
|
|
baseRequest.prcsYnList,
|
|
],
|
|
queryFn: () =>
|
|
statisticsApi.getVerificationResultList({
|
|
...baseRequest,
|
|
vrfcMthdCd: "HB",
|
|
}),
|
|
},
|
|
],
|
|
});
|
|
|
|
const isLoading =
|
|
nvQuery.isLoading || imrQuery.isLoading || hbQuery.isLoading;
|
|
const error = nvQuery.error ?? imrQuery.error ?? hbQuery.error;
|
|
|
|
const listsByTab: Record<VerificationReportTabKey, VerificationResultReportItem[]> = {
|
|
NV: nvQuery.data?.data?.verifyResultList ?? [],
|
|
IMR: imrQuery.data?.data?.verifyResultList ?? [],
|
|
HB: hbQuery.data?.data?.verifyResultList ?? [],
|
|
};
|
|
|
|
const openInputFromStatsCd = useCallback(
|
|
(item: VerificationResultReportItem) => {
|
|
const nav = resolveInputPageNavigationFromStatsCd(item.statsCd);
|
|
if (!nav) {
|
|
alert(
|
|
`입력 화면으로 연결되지 않는 통계코드입니다: ${item.statsCd}`,
|
|
);
|
|
return;
|
|
}
|
|
if (!menuList) {
|
|
alert(
|
|
"메뉴 정보를 불러오는 중입니다. 잠시 후 다시 시도해 주세요.",
|
|
);
|
|
return;
|
|
}
|
|
const menu = findMenuItemByUrlPath(menuList, nav.urlPath);
|
|
if (!menu) {
|
|
alert(
|
|
"해당 입력 메뉴를 찾을 수 없습니다. 메뉴 권한을 확인해 주세요.",
|
|
);
|
|
return;
|
|
}
|
|
setPendingInputTab(nav.urlPath, nav.tabKey, {
|
|
statsYr: item.statsYr,
|
|
sewerBzmnCd: item.sewerBzmnCd,
|
|
ctpvNm: item.ctpvNm,
|
|
sggNm: item.sggNm ?? "",
|
|
});
|
|
setCurrentMenuInfo(menu);
|
|
const ok = addTab(menu);
|
|
if (ok) {
|
|
addTabForGrid(menu);
|
|
} else {
|
|
alert(`최대 추가 할 수 있는 화면 수(10개)를 \n초과 하였습니다.
|
|
기존 화면을 닫은 후 이용해주세요.`);
|
|
}
|
|
},
|
|
[
|
|
menuList,
|
|
addTab,
|
|
addTabForGrid,
|
|
setCurrentMenuInfo,
|
|
setPendingInputTab,
|
|
alert,
|
|
],
|
|
);
|
|
|
|
const renderResultList = (
|
|
typeCode: VerificationReportTabKey,
|
|
items: VerificationResultReportItem[],
|
|
) => (
|
|
<div className="min-h-0 flex-1 overflow-y-auto p-2">
|
|
{items.length === 0 ? (
|
|
<div className="flex h-full min-h-[8rem] items-center justify-center text-lg text-muted-foreground">
|
|
항목이 없습니다.
|
|
</div>
|
|
) : (
|
|
<div className="space-y-2 tracking-tight">
|
|
{items.map((item) => (
|
|
<div
|
|
key={`${typeCode}-${item.vrfcSn}`}
|
|
role="button"
|
|
tabIndex={0}
|
|
className="flex flex-col rounded-sm border border-border p-3 cursor-pointer hover:bg-gray-100"
|
|
onClick={() => openInputFromStatsCd(item)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter" || e.key === " ") {
|
|
e.preventDefault();
|
|
openInputFromStatsCd(item);
|
|
}
|
|
}}
|
|
>
|
|
<div className="mb-2 flex items-center justify-between">
|
|
<span className="font-medium">
|
|
{item.statsNm}
|
|
</span>
|
|
<Badge
|
|
color={
|
|
TYPE_CODE_COLOR_MAP[item.vrfcMthdCd] ??
|
|
"gray"
|
|
}
|
|
className="text-sm"
|
|
>
|
|
{TYPE_CODE_NAME_MAP[item.vrfcMthdCd] ??
|
|
item.vrfcMthdNm}
|
|
</Badge>
|
|
</div>
|
|
<p className="text-lg text-muted-foreground">
|
|
{item.rsnExpln}
|
|
</p>
|
|
<div className="flex justify-between text-lg text-muted-foreground">
|
|
<span>{item.admdstNm}</span>
|
|
<span>
|
|
현재: {item.nowVlCn ?? "-"} / 비교:{" "}
|
|
{item.compVlCn ?? "-"}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div className="w-1/5 bg-container p-4 !pb-2 flex border-r border-border">
|
|
<div className="card card-border flex-1 bg-background !px-0">
|
|
<div className="border-b border-border flex justify-between">
|
|
<h2 className="text-[2.4rem] font-semibold text-title p-4 ">이상치 선별 보고서</h2>
|
|
<span
|
|
role="button"
|
|
tabIndex={0}
|
|
aria-label="닫기"
|
|
className="mt-2 mr-4 flex flex-shrink-0 cursor-pointer items-start justify-center text-3xl leading-none hover:text-[#2094fa]"
|
|
onClick={() => onClose?.()}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter" || e.key === " ") {
|
|
e.preventDefault();
|
|
onClose?.();
|
|
}
|
|
}}
|
|
>
|
|
<i className="ri-close-line" />
|
|
</span>
|
|
</div>
|
|
<div className="card-body max-h-[calc(100%-3.25rem)] p-2.5">
|
|
{isLoading && (
|
|
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
|
|
<div className="relative size-[5rem]">
|
|
<LoadingWithAnimation color="primary" />
|
|
</div>
|
|
</div>
|
|
)}
|
|
{error && (
|
|
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
|
|
데이터를 불러올 수 없습니다.
|
|
</div>
|
|
)}
|
|
{!isLoading && !error && (
|
|
<section className="flex min-h-0 flex-1 flex-col h-[85rem]">
|
|
<Tabs
|
|
value={activeTab}
|
|
onValueChange={(value) =>
|
|
setActiveTab(value as VerificationReportTabKey)
|
|
}
|
|
className="flex min-h-0 flex-1 flex-col h-full"
|
|
variant="default"
|
|
>
|
|
<TabsList className="grid w-full grid-cols-3 p-1">
|
|
{VERIFICATION_REPORT_TABS.map((tab) => (
|
|
<TabsTrigger
|
|
key={tab.value}
|
|
value={tab.value}
|
|
className="!px-2 !py-2 text-[1.3rem] whitespace-normal"
|
|
>
|
|
{tab.label} ({listsByTab[tab.value].length})
|
|
</TabsTrigger>
|
|
))}
|
|
</TabsList>
|
|
{VERIFICATION_REPORT_TABS.map((tab) => (
|
|
<TabsContent
|
|
key={tab.value}
|
|
value={tab.value}
|
|
className="mt-0 flex min-h-0 flex-1 flex-col p-0 data-[state=inactive]:hidden"
|
|
>
|
|
{activeTab === tab.value
|
|
? renderResultList(
|
|
tab.value,
|
|
listsByTab[tab.value],
|
|
)
|
|
: null}
|
|
</TabsContent>
|
|
))}
|
|
</Tabs>
|
|
</section>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default VerificationSelectionReport;
|