From f1fcfa9ee49738c796e4667b8a877779a1b3f2cd Mon Sep 17 00:00:00 2001 From: rsone001 Date: Fri, 3 Jul 2026 16:19:27 +0900 Subject: [PATCH] =?UTF-8?q?[st]=20-=20=EA=B7=B8=EB=A6=AC=EB=93=9C=20style?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= =?UTF-8?q?,=20=EB=A1=9C=EA=B7=B8=20=EC=A1=B0=ED=9A=8C=20=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20=EC=84=A0=ED=83=9D=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(=EC=B5=9C=EB=8C=80=201=EB=85=84=20=EC=A0=9C=ED=95=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/st/src/components/module/DataGrid.tsx | 25 ++- .../input/StatisticsReviewHistory.tsx | 4 - .../statistics/management/LogHistory.tsx | 162 +++++++++++++++--- .../statistics/model/statistics-interface.ts | 2 - 4 files changed, 164 insertions(+), 29 deletions(-) diff --git a/apps/st/src/components/module/DataGrid.tsx b/apps/st/src/components/module/DataGrid.tsx index 7403df3b8..d99127482 100644 --- a/apps/st/src/components/module/DataGrid.tsx +++ b/apps/st/src/components/module/DataGrid.tsx @@ -61,6 +61,25 @@ type GridColumnWithStyleCallback = { ) => CellStyleCallbackResult; }; +function mergeGridRowsWithSourceMetadata( + rows: Record[], + sourceRows: Record[], + columnFieldNames: string[], +): Record[] { + const columnFieldSet = new Set(columnFieldNames); + return rows.map((row, index) => { + const source = sourceRows[index]; + if (!source) return row; + + const merged: Record = { ...row }; + for (const [key, value] of Object.entries(source)) { + if (columnFieldSet.has(key) || value === undefined) continue; + merged[key] = value as string | number; + } + return merged; + }); +} + function applyCellStyleCallbacksToColumns( gridView: unknown, columns: ColumnConfig[], @@ -1620,7 +1639,11 @@ useEffect(() => { r[f] = ds.getValue(i, f) as string | number; rows.push(r); } - return rows; + return mergeGridRowsWithSourceMetadata( + rows, + rowDataRef.current, + fieldNames, + ); }; } diff --git a/apps/st/src/components/statistics/input/StatisticsReviewHistory.tsx b/apps/st/src/components/statistics/input/StatisticsReviewHistory.tsx index 669ae0c76..ddd8bc8dc 100644 --- a/apps/st/src/components/statistics/input/StatisticsReviewHistory.tsx +++ b/apps/st/src/components/statistics/input/StatisticsReviewHistory.tsx @@ -18,8 +18,6 @@ const StatisticsReviewHistory = () => { { name: "statsYr", fieldName: "statsYr", header: { text: "연도" }, editable: false }, { name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false }, { name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false }, - { name: "sewerBzmnCd", fieldName: "sewerBzmnCd", header: { text: "하수사업자코드" }, editable: false }, - { name: "statsCd", fieldName: "statsCd", header: { text: "통계코드" }, editable: false }, { name: "menuNm", fieldName: "menuNm", header: { text: "통계항목" }, editable: false }, { name: "chckerOpinionSm", fieldName: "chckerOpinionSm", header: { text: "검토자의견" }, editable: false }, { name: "wrterOpinionSm", fieldName: "wrterOpinionSm", header: { text: "처리내역" }, editable: false }, @@ -29,8 +27,6 @@ const StatisticsReviewHistory = () => { "statsYr", "ctpvNm", "sggNm", - "sewerBzmnCd", - "statsCd", "menuNm", "chckerOpinionSm", "wrterOpinionSm", diff --git a/apps/st/src/components/statistics/management/LogHistory.tsx b/apps/st/src/components/statistics/management/LogHistory.tsx index 365d61b72..184a17dce 100644 --- a/apps/st/src/components/statistics/management/LogHistory.tsx +++ b/apps/st/src/components/statistics/management/LogHistory.tsx @@ -3,6 +3,7 @@ import { useCallback, useEffect, useState } from "react"; import dayjs from "dayjs"; +import { CalendarIcon } from "lucide-react"; import useSWR from "swr"; import { useAlertStore } from "@/shared/lib"; @@ -17,9 +18,13 @@ import type { } from "@/entities/statistics/model/statistics-interface"; import { Button, - InputDatePicker, + Calendar, + Input, Label, LoadingWithAnimation, + Popover, + PopoverContent, + PopoverTrigger, Select, SelectContent, SelectItem, @@ -34,6 +39,91 @@ import { LogHistoryColumn } from "@/components/util/column/management/LogHistory import { toGridRows } from "@/components/util/grid/gridFunction"; const PAGE_SIZE = 100; +const MAX_DATE_RANGE_YEARS = 1; + +const isWithinMaxDateRange = (start: Date, end: Date) => { + const startDay = dayjs(start).startOf("day"); + const endDay = dayjs(end).startOf("day"); + return ( + !endDay.isBefore(startDay, "day") && + !endDay.isAfter(startDay.add(MAX_DATE_RANGE_YEARS, "year"), "day") + ); +}; + +type LogHistoryDateFieldProps = { + value: Date; + onChange: (date: Date) => void; + className?: string; +}; + +/** 로그 이력 전용: 날짜를 비울 수 없는 date input */ +const LogHistoryDateField = ({ + value, + onChange, + className, +}: LogHistoryDateFieldProps) => { + const [open, setOpen] = useState(false); + const [month, setMonth] = useState(value); + const dateValue = dayjs(value).format("YYYY-MM-DD"); + + useEffect(() => { + setMonth(value); + }, [value]); + + return ( +
+ { + if (!e.target.value) return; + const next = dayjs(e.target.value); + if (!next.isValid()) return; + onChange(next.toDate()); + }} + onKeyDown={(e) => { + if (e.key === "ArrowDown") { + e.preventDefault(); + setOpen(true); + } + }} + /> + + + + + + { + if (!newDate) return; + onChange(newDate); + setOpen(false); + }} + /> + + +
+ ); +}; const getSidoListFetcher = async (): Promise> => { return statisticsApi.getSidoList(); @@ -63,13 +153,45 @@ const LogHistory = () => { string, string | number > | null>(null); - const [startDate, setStartDate] = useState(() => + const [startDate, setStartDate] = useState(() => dayjs().subtract(1, "month").startOf("day").toDate(), ); - const [endDate, setEndDate] = useState(() => + const [endDate, setEndDate] = useState(() => dayjs().endOf("day").toDate(), ); + const handleStartDateChange = (date: Date) => { + const next = dayjs(date).startOf("day"); + const end = dayjs(endDate).startOf("day"); + + if (next.isAfter(end, "day")) { + alert("시작일은 종료일보다 늦을 수 없습니다."); + return; + } + if (next.isBefore(end.subtract(MAX_DATE_RANGE_YEARS, "year"), "day")) { + alert("조회 기간은 최대 1년까지 설정할 수 있습니다."); + return; + } + + setStartDate(next.toDate()); + }; + + const handleEndDateChange = (date: Date) => { + const next = dayjs(date).startOf("day"); + const start = dayjs(startDate).startOf("day"); + + if (next.isBefore(start, "day")) { + alert("종료일은 시작일보다 빠를 수 없습니다."); + return; + } + if (next.isAfter(start.add(MAX_DATE_RANGE_YEARS, "year"), "day")) { + alert("조회 기간은 최대 1년까지 설정할 수 있습니다."); + return; + } + + setEndDate(next.toDate()); + }; + const { data: sidoList } = useSWR>( "log-history-sido-list", getSidoListFetcher, @@ -141,10 +263,8 @@ const LogHistory = () => { const param: StatisticsLogRequest = { page: page1Based, size: PAGE_SIZE, - startDate: startDate - ? dayjs(startDate).format("YYYY-MM-DD") - : "", - endDate: endDate ? dayjs(endDate).format("YYYY-MM-DD") : "", + startDate: dayjs(startDate).format("YYYY-MM-DD"), + endDate: dayjs(endDate).format("YYYY-MM-DD"), ...(!(statsCd === "" || statsCd === "ALL") && { statsCd }), ...(sido && sido !== "ALL" && { ctpvNm: sido }), ...(gugun && gugun !== "ALL" && { sggNm: gugun }), @@ -199,17 +319,15 @@ const LogHistory = () => { const handleSearch = async (e: React.FormEvent) => { e.preventDefault(); - if (startDate && endDate) { - const start = dayjs(startDate); - const end = dayjs(endDate); - if (start.isAfter(end, "day")) { - alert("시작일은 종료일보다 늦을 수 없습니다."); - return; - } - if (end.isBefore(start, "day")) { - alert("종료일은 시작일보다 빠를 수 없습니다."); - return; - } + const start = dayjs(startDate); + const end = dayjs(endDate); + if (end.isBefore(start, "day")) { + alert("종료일은 시작일보다 빠를 수 없습니다."); + return; + } + if (!isWithinMaxDateRange(startDate, endDate)) { + alert("조회 기간은 최대 1년까지 설정할 수 있습니다."); + return; } await loadLogs(1); @@ -310,15 +428,15 @@ const LogHistory = () => {
- -
diff --git a/apps/st/src/entities/statistics/model/statistics-interface.ts b/apps/st/src/entities/statistics/model/statistics-interface.ts index f748a5c04..571835065 100644 --- a/apps/st/src/entities/statistics/model/statistics-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-interface.ts @@ -463,8 +463,6 @@ export interface StatiscicsManagementReviewHistoryItem { statsYr: string; ctpvNm: string; sggNm: string; - sewerBzmnCd: string; - statsCd: string; menuNm: string; wrterOpinionSm: string | null; chckerOpinionSm: string | null;