Files
frontend/apps/sn/app/html/(map-layout)/aged-pipe/AgedPipeListModal.tsx
T

178 lines
6.0 KiB
TypeScript

"use client";
import { useRef } from "react";
import { Button, Input } from "@common/components";
import { UiTable } from "@common/business_components/ui";
import LoadingOverlay from "@/components/ui/LoadingBar";
import useStore from "@/store/useStore";
import {
AGED_PIPE_LIST_HEADLIST,
type AgedPipeFilters,
type AgedPipeRow,
} from "./agedPipeUtils";
type Props = {
open: boolean;
onOpenChange: (open: boolean) => void;
filters: AgedPipeFilters;
onFiltersChange: (filters: AgedPipeFilters) => void;
rows: AgedPipeRow[];
total: number;
currentPage: number;
isLoading: boolean;
onSearch: (page?: number) => void;
onDownload: () => void;
onRowDoubleClick: (row: AgedPipeRow) => void;
};
export default function AgedPipeListModal({
open,
onOpenChange,
filters,
onFiltersChange,
rows,
total,
currentPage,
isLoading,
onSearch,
onDownload,
onRowDoubleClick,
}: Props) {
const { ctpv, sgg } = useStore();
const lastClickRef = useRef<{ index: number; clickedAt: number } | null>(null);
const sggOptions = filters.ctpvCd === "0"
? []
: sgg.filter((item) => String(item.val).startsWith(filters.ctpvCd.slice(0, 2)));
const handleRowClick = (index?: number) => {
if (typeof index !== "number") return;
const row = rows[index];
if (!row) return;
const now = Date.now();
const lastClick = lastClickRef.current;
if (lastClick && lastClick.index === index && now - lastClick.clickedAt < 300) {
lastClickRef.current = null;
onRowDoubleClick(row);
return;
}
lastClickRef.current = { index, clickedAt: now };
};
if (!open) return null;
return (
<div className="pointer-events-none absolute inset-0 z-[70] flex items-center justify-center px-6 py-8">
<div className="pointer-events-auto relative flex max-h-[82vh] w-[min(1120px,94vw)] flex-col overflow-hidden border border-[#dbe2e6] bg-white shadow-[0_0_12px_rgba(0,0,0,0.25)]">
<LoadingOverlay isLoading={isLoading} />
<div className="flex items-center justify-between border-b border-[#dbe2e6] bg-primary px-5 py-3">
<div>
<div className="text-[1.45rem] font-semibold text-white">노후관로 목록</div>
</div>
<Button
type="button"
color="white"
size="xs"
className="border border-white/30 px-3 py-1 text-[1.05rem] text-white"
onClick={() => onOpenChange(false)}
>
닫기
</Button>
</div>
<div className="relative overflow-y-auto p-4">
<div className="grid grid-cols-1 gap-3 bg-slate-50 pb-4 md:grid-cols-[110px_minmax(0,1fr)_110px_minmax(0,1fr)_auto_auto]">
<label className="block">
<div className="mb-1 text-[1.05rem] font-medium text-slate-700">경과년수</div>
<Input
className="h-10 !text-[1.1rem]"
inputMode="numeric"
value={String(filters.elapsedYears)}
onChange={(event) => {
const next = Number(event.target.value.replace(/[^0-9]/g, "") || "0");
onFiltersChange({ ...filters, elapsedYears: next });
}}
onKeyDown={(event) => {
if (event.key === "Enter") onSearch(1);
}}
/>
</label>
<label className="block">
<div className="mb-1 text-[1.05rem] font-medium text-slate-700">시도</div>
<select
className="h-10 w-full rounded-md border border-slate-300 px-3 text-[1.1rem]"
value={filters.ctpvCd}
onChange={(event) =>
onFiltersChange({
...filters,
ctpvCd: event.target.value,
sggCd: "0",
})
}
>
<option value="0">전체</option>
{ctpv.map((item) => (
<option key={item.val} value={String(item.val)}>
{item.text}
</option>
))}
</select>
</label>
<label className="block">
<div className="mb-1 text-[1.05rem] font-medium text-slate-700">시군구</div>
<select
className="h-10 w-full rounded-md border border-slate-300 px-3 text-[1.1rem]"
value={filters.sggCd}
onChange={(event) => onFiltersChange({ ...filters, sggCd: event.target.value })}
>
<option value="0">전체</option>
{sggOptions.map((item) => (
<option key={item.val} value={String(item.val)}>
{item.text}
</option>
))}
</select>
</label>
<div className="flex flex-wrap gap-2 md:col-span-3 md:justify-end">
<Button type="button" size="sm" color="primary" className="h-10 px-5 !text-[1.1rem]" onClick={() => onSearch(1)}>
조회
</Button>
<Button
type="button"
size="sm"
color="green"
className="h-10 px-5 !text-[1.1rem]"
onClick={onDownload}
>
다운로드
</Button>
</div>
</div>
<div className="border-t border-slate-200 pt-3">
<UiTable<AgedPipeRow>
headName={<>노후관로 조회 결과</>}
tableData={rows}
headlist={AGED_PIPE_LIST_HEADLIST}
pageSize={10}
total={total}
cellClick={(index) => handleRowClick(index)}
onPageChange={(page) => onSearch(page)}
curPage={currentPage}
widthFixed={true}
/>
</div>
<div className="px-[1.2rem] pb-1 text-[1rem] text-slate-500">
목록 행을 더블클릭하면 지도 이동 후 상세 패널이 열립니다.
</div>
</div>
</div>
</div>
);
}