[st] - fix: 집계06.공공하수처리시설 조회 조건 추가, 입력06.공공하수처리시설 매핑 계약기간 DatePicker 수정, 입력자 예외 제출 valuedation 수정

This commit is contained in:
2026-07-22 13:38:58 +09:00
parent 533e89c919
commit db7584cb01
6 changed files with 139 additions and 8 deletions
@@ -69,6 +69,7 @@ function buildSearchFormValues(
isDetail: false,
isSummary: d.isSummary || "",
clsfCd: initialClsfCd,
capacityGbn: d.capacityGbn || "ALL",
};
}
@@ -85,6 +86,7 @@ function buildSearchFormValues(
isDetail: false,
isSummary: d?.isSummary || "",
clsfCd: initialClsfCd,
capacityGbn: d?.capacityGbn || "ALL",
};
}
@@ -362,6 +364,26 @@ const SearchForm = (props: SearchFormProps = {}) => {
/>
</div>
)}
{mergedProps.capacityGbn && (
<div className="flex items-center gap-2">
<Label className="text-label">구분</Label>
<Select
value={values.capacityGbn}
onValueChange={(value) =>
setValues({ ...values, capacityGbn: value })
}
>
<SelectTrigger className="w-[10rem]">
<SelectValue placeholder="전체"></SelectValue>
</SelectTrigger>
<SelectContent>
<SelectItem value='ALL'>전체</SelectItem>
<SelectItem value='500톤이상'>500톤이상</SelectItem>
<SelectItem value='500톤미만'>500톤미만</SelectItem>
</SelectContent>
</Select>
</div>
)}
{mergedProps.isDetail && (
<div className="flex items-center gap-2">
<Label className="text-label cursor-pointer" htmlFor="isDetail">
@@ -311,15 +311,24 @@ export function VerificationResultDialog({
}, [rows, loading]);
const handleExceptionSubmitClick = useCallback(async () => {
if (checkedIndices.size === 0) {
alert("작성된 예외 제출 항목이 없습니다.");
return;
}
const gridRows = getGridDataRef.current?.() ?? [];
const payload: VerificationExceptionSubmitItem[] = [];
checkedIndices.forEach((idx) => {
for (const idx of checkedIndices) {
const prcsOpnnExpln = String(gridRows[idx]?.prcsOpnnExpln ?? "").trim();
if (!prcsOpnnExpln) return;
if (!prcsOpnnExpln) {
alert("예외 제출 사유를 작성해주세요.");
return;
}
const vrfcSn = Number(rows[idx]?.vrfcSn);
if (!Number.isFinite(vrfcSn)) return;
if (!Number.isFinite(vrfcSn)) continue;
payload.push({ vrfcSn, prcsOpnnExpln });
});
}
if (payload.length === 0) {
alert("작성된 예외 제출 항목이 없습니다.");
return;
@@ -1,10 +1,13 @@
"use client";
import { useEffect, useState } from "react";
import { CalendarIcon } from "lucide-react";
import dayjs from "dayjs";
import { useAlertStore } from "@/shared/lib";
import {
Button,
Calendar,
Dialog,
DialogClose,
DialogContent,
@@ -12,8 +15,10 @@ import {
DialogHeader,
DialogTitle,
Input,
InputDatePicker,
Label,
Popover,
PopoverContent,
PopoverTrigger,
} from "@common/components";
export interface MainFacilityMappingSaveDialogValues {
@@ -79,6 +84,95 @@ function validateContractRange(start: Date | undefined, end: Date | undefined):
return null;
}
const CONTRACT_DATE_END_MONTH = new Date(new Date().getFullYear() + 10, 11);
function isValidDate(date: Date | undefined) {
if (!date) return false;
return !Number.isNaN(date.getTime());
}
/** 이 다이얼로그 전용 — 연도 드롭다운을 현재+10년까지 허용 */
function ContractDatePicker({
value,
onChange,
disabled,
className,
}: {
value?: Date;
onChange: (date: Date | undefined) => void;
disabled?: boolean;
className?: string;
}) {
const [open, setOpen] = useState(false);
const [month, setMonth] = useState<Date | undefined>(value);
const [displayValue, setDisplayValue] = useState<Date | undefined>(value);
useEffect(() => {
setDisplayValue(value);
setMonth(value);
}, [value]);
return (
<div className="relative flex gap-2">
<Input
type="date"
value={displayValue ? dayjs(displayValue).format("YYYY-MM-DD") : ""}
disabled={disabled}
max={dayjs(CONTRACT_DATE_END_MONTH).format("YYYY-MM-DD")}
className={`bg-background pr-10 ${className ?? ""}`}
onChange={(e) => {
const newDate = new Date(e.target.value);
setDisplayValue(newDate);
if (isValidDate(newDate)) {
onChange(newDate);
setMonth(newDate);
}
}}
onKeyDown={(e) => {
if (e.key === "ArrowDown") {
e.preventDefault();
setOpen(true);
}
}}
/>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
type="button"
variant="ghost"
color="white"
disabled={disabled}
className="absolute right-2 top-1/2 size-7 -translate-y-1/2 rounded-[0.4rem] border-none p-0"
>
<CalendarIcon className="!size-full" />
<span className="sr-only">Select date</span>
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto overflow-hidden p-0"
align="end"
alignOffset={-8}
sideOffset={10}
>
<Calendar
mode="single"
selected={value}
captionLayout="dropdown"
month={month}
onMonthChange={setMonth}
endMonth={CONTRACT_DATE_END_MONTH}
onSelect={(newDate) => {
onChange(newDate);
setDisplayValue(newDate);
setOpen(false);
}}
/>
</PopoverContent>
</Popover>
</div>
);
}
export function MainFacilityMappingSaveDialog({
open,
onOpenChange,
@@ -220,7 +314,7 @@ export function MainFacilityMappingSaveDialog({
<div className="flex flex-col gap-3 sm:flex-row sm:items-end sm:gap-4">
<div className="flex min-w-0 flex-1 flex-col gap-2">
<Label className="text-label text-[1.2rem]">시작일</Label>
<InputDatePicker
<ContractDatePicker
value={prxyCtrtStart}
onChange={setPrxyCtrtStart}
className="w-full"
@@ -229,7 +323,7 @@ export function MainFacilityMappingSaveDialog({
</div>
<div className="flex min-w-0 flex-1 flex-col gap-2">
<Label className="text-label text-[1.2rem]">종료일</Label>
<InputDatePicker
<ContractDatePicker
value={prxyCtrtEnd}
onChange={setPrxyCtrtEnd}
className="w-full"
@@ -21,6 +21,7 @@ const PublicSewageFacilityTot = () => {
setIsLoading(true);
const isSummaryEnabled = values.isSummary !== 'ALL';
const isCapacityGbn = values.capacityGbn !== 'ALL'
const summaryYearCnt = isSummaryEnabled ? Number(values.isSummary) : undefined;
const param = {
@@ -29,6 +30,7 @@ const PublicSewageFacilityTot = () => {
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
...(isCapacityGbn ? { capacityGbn: values.capacityGbn } : {}),
summaryYn: isSummaryEnabled,
...(isSummaryEnabled ? { summaryYearCnt } : {}),
};
@@ -50,7 +52,7 @@ const PublicSewageFacilityTot = () => {
return (
<>
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} sidoSelectText="전체" />
<SearchForm isDetail={true} capacityGbn={true} isSummary={true} onSubmit={handleSearch} sidoSelectText="전체" />
{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]">
@@ -12,6 +12,7 @@ export type SearchFormValues = {
isSummary?: string;
/** 분류: 전체(ALL), 입력(INPT), 검증(VRFC) */
clsfCd?: SearchFormClsfCd;
capacityGbn?: string;
};
export type SearchFormProps = {
@@ -24,6 +25,7 @@ export type SearchFormProps = {
/** 분류(입력/검증) 셀렉트 표시 */
clsf?: boolean;
userMenual?: boolean;
capacityGbn?: boolean;
isDetail?: boolean;
isSummary?: boolean;
onSubmit?: (values: SearchFormValues) => void;
@@ -44,6 +46,7 @@ export const defaultSearchFormProps: Required<
fctName: false,
clsf: false,
userMenual: false,
capacityGbn: false,
isDetail: false,
isSummary: false,
isReset: true,
@@ -6,6 +6,7 @@ export interface SewagePipeMaintainTotalRequest {
rowDataYn?: boolean;
summaryYn?: boolean;
summaryYearCnt?: number;
capacityGbn?: string;
}
export interface SewagePipeMaintainRepairTotalItem {