diff --git a/apps/st/package.json b/apps/st/package.json index 5cf4bf67d..ef32c14cd 100644 --- a/apps/st/package.json +++ b/apps/st/package.json @@ -14,7 +14,9 @@ "@common/components": "workspace:*", "@common/config": "workspace:*", "@common/network": "workspace:*", - "@rsone/components": "workspace:*", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^8.0.0", + "@dnd-kit/utilities": "^3.2.2", "@faker-js/faker": "^9.9.0", "@fontsource-variable/noto-sans-kr": "^5.2.5", "@frontend-opensource/use-react-hooks": "^1.1.4", @@ -29,6 +31,7 @@ "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", + "@rsone/components": "workspace:*", "@tanstack/react-query": "^5.67.1", "@tanstack/react-query-devtools": "^5.75.5", "@tanstack/react-table": "^8.21.2", diff --git a/apps/st/src/components/statistics/Dashboard.tsx b/apps/st/src/components/statistics/Dashboard.tsx index f3682d1e0..b3734e386 100644 --- a/apps/st/src/components/statistics/Dashboard.tsx +++ b/apps/st/src/components/statistics/Dashboard.tsx @@ -1,9 +1,96 @@ -const Dashboard = () => { - return ( -
- Dashboard -
- ); -}; +"use client"; -export default Dashboard; \ No newline at end of file +import { useEffect, useState } from "react"; + +import { closestCenter, DndContext } from "@dnd-kit/core"; +import { rectSortingStrategy, SortableContext } from "@dnd-kit/sortable"; +import dayjs from "dayjs"; + +import { useUserStore } from "@/shared/lib"; +import { Skeleton } from "@common/components"; + +import { useDndSensors } from "@/pages/statistics/lib/hooks/use-dnd-sensors"; +import { usePanelOrder } from "@/pages/statistics/lib/hooks/use-panel-order"; +import { getInitialPanels } from "@/pages/statistics/model/panel-config"; +import { SortablePanel } from "@/pages/statistics/ui/sortable-panel"; + +interface PanelItem { + id: string; + component: React.ReactNode; + className?: string; +} + +function Dashboard() { + const userInfo = useUserStore((state) => state.userInfo); + const [initialPanels, setInitialPanels] = useState([]); + const [isMounted, setIsMounted] = useState(false); + const sensors = useDndSensors(); + + const { panels, handleDragEnd } = usePanelOrder({ + initialPanels, + storageKey: "statistics-dashboard-panels-order", + }); + + useEffect(() => { + setIsMounted(true); + }, []); + + useEffect(() => { + if (!userInfo) return; + const initialPanels = getInitialPanels({ + dashboardParams: { + statsYr: String(dayjs().year() - 1), + dtlInstCd: userInfo.dtlInstCd, + enoCd: userInfo.enoCd, + laeSeCd: userInfo.laeSeCd, + }, + scheduleInfoParams: { + statsYr: String(dayjs().year() - 1), + }, + }); + setInitialPanels(initialPanels); + }, [userInfo]); + + return ( + <> + {isMounted && initialPanels.length > 0 ? ( + + p.id)} + strategy={rectSortingStrategy} + > + {/* TBD: 그리드 레이아웃 비율 정의 필요 + 현재: 3열 그리드, 반응형 브레이크포인트 적용 + SPEC 레이아웃에 따라 조정 필요 */} +
+ {panels.map((panel) => ( + + {panel.component} + + ))} +
+
+
+ ) : ( +
+ + + + + + +
+ )} + + ); +} + +export default Dashboard; diff --git a/apps/st/src/components/statistics/verification/StatisticsVerificationReport.tsx b/apps/st/src/components/statistics/verification/StatisticsVerificationReport.tsx index 896ee061c..6cd57b4f1 100644 --- a/apps/st/src/components/statistics/verification/StatisticsVerificationReport.tsx +++ b/apps/st/src/components/statistics/verification/StatisticsVerificationReport.tsx @@ -129,6 +129,7 @@ const DEFAULT_FILTERS: StatisticsVerificationFilterState = { statsYr: String(dayjs().year() - 1), sewerBzmnCd: null, statsCd: null, + laeSbmsnSttsCd: "2", }; const DEFAULT_SUB_FILTERS: StatisticsVerificationSubFilterState = { @@ -156,15 +157,6 @@ function StatisticsVerificationReport() { useState(DEFAULT_FILTERS); const [subFilters, setSubFilters] = useState(DEFAULT_SUB_FILTERS); - const [submittedFilters, setSubmittedFilters] = - useState(DEFAULT_FILTERS); - const [submittedParams, setSubmittedParams] = useState<{ - main: StatisticsVerificationFilterState; - sub: StatisticsVerificationSubFilterState; - }>({ - main: DEFAULT_FILTERS, - sub: DEFAULT_SUB_FILTERS, - }); const [selectedReportRows, setSelectedReportRows] = useState< StatsInputStatusList[] >([]); @@ -182,11 +174,30 @@ function StatisticsVerificationReport() { const { data: sigunguData } = useStAnlsSigunguCombo( filters.sewerBzmnCd ?? "", ); + const detailParams = useMemo( + () => ({ + statsYr: filters.statsYr, + ctpvNm: + subFilters.ctpvNm !== "" ? subFilters.ctpvNm : filters.ctpvNm, + sggNm: subFilters.sggNm !== "" ? subFilters.sggNm : filters.sggNm, + sewerBzmnCd: + subFilters.sewerBzmnCd !== "" + ? subFilters.sewerBzmnCd + : filters.sewerBzmnCd, + statsCd: + subFilters.statsCd !== "" + ? subFilters.statsCd + : filters.statsCd, + prcsYnList: subFilters.prcsYnList, + pbcpAprvYn: subFilters.pbcpAprvYn, + laeSbmsnSttsCd: subFilters.laeSbmsnSttsCd, + }), + [filters, subFilters], + ); - const statisticsVerificationReportQuery = - useStatsVrfcRptData(submittedFilters); + const statisticsVerificationReportQuery = useStatsVrfcRptData(filters); const statisticsVerificationResultQuery = - useStatsVrfcRsltData(submittedParams); + useStatsVrfcRsltData(detailParams); const statisticsVerificationReportData = statisticsVerificationReportQuery.data ?? []; @@ -253,11 +264,17 @@ function StatisticsVerificationReport() { const nextRows = statisticsVerificationResultGridRows; const prevLength = prevLengthRef.current; - if (!dataProvider || nextRows.length === 0) { + if (!dataProvider) { prevLengthRef.current = nextRows.length; return; } + if (nextRows.length === 0) { + dataProvider.setRows?.([]); + prevLengthRef.current = 0; + return; + } + if (nextRows.length > prevLength && prevLength > 0) { // 무한스크롤 시 데이터 처리 const newRows = nextRows.slice(prevLength); @@ -327,17 +344,16 @@ function StatisticsVerificationReport() { statisticsVerificationReportQuery.isError, ]); - function handleSearch( - nextFilters: StatisticsVerificationFilterState, - nextSubFilters: StatisticsVerificationSubFilterState, - ) { - setSelectedReportRows([]); + function resetResultState() { setSelectedResultRows([]); - setSubmittedFilters({ ...nextFilters }); - setSubmittedParams({ - main: { ...nextFilters }, - sub: { ...nextSubFilters }, - }); + editedResultRowMapRef.current = new Map(); + prevLengthRef.current = 0; + resultGridRef.current?.dataProvider?.setRows?.([]); + } + + function resetReportState() { + setSelectedReportRows([]); + resetResultState(); } async function handleLoadMoreDetail() { @@ -350,14 +366,6 @@ function StatisticsVerificationReport() { } } - function handleResultSearch() { - setSelectedResultRows([]); - setSubmittedParams({ - main: { ...filters }, - sub: { ...subFilters }, - }); - } - function handleFilterChange< K extends keyof StatisticsVerificationFilterState, >(key: K) { @@ -365,6 +373,7 @@ function StatisticsVerificationReport() { value?: StatisticsVerificationFilterState[K], optionData?: { label: string; value: string }, ) => { + resetReportState(); if (key === "sewerBzmnCd") { setFilters((prev) => ({ ...prev, @@ -372,8 +381,17 @@ function StatisticsVerificationReport() { sewerBzmnCd: optionData?.value, sggNm: "", })); + setSubFilters((prev) => ({ + ...prev, + ctpvNm: "", + sggNm: "", + sewerBzmnCd: "", + })); return; } + if (key === "statsCd") { + setSubFilters((prev) => ({ ...prev, statsCd: "" })); + } setFilters((prev) => ({ ...prev, [key]: value })); }; } @@ -382,10 +400,16 @@ function StatisticsVerificationReport() { K extends keyof StatisticsVerificationSubFilterState, >(key: K) { return (value?: StatisticsVerificationSubFilterState[K]) => { + resetResultState(); setSubFilters((prev) => ({ ...prev, [key]: value })); }; } + function handleSubFilterReset() { + setSubFilters(DEFAULT_SUB_FILTERS); + resetResultState(); + } + function handleCellEdited(params: CellEditedCallbackParams) { const { itemIndex, fieldName, newValue } = params; const dataProvider = resultGridRef.current?.dataProvider as @@ -403,11 +427,7 @@ function StatisticsVerificationReport() { function handleReset() { setFilters(DEFAULT_FILTERS); setSubFilters(DEFAULT_SUB_FILTERS); - setSelectedReportRows([]); - setSelectedResultRows([]); - editedResultRowMapRef.current = new Map(); - prevLengthRef.current = 0; - handleSearch(DEFAULT_FILTERS, DEFAULT_SUB_FILTERS); + resetReportState(); } function handleApprove() { @@ -532,9 +552,6 @@ function StatisticsVerificationReport() { - +