import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useConfirm } from "@frontend-opensource/use-react-hooks"; import { RealGridReact } from "realgrid-react"; import useSWR, { useSWRConfig } from "swr"; import { useActiveTabRegAuthorityAllowed, useAlertStore } from "@/shared/lib"; import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service"; import { StatisticsInputStatusApi } from "@/entities/statistics/api/input/statistics-input-status-service"; import { statisticsApi } from "@/entities/statistics/api/statistics-service"; import { isInputPeriodCurrent, type CheckInputPeriodItem, } from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface"; import type { StatsInputStatusApproveItem } from "@/entities/statistics/model/statistics-interface"; import type { StatsInputStatusListApiResponse, StatsInputStatusListRequestParams, } from "@/entities/statistics/model/input/statistics-input-status-interface"; import { getUserInfo } from "../../../../../../packages/common/components/src/api"; import { LoadingWithAnimation } from "@common/components"; import ApproveButton from "@/components/button/ApproveButton"; import DataGrid from "@/components/module/DataGrid"; import type { DataCellClickCallbackParams } from "@/components/type/DataGrid.type"; import { ExceptionProcessingHistoryDialog, type ExceptionProcessingHistoryQueryContext, } from "@/components/module/ExceptionProcessingHistoryDialog"; import SearchForm from "@/components/module/SearchForm"; import type { SearchFormValues } from "@/components/type/SearchForm.type"; import { doList } from "@/components/util/column/input/DoList"; import { StatsInputStatusInpColumn } from "@/components/util/column/input/StatsInputStatusInpColumn"; import { toGridRows } from "@/components/util/grid/gridFunction"; import { useTabStore } from "@/store/tab-store"; const getStatsInputStatusFetcher = async ( key: [string, StatsInputStatusListRequestParams], ): Promise => { return StatisticsInputStatusApi.getStatisticsInputStatusList(key[1]); }; // 하수도 통계 관리 > 00. 통계 입력 현황 const StatsInputStatusInp = () => { const { setMessage: alert } = useAlertStore((state) => state); const { confirm } = useConfirm(); const activeTabId = useTabStore((state) => state.activeTabId); const [rowData, setRowData] = useState[]>( [], ); const [searchParams, setSearchParams] = useState(null); const [lastSearchedValues, setLastSearchedValues] = useState(null); const [hasCompletedSearch, setHasCompletedSearch] = useState(false); const periodCheckListRef = useRef([]); const inputPeriodCurrentRef = useRef(false); const gridRef = useRef(null); const [exceptionHistoryOpen, setExceptionHistoryOpen] = useState(false); const [exceptionHistoryContext, setExceptionHistoryContext] = useState< ExceptionProcessingHistoryQueryContext | null >(null); const [isApproveLoading, setIsApproveLoading] = useState(false); const regAuthorityToolbarAllowed = useActiveTabRegAuthorityAllowed(); const swrKey = searchParams ? (["stats-input-status", searchParams] as const) : null; const { mutate } = useSWRConfig(); const { isLoading } = useSWR( swrKey, getStatsInputStatusFetcher, { revalidateOnFocus: true, onSuccess: (data) => { if (data.status === "success" && data.data) { setRowData( toGridRows(data.data).map((r) => ({ ...r, exceptionSubmitHistory: "예외제출내역", })), ); setHasCompletedSearch(true); } else { setHasCompletedSearch(false); setRowData([]); alert(data.message ?? "조회에 실패했습니다."); } }, onError: () => { alert("데이터 조회 중 오류가 발생했습니다."); setHasCompletedSearch(false); setRowData([]); }, }, ); useEffect(() => { if (activeTabId === "StatsInputStatusInp" && swrKey) { mutate(swrKey); } }, [activeTabId]); const handleSearch = async (values: SearchFormValues) => { if (values.sido === "" || values.sido === "ALL") { alert("시도를 선택해주세요."); return; } try { const response = await statisticsInputSewSupplyRateApi.checkInputPeriod(values.year || ""); if (response.status !== "success") { alert(response.message ?? "입력기간 확인에 실패했습니다."); return; } const periods = Array.isArray(response.data) ? response.data : []; periodCheckListRef.current = periods; const checkEnterable = isInputPeriodCurrent(periods); inputPeriodCurrentRef.current = checkEnterable; const params: StatsInputStatusListRequestParams = { ctpvNm: values.sido, sewerBzmnCd: values.bsnmCd, sggNm: values.gugun === "ALL" ? "" : values.gugun, statsYr: values.year ?? "", }; setHasCompletedSearch(false); setLastSearchedValues(values); setSearchParams(params); mutate(["stats-input-status", params]); } catch { alert("데이터 조회 중 오류가 발생했습니다."); } }; const handleReset = () => { setSearchParams(null); setRowData([]); setLastSearchedValues(null); setHasCompletedSearch(false); periodCheckListRef.current = []; inputPeriodCurrentRef.current = false; setExceptionHistoryOpen(false); setExceptionHistoryContext(null); setIsApproveLoading(false); }; const approveFunc = async () => { if (rowData.length === 0) { alert("조회된 통계 항목이 없습니다."); return; } if (!lastSearchedValues?.year?.trim() || !lastSearchedValues?.bsnmCd?.trim()) { alert("먼저 조회를 진행해주세요."); return; } const hasInvalidStatus = rowData.some((row) => ["0", "1", "4"].includes(String(row.laeSbmsnSttsCd ?? "")), ); if (hasInvalidStatus) { alert("미작성이나 미제출, 혹은 보완요청(반려) 상태인 항목이 있습니다."); return; } if (!(await confirm("조회된 통계 항목을 승인하시겠습니까?"))) { return; } setIsApproveLoading(true); try { const userInfo = await getUserInfo(); const mdfrId = (userInfo.userUnqId ?? "").trim(); if (!mdfrId) { alert("수정자 ID를 확인할 수 없습니다."); return; } const statsYr = lastSearchedValues.year.trim(); const sewerBzmnCdDefault = lastSearchedValues.bsnmCd.trim(); const approveItems: StatsInputStatusApproveItem[] = rowData .map((row) => { const statsCd = String(row.statsCd ?? "").trim(); const sewerBzmnCd = String(row.sewerBzmnCd ?? sewerBzmnCdDefault).trim(); return { statsYr, sewerBzmnCd, statsCd, mdfrId }; }) .filter((item) => item.statsCd.length > 0); if (approveItems.length === 0) { alert("통계 코드(statsCd)가 있는 행이 없습니다."); return; } const response = await statisticsApi.approveStatsInputStatus({ list: approveItems }, "INPUT"); if (response.resultCode.toLocaleLowerCase() === "success") { if (searchParams) { await mutate(["stats-input-status", searchParams]); } alert(response.message ?? "승인되었습니다."); } else { alert(response.message ?? "승인에 실패했습니다."); } } catch { alert("승인 처리 중 오류가 발생했습니다."); } finally { setIsApproveLoading(false); } }; const openExceptionHistoryForRow = useCallback( (row: Record) => { if (!regAuthorityToolbarAllowed) { alert("권한이 없습니다."); return; } const ctx = lastSearchedValues; if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd) { alert("먼저 조회를 진행해주세요."); return; } const statsCd = String(row.statsCd ?? "").trim(); if (!statsCd) { alert("통계 코드가 없어 예외처리 내역을 조회할 수 없습니다."); return; } setExceptionHistoryContext({ statsYr: ctx.year ?? "", sewerBzmnCd: ctx.bsnmCd ?? "", ctpvNm: ctx.sido ?? "", sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""), statsCd, }); setExceptionHistoryOpen(true); }, [alert, lastSearchedValues, regAuthorityToolbarAllowed], ); const handleExceptionSubmitHistoryCellClick = useCallback( ({ fieldName, row }: DataCellClickCallbackParams) => { if (fieldName !== "exceptionSubmitHistory") return; openExceptionHistoryForRow(row); }, [openExceptionHistoryForRow], ); const handleExceptionHistoryRejectSuccess = useCallback(async () => { if (!searchParams) return; await mutate(["stats-input-status", searchParams]); }, [mutate, searchParams]); const gugunOkOrNotDoList = doList.includes(lastSearchedValues?.bsnmCd || "") ? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== "ALL") : true; const allRowsLaeSbmsnApproved = useMemo( () => rowData.length > 0 && rowData.every((row) => String(row.laeSbmsnSttsCd ?? "") === "3"), [rowData], ); const showActionToolbarCard = hasCompletedSearch && rowData.length > 0 && gugunOkOrNotDoList && inputPeriodCurrentRef.current && !allRowsLaeSbmsnApproved && regAuthorityToolbarAllowed; return ( <>
{showActionToolbarCard && (
)} {(isLoading || isApproveLoading) && (
)} { if (fieldName !== "laeSbmsnSttsNm") { return undefined; } const statusCd = String(row.laeSbmsnSttsCd ?? ""); if (statusCd === "0" || statusCd === "1") { return "rg-cell-stats-status-pending"; } if (statusCd === "2") { return "rg-cell-stats-status-submitted"; } if (statusCd === "3") { return "rg-cell-stats-status-approved"; } if (statusCd === "4") { return "rg-cell-stats-status-rejected"; } return undefined; }} />
); }; export default StatsInputStatusInp;