750 lines
33 KiB
TypeScript
750 lines
33 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useMemo, useState } from "react";
|
|
|
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import {
|
|
getCoreRowModel,
|
|
getPaginationRowModel,
|
|
useReactTable,
|
|
} from "@tanstack/react-table";
|
|
import type { AxiosError } from "axios";
|
|
import dayjs from "dayjs";
|
|
|
|
import { ENDPOINT } from "@/shared/config";
|
|
import { useAlertStore } from "@/shared/lib";
|
|
import type { ApiErrorResponse } from "@/shared/model";
|
|
import { CodeSelect } from "@/entities/code";
|
|
import { analysisApi } from "@/entities/statistics/api/analysis-api";
|
|
import { analysisHistoryApi } from "@/entities/statistics/api/analysis-history-api";
|
|
import { verificationTargetManagementApi } from "@/entities/statistics/api/verification-target-management-service";
|
|
import type {
|
|
StAnlsMstResponse,
|
|
StAnlsMstSearchParams,
|
|
StAnlsOutlierListResponse,
|
|
} from "@/entities/statistics/model/analysis-history-interface";
|
|
import type {
|
|
AnalysisRequestPayload,
|
|
AnalysisResponseDto,
|
|
} from "@/entities/statistics/model/analysis-interface";
|
|
import { verificationHistoryColumns } from "@/entities/statistics/model/verification-history-columns";
|
|
import type { VerificationTargetCodeResponse } from "@/entities/statistics/model/verification-target-management-interface";
|
|
import {
|
|
Button,
|
|
Checkbox,
|
|
DataTable,
|
|
DateRangePicker,
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
Input,
|
|
Label,
|
|
LoadingWithAnimation,
|
|
RadioGroup,
|
|
RadioGroupItem,
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@common/components";
|
|
|
|
import HbVerificationDetail from "./detail/HbVerificationDetail";
|
|
|
|
type DateRangeValue = {
|
|
from: Date;
|
|
to?: Date;
|
|
};
|
|
|
|
type VerifyType = "ALL" | "SELECT";
|
|
|
|
interface VerificationFormState {
|
|
mu: string;
|
|
percentile: string;
|
|
a: string;
|
|
c: string;
|
|
verificationYear: string;
|
|
}
|
|
|
|
interface OutlierDialogState {
|
|
open: boolean;
|
|
anlsSn: number | null;
|
|
}
|
|
|
|
function getDefaultHistoryDateRange(): DateRangeValue {
|
|
const to = dayjs().endOf("day").toDate();
|
|
const from = dayjs().subtract(7, "day").startOf("day").toDate();
|
|
return { from, to };
|
|
}
|
|
|
|
function getVerificationYearOptions(): string[] {
|
|
const currentYear = dayjs().year();
|
|
const startYear = 2017;
|
|
const endYear = currentYear - 1;
|
|
if (endYear < startYear) return [];
|
|
return Array.from({ length: endYear - startYear + 1 }, (_, index) =>
|
|
String(startYear + index),
|
|
);
|
|
}
|
|
|
|
function toNumber(value: string): number {
|
|
const parsed = Number(value);
|
|
return Number.isNaN(parsed) ? 0 : parsed;
|
|
}
|
|
|
|
const HbStatisticsVerification = () => {
|
|
const { setMessage: alert } = useAlertStore((state) => state);
|
|
const queryClient = useQueryClient();
|
|
|
|
const yearOptions = useMemo(() => getVerificationYearOptions(), []);
|
|
const defaultYear: string =
|
|
yearOptions.length > 0
|
|
? (yearOptions[yearOptions.length - 1] ?? "")
|
|
: "";
|
|
|
|
const [verifyType, setVerifyType] = useState<VerifyType>("ALL");
|
|
const [formState, setFormState] = useState<VerificationFormState>({
|
|
mu: "0",
|
|
percentile: "0",
|
|
a: "0",
|
|
c: "0",
|
|
verificationYear: defaultYear,
|
|
});
|
|
const [selectedTargets, setSelectedTargets] = useState<string[]>([]);
|
|
const [outlierDialog, setOutlierDialog] = useState<OutlierDialogState>({
|
|
open: false,
|
|
anlsSn: null,
|
|
});
|
|
const [selectedStatsCd, setSelectedStatsCd] = useState<string>("");
|
|
const [selectedAnlsSn, setSelectedAnlsSn] = useState<number | null>(null);
|
|
|
|
const defaultHistoryDateRange = useMemo(
|
|
() => getDefaultHistoryDateRange(),
|
|
[],
|
|
);
|
|
const [historyDateRange, setHistoryDateRange] = useState<
|
|
DateRangeValue | undefined
|
|
>(defaultHistoryDateRange);
|
|
const [appliedHistoryDateRange, setAppliedHistoryDateRange] = useState<
|
|
DateRangeValue | undefined
|
|
>(defaultHistoryDateRange);
|
|
|
|
const [historyPagination, setHistoryPagination] = useState({
|
|
pageIndex: 0,
|
|
pageSize: 10,
|
|
});
|
|
|
|
const { data: verificationTargetCodes } = useQuery<
|
|
VerificationTargetCodeResponse[]
|
|
>({
|
|
queryKey: [ENDPOINT.STATVERIFY_SERVICE.VRFCTGT_MNG, "codes"],
|
|
queryFn: () =>
|
|
verificationTargetManagementApi.getVerificationTargetCodes(),
|
|
});
|
|
|
|
const filteredVerificationTargetCodes = useMemo(() => {
|
|
return verificationTargetCodes?.filter((item) =>
|
|
selectedStatsCd
|
|
? item.vrfcTrgtSn.startsWith(selectedStatsCd)
|
|
: true,
|
|
);
|
|
}, [verificationTargetCodes, selectedStatsCd]);
|
|
|
|
const allVerificationTargetIds = useMemo(
|
|
() =>
|
|
(filteredVerificationTargetCodes ?? []).map(
|
|
(item) => item.vrfcTrgtSn,
|
|
),
|
|
[filteredVerificationTargetCodes],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!allVerificationTargetIds.length) return;
|
|
setSelectedTargets(allVerificationTargetIds);
|
|
}, [allVerificationTargetIds]);
|
|
|
|
const { data: analysisHistory } = useQuery({
|
|
queryKey: [
|
|
ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST,
|
|
historyPagination,
|
|
appliedHistoryDateRange,
|
|
],
|
|
queryFn: () => {
|
|
const params: StAnlsMstSearchParams = {
|
|
page: historyPagination.pageIndex,
|
|
size: historyPagination.pageSize,
|
|
};
|
|
|
|
if (appliedHistoryDateRange?.from) {
|
|
params.verifyDtStart = dayjs(
|
|
appliedHistoryDateRange.from,
|
|
).format("YYYY-MM-DD");
|
|
}
|
|
|
|
if (appliedHistoryDateRange?.to || appliedHistoryDateRange?.from) {
|
|
const to =
|
|
appliedHistoryDateRange.to ?? appliedHistoryDateRange.from!;
|
|
params.verifyDtEnd = dayjs(to).format("YYYY-MM-DD");
|
|
}
|
|
|
|
return analysisHistoryApi.getAnalysisHistoryList(params);
|
|
},
|
|
refetchInterval: (query) => {
|
|
const data = query.state.data;
|
|
if (!data?.content?.length) return false;
|
|
const hasProcessing = (data.content as StAnlsMstResponse[]).some(
|
|
(item) => item.anlsSttsCd === "PROCESSING",
|
|
);
|
|
return hasProcessing ? 3000 : false;
|
|
},
|
|
});
|
|
|
|
const historyRowData: StAnlsMstResponse[] = (analysisHistory?.content ??
|
|
[]) as StAnlsMstResponse[];
|
|
|
|
const table = useReactTable({
|
|
data: historyRowData,
|
|
columns: verificationHistoryColumns,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
manualPagination: true,
|
|
pageCount: analysisHistory?.totalPages ?? 0,
|
|
state: {
|
|
pagination: historyPagination,
|
|
},
|
|
onPaginationChange: (updater) => {
|
|
setHistoryPagination((prev) =>
|
|
typeof updater === "function" ? updater(prev) : updater,
|
|
);
|
|
},
|
|
meta: {
|
|
onOutlierCellClick: (row: StAnlsMstResponse) => {
|
|
setOutlierDialog({ open: true, anlsSn: row.anlsSn });
|
|
},
|
|
},
|
|
});
|
|
|
|
const { data: outlierList, isLoading: isLoadingOutlier } = useQuery({
|
|
queryKey: [
|
|
ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST,
|
|
"outlier",
|
|
outlierDialog.anlsSn,
|
|
],
|
|
queryFn: async () => {
|
|
if (!outlierDialog.anlsSn) return [];
|
|
const response = await analysisHistoryApi.getOutlierList(
|
|
outlierDialog.anlsSn,
|
|
);
|
|
return response;
|
|
},
|
|
enabled: outlierDialog.open && !!outlierDialog.anlsSn,
|
|
});
|
|
|
|
const analysisMutation = useMutation<
|
|
AnalysisResponseDto,
|
|
AxiosError<ApiErrorResponse>,
|
|
AnalysisRequestPayload
|
|
>({
|
|
mutationFn: async (payload) => analysisApi.analyze(payload),
|
|
onSuccess: () => {
|
|
alert("검증을 요청했습니다.");
|
|
queryClient.invalidateQueries({
|
|
queryKey: [ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST],
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
alert(
|
|
error.response?.data?.message ??
|
|
"검증 실행 중 오류가 발생했습니다.",
|
|
);
|
|
},
|
|
});
|
|
|
|
function handleFormChange<Key extends keyof VerificationFormState>(
|
|
key: Key,
|
|
value: string,
|
|
) {
|
|
setFormState((prev) => ({
|
|
...prev,
|
|
[key]: value,
|
|
}));
|
|
}
|
|
|
|
function handleTargetToggle(statsCd: string) {
|
|
setSelectedTargets((prev) =>
|
|
prev.includes(statsCd)
|
|
? prev.filter((cd) => cd !== statsCd)
|
|
: [...prev, statsCd],
|
|
);
|
|
}
|
|
|
|
async function handleSubmit() {
|
|
if (!formState.verificationYear) {
|
|
alert("검증년도를 선택해 주세요.");
|
|
return;
|
|
}
|
|
|
|
if (verifyType === "SELECT" && selectedTargets.length === 0) {
|
|
alert("검증 대상을 1개 이상 선택해 주세요.");
|
|
return;
|
|
}
|
|
|
|
const yearNew = toNumber(formState.verificationYear);
|
|
const payload: AnalysisRequestPayload = {
|
|
dataset:
|
|
verifyType === "ALL"
|
|
? allVerificationTargetIds.length > 0
|
|
? allVerificationTargetIds.join(",")
|
|
: undefined
|
|
: selectedTargets.length > 0
|
|
? selectedTargets.join(",")
|
|
: undefined,
|
|
yearNew,
|
|
yearOld: yearNew - 1,
|
|
mu: toNumber(formState.mu),
|
|
percentile: toNumber(formState.percentile),
|
|
a: toNumber(formState.a),
|
|
c: toNumber(formState.c),
|
|
vrfcMthdCd: "HB",
|
|
allVerifyYn: verifyType === "ALL" ? "Y" : "N",
|
|
};
|
|
|
|
analysisMutation.mutate(payload);
|
|
}
|
|
|
|
function handleReset() {
|
|
setFormState({
|
|
mu: "0",
|
|
percentile: "0",
|
|
a: "0",
|
|
c: "0",
|
|
verificationYear: defaultYear,
|
|
});
|
|
setSelectedTargets([]);
|
|
}
|
|
|
|
function handleHistoryRowClick(row: StAnlsMstResponse) {
|
|
const anlsSn = Number(row.anlsSn ?? 0);
|
|
if (!anlsSn) return;
|
|
setSelectedAnlsSn(anlsSn);
|
|
}
|
|
|
|
function applyHistoryDateSearch(nextRange: DateRangeValue | undefined) {
|
|
setAppliedHistoryDateRange(nextRange);
|
|
setHistoryPagination((prev) => ({
|
|
...prev,
|
|
pageIndex: 0,
|
|
}));
|
|
}
|
|
|
|
function handleHistoryDateSearch() {
|
|
applyHistoryDateSearch(historyDateRange);
|
|
}
|
|
|
|
function handleHistoryDateReset() {
|
|
const resetRange = getDefaultHistoryDateRange();
|
|
setHistoryDateRange(resetRange);
|
|
applyHistoryDateSearch(resetRange);
|
|
}
|
|
|
|
function closeOutlierDialog() {
|
|
setOutlierDialog({ open: false, anlsSn: null });
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{selectedAnlsSn ? (
|
|
<HbVerificationDetail
|
|
anlsSn={selectedAnlsSn}
|
|
onBack={() => setSelectedAnlsSn(null)}
|
|
/>
|
|
) : (
|
|
<>
|
|
<div className="card card-border flex flex-col gap-6">
|
|
{/* 검증 종류 선택 */}
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-6">
|
|
<div className="flex items-center gap-4 pl-2">
|
|
<Label className="text-[1.3rem] font-medium text-label">
|
|
검증 년도
|
|
</Label>
|
|
<Select
|
|
value={formState.verificationYear}
|
|
onValueChange={(value) =>
|
|
handleFormChange(
|
|
"verificationYear",
|
|
value,
|
|
)
|
|
}
|
|
>
|
|
<SelectTrigger className="w-[10rem] flex-1">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{yearOptions.map((year) => (
|
|
<SelectItem
|
|
key={year}
|
|
value={year}
|
|
>
|
|
{year}년
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="flex items-center gap-4 pl-2">
|
|
<Label className="text-[1.3rem] font-medium text-label">
|
|
검증 방식
|
|
</Label>
|
|
<RadioGroup
|
|
value={verifyType}
|
|
onValueChange={(value) =>
|
|
setVerifyType(value as VerifyType)
|
|
}
|
|
className="flex items-center gap-4"
|
|
>
|
|
<label className="flex cursor-pointer items-center gap-2 text-label">
|
|
<RadioGroupItem value="ALL" />
|
|
<span>일괄 검증</span>
|
|
</label>
|
|
<label className="flex cursor-pointer items-center gap-2 text-label">
|
|
<RadioGroupItem value="SELECT" />
|
|
<span>선택 검증</span>
|
|
</label>
|
|
</RadioGroup>
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button
|
|
color="green"
|
|
className="h-[3.6rem] w-[120px]"
|
|
onClick={handleSubmit}
|
|
disabled={analysisMutation.isPending}
|
|
>
|
|
검증 실행
|
|
</Button>
|
|
<Button
|
|
color="gray"
|
|
className="h-[3.6rem] w-[120px]"
|
|
type="button"
|
|
onClick={handleReset}
|
|
>
|
|
초기화
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
{/* //검증 종류 선택 */}
|
|
|
|
{verifyType === "SELECT" && (
|
|
<div className="flex gap-4">
|
|
{/* 검증 대상 */}
|
|
<div className="flex-1">
|
|
<h4 className="mb-3 rounded bg-gray-100 px-4 py-2 text-[1.4rem] font-medium">
|
|
검증 대상
|
|
</h4>
|
|
<div className="mb-3 flex items-center gap-4 px-4">
|
|
<Label className="text-[1.3rem] font-medium text-label">
|
|
통계항목
|
|
</Label>
|
|
<CodeSelect
|
|
upCd="STITEM"
|
|
onValueChange={setSelectedStatsCd}
|
|
className="w-[24rem]"
|
|
/>
|
|
<Button
|
|
type="button"
|
|
color="gray"
|
|
className="h-[3.2rem] px-4 text-[1.3rem]"
|
|
onClick={() =>
|
|
setSelectedStatsCd("")
|
|
}
|
|
>
|
|
초기화
|
|
</Button>
|
|
</div>
|
|
<div className="max-h-[16rem] space-y-4 overflow-auto rounded-md border p-4">
|
|
{verifyType === "SELECT" &&
|
|
filteredVerificationTargetCodes &&
|
|
filteredVerificationTargetCodes.length >
|
|
0 ? (
|
|
<div className="grid grid-cols-3 gap-4">
|
|
{filteredVerificationTargetCodes.map(
|
|
(item) => (
|
|
<div
|
|
key={
|
|
item.vrfcTrgtSn
|
|
}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<Checkbox
|
|
id={
|
|
item.vrfcTrgtSn
|
|
}
|
|
checked={selectedTargets.includes(
|
|
item.vrfcTrgtSn,
|
|
)}
|
|
onCheckedChange={() =>
|
|
handleTargetToggle(
|
|
item.vrfcTrgtSn,
|
|
)
|
|
}
|
|
/>
|
|
<Label
|
|
htmlFor={
|
|
item.vrfcTrgtSn
|
|
}
|
|
className="cursor-pointer text-label"
|
|
>
|
|
{
|
|
item.vrfcTrgtNm
|
|
}
|
|
</Label>
|
|
</div>
|
|
),
|
|
)}
|
|
</div>
|
|
) : (
|
|
<div className="flex items-center justify-center py-4">
|
|
<p className="text-[1.3rem] text-muted-foreground">
|
|
검증 대상이 없습니다.
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
{/* //검증 대상 */}
|
|
|
|
{/* 검증 변수 */}
|
|
<div className="flex-[0.5]">
|
|
<h4 className="mb-3 rounded bg-gray-100 px-4 py-2 text-[1.4rem] font-medium">
|
|
검증 변수
|
|
</h4>
|
|
|
|
<div className="space-y-3">
|
|
<div className="flex items-center gap-4">
|
|
<Label className="w-32 text-label">
|
|
μ
|
|
</Label>
|
|
<Input
|
|
type="number"
|
|
value={formState.mu}
|
|
onChange={(event) =>
|
|
handleFormChange(
|
|
"mu",
|
|
event.target.value,
|
|
)
|
|
}
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4">
|
|
<Label className="w-32 text-label">
|
|
Percentile
|
|
</Label>
|
|
<Input
|
|
type="number"
|
|
value={formState.percentile}
|
|
onChange={(event) =>
|
|
handleFormChange(
|
|
"percentile",
|
|
event.target.value,
|
|
)
|
|
}
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4">
|
|
<Label className="w-32 text-label">
|
|
A
|
|
</Label>
|
|
<Input
|
|
type="number"
|
|
value={formState.a}
|
|
onChange={(event) =>
|
|
handleFormChange(
|
|
"a",
|
|
event.target.value,
|
|
)
|
|
}
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4">
|
|
<Label className="w-32 text-label">
|
|
C
|
|
</Label>
|
|
<Input
|
|
type="number"
|
|
value={formState.c}
|
|
onChange={(event) =>
|
|
handleFormChange(
|
|
"c",
|
|
event.target.value,
|
|
)
|
|
}
|
|
className="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/* //검증 변수 */}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="mt-10 px-2 pb-8">
|
|
<h3 className="mb-4 text-[1.6rem] font-semibold text-title">
|
|
검증 이력
|
|
</h3>
|
|
<div className="card card-border !m-0 !mb-2 flex justify-between">
|
|
<div className="flex items-center gap-4 px-2">
|
|
<span className="text-[1.3rem] font-medium text-label">
|
|
검증일시
|
|
</span>
|
|
<DateRangePicker
|
|
value={historyDateRange}
|
|
defaultValue={defaultHistoryDateRange}
|
|
onValueChange={(value) =>
|
|
setHistoryDateRange(
|
|
value?.from
|
|
? (value as DateRangeValue)
|
|
: undefined,
|
|
)
|
|
}
|
|
className="max-w-[26rem]"
|
|
/>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button
|
|
type="button"
|
|
className="h-[3.2rem] px-4 text-[1.3rem]"
|
|
onClick={handleHistoryDateSearch}
|
|
>
|
|
조회
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
color="gray"
|
|
className="h-[3.2rem] px-4 text-[1.3rem]"
|
|
onClick={handleHistoryDateReset}
|
|
>
|
|
초기화
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<DataTable
|
|
table={table}
|
|
variant="secondary"
|
|
onRowClick={handleHistoryRowClick}
|
|
viewPagination={true}
|
|
className="[&_td]:!p-0"
|
|
/>
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
<Dialog open={outlierDialog.open} onOpenChange={closeOutlierDialog}>
|
|
<DialogContent className="max-w-[800px]">
|
|
<DialogHeader>
|
|
<DialogTitle>이상건수 상세</DialogTitle>
|
|
</DialogHeader>
|
|
{isLoadingOutlier && (
|
|
<div className="flex w-full items-center justify-center py-40">
|
|
<div className="relative size-[5rem]">
|
|
<LoadingWithAnimation color="primary" />
|
|
</div>
|
|
</div>
|
|
)}
|
|
{!isLoadingOutlier && (outlierList?.length ?? 0) === 0 && (
|
|
<p className="px-2 py-4 text-[1.3rem] text-muted-foreground">
|
|
표시할 이상치 데이터가 없습니다.
|
|
</p>
|
|
)}
|
|
{!isLoadingOutlier && (outlierList?.length ?? 0) > 0 && (
|
|
<div className="max-h-[40rem] space-y-4 overflow-auto px-1 py-2 text-[1.25rem]">
|
|
{outlierList?.map(
|
|
(item: StAnlsOutlierListResponse) => (
|
|
<Table
|
|
key={`${item.anlsSn}-${item.vrfcTrgtSn}`}
|
|
variant="secondary"
|
|
className="table-fixed"
|
|
>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead
|
|
colSpan={5}
|
|
className="bg-muted text-left text-[1.3rem] font-semibold"
|
|
>
|
|
{item.vrfcTrgtNm}
|
|
</TableHead>
|
|
</TableRow>
|
|
<TableRow>
|
|
<TableHead className="bg-muted font-semibold">
|
|
시도
|
|
</TableHead>
|
|
<TableHead className="bg-muted font-semibold">
|
|
시군구
|
|
</TableHead>
|
|
<TableHead className="bg-muted font-semibold">
|
|
행정구역
|
|
</TableHead>
|
|
<TableHead className="bg-muted font-semibold">
|
|
시설명
|
|
</TableHead>
|
|
<TableHead className="bg-muted text-center font-semibold">
|
|
입력값
|
|
</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{item.outlierDataList.map(
|
|
(data) => (
|
|
<TableRow key={data.rsltSn}>
|
|
<TableCell className="text-center">
|
|
{data.sido ?? "-"}
|
|
</TableCell>
|
|
<TableCell className="text-center">
|
|
{data.gugun ?? "-"}
|
|
</TableCell>
|
|
<TableCell className="text-center">
|
|
{data.adstDong ??
|
|
"-"}
|
|
</TableCell>
|
|
<TableCell className="text-center">
|
|
{/* 시설명: DTO에 해당 필드 없음 */}
|
|
-
|
|
</TableCell>
|
|
<TableCell className="text-right">
|
|
{data.cryrVl != null
|
|
? String(
|
|
data.cryrVl,
|
|
)
|
|
: "-"}
|
|
</TableCell>
|
|
</TableRow>
|
|
),
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
),
|
|
)}
|
|
</div>
|
|
)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default HbStatisticsVerification;
|