diff --git a/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx b/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx index fd96905cc..b568d737f 100644 --- a/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx +++ b/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx @@ -1115,7 +1115,7 @@ const PublicSewageFacilityInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - const hasImr = errors.some((e) => e.vrfcCd === "IMR"); + const hasImr = errors.some((e) => e.vrfcCd === "IMR" || e.vrfcCd === "HB"); return hasImr ? "rg-cell-verify-error-imr" : "rg-cell-verify-error"; } }} @@ -1127,10 +1127,11 @@ const PublicSewageFacilityInp = () => { return errors .map((e) => { const prefix = - e.vrfcCd === "IMR" ? "IMR검증 - " : "일반검증 - "; + e.vrfcCd === "IMR" ? "IMR검증 - " : (e.vrfcCd === "NV" ? "일반검증 - " : "HB검증 - "); const comp = e.compVlCn ?? ""; const now = e.nowVlCn ?? ""; - return `${prefix}${e.rsnExpln}(검증 기준값: ${comp} / 현재 입력값: ${now})`; + return `${prefix}${e.rsnExpln} + (검증 기준값: ${comp} / 현재 입력값: ${now})`; }) .filter(Boolean) .join("\n"); diff --git a/apps/st/src/components/statistics/input/SewagePipeInp.tsx b/apps/st/src/components/statistics/input/SewagePipeInp.tsx index 0702193ce..a39ab67bd 100644 --- a/apps/st/src/components/statistics/input/SewagePipeInp.tsx +++ b/apps/st/src/components/statistics/input/SewagePipeInp.tsx @@ -1978,7 +1978,7 @@ const SewagePipeInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - const hasImr = errors.some((e) => e.vrfcCd === "IMR"); + const hasImr = errors.some((e) => e.vrfcCd === "IMR" || e.vrfcCd === "HB"); return hasImr ? "rg-cell-verify-error-imr" : "rg-cell-verify-error"; } }} @@ -1990,10 +1990,11 @@ const SewagePipeInp = () => { return errors .map((e) => { const prefix = - e.vrfcCd === "IMR" ? "IMR검증 - " : "일반검증 - "; + e.vrfcCd === "IMR" ? "IMR검증 - " : (e.vrfcCd === "NV" ? "일반검증 - " : "HB검증 - "); const comp = e.compVlCn ?? ""; const now = e.nowVlCn ?? ""; - return `${prefix}${e.rsnExpln}(검증 기준값: ${comp} / 현재 입력값: ${now})`; + return `${prefix}${e.rsnExpln} + (검증 기준값: ${comp} / 현재 입력값: ${now})`; }) .filter(Boolean) .join("\n"); diff --git a/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx b/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx index dccc909ae..ddbb712d9 100644 --- a/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx +++ b/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx @@ -766,7 +766,7 @@ const SeweragePenetrationRateInp = () => { const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { // IMR 검증은 노란색으로 구분 - const hasImr = errors.some((e) => e.vrfcCd === 'IMR'); + const hasImr = errors.some((e) => e.vrfcCd === 'IMR' || e.vrfcCd === 'HB'); return hasImr ? 'rg-cell-verify-error-imr' : 'rg-cell-verify-error'; } }} @@ -777,8 +777,9 @@ const SeweragePenetrationRateInp = () => { if (Array.isArray(errors) && errors.length > 0) { return errors .map((e) => { - const prefix = e.vrfcCd === 'IMR' ? 'IMR검증 - ' : '일반검증 - '; - return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`; + const prefix = e.vrfcCd === 'IMR' ? 'IMR검증 - ' : (e.vrfcCd === 'NM' ? '일반검증 - ' : 'HB검증 -'); + return `${prefix}${e.rsnExpln} + (검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`; }) .filter(Boolean) .join('\n'); diff --git a/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx b/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx index 6c37af823..9efaf3d4d 100644 --- a/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx +++ b/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx @@ -4,13 +4,17 @@ import { RealGridReact } from "realgrid-react"; import useSWR, { useSWRConfig } from "swr"; import { 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 { + isInputPeriodCurrent, + type CheckInputPeriodItem, +} from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface"; import type { StatsInputStatusListRequestParams } from "@/entities/statistics/model/input/statistics-input-status-interface"; import type { StatsInputStatusListApiResponse } from "@/entities/statistics/model/input/statistics-input-status-interface"; import { LoadingWithAnimation } from "@common/components"; import ApproveButton from "@/components/button/ApproveButton"; -import RejectButton from "@/components/button/RejectButton"; import DataGrid from "@/components/module/DataGrid"; import SearchForm from "@/components/module/SearchForm"; import type { SearchFormValues } from "@/components/type/SearchForm.type"; @@ -37,6 +41,8 @@ const StatsInputStatusInp = () => { const [lastSearchedValues, setLastSearchedValues] = useState(null); const [hasCompletedSearch, setHasCompletedSearch] = useState(false); + const periodCheckListRef = useRef([]); + const inputPeriodCurrentRef = useRef(false); const gridRef = useRef(null); const swrKey = searchParams @@ -72,21 +78,38 @@ const StatsInputStatusInp = () => { } }, [activeTabId]); - const handleSearch = (values: SearchFormValues) => { + const handleSearch = async (values: SearchFormValues) => { if (values.sido === "" || values.sido === "ALL") { alert("시도를 선택해주세요."); return; } - 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]); + + 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 = () => { @@ -94,10 +117,8 @@ const StatsInputStatusInp = () => { setRowData([]); setLastSearchedValues(null); setHasCompletedSearch(false); - }; - - const rejectFunc = () => { - // 보완 요청 로직 + periodCheckListRef.current = []; + inputPeriodCurrentRef.current = false; }; const approveFunc = () => { @@ -125,7 +146,7 @@ const StatsInputStatusInp = () => { ? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== "ALL") : true; const showActionToolbarCard = - hasCompletedSearch && rowData.length > 0 && gugunOkOrNotDoList; + hasCompletedSearch && rowData.length > 0 && gugunOkOrNotDoList && inputPeriodCurrentRef.current; return ( <> @@ -134,7 +155,6 @@ const StatsInputStatusInp = () => { {showActionToolbarCard && (
-
diff --git a/apps/st/src/components/statistics/review/PublicSewageFacilityRvw.tsx b/apps/st/src/components/statistics/review/PublicSewageFacilityRvw.tsx index 891dbeacb..37dff9b7e 100644 --- a/apps/st/src/components/statistics/review/PublicSewageFacilityRvw.tsx +++ b/apps/st/src/components/statistics/review/PublicSewageFacilityRvw.tsx @@ -1,20 +1,22 @@ import { useMemo, useState } from "react"; import { LoadingWithAnimation } from "@common/components"; -import { getUserInfo } from "../../../../../../packages/common/components/src/api"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { useAlertStore } from "@/shared/lib"; +import { useAlertStore, useUserStore } from "@/shared/lib"; import { statisticsApi } from "@/entities/statistics/api/statistics-service"; import { statisticsManagementApi } from "@/entities/statistics/api/statistics-management-service"; import type { PublicSewageFacilityReviewItem } from "@/entities/statistics/model/statistics-public-sewage-facility-review-interface"; import type { StatsStatusInfo } from "@/entities/statistics/model/statistics-sewage-pipe-review-interface"; -import type { ReviewOpinionSaveRequest } from "@/entities/statistics/model/statistics-interface"; +import type { + ReviewOpinionSaveRequest, + ReviewerInfoRequest, +} from "@/entities/statistics/model/statistics-interface"; import DataGrid from "@/components/module/DataGrid"; import SearchForm from "@/components/module/SearchForm"; import SubmitButton from "@/components/button/SumitButton"; import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; import ReturnButton from "@/components/button/ReturnButton"; import { ReturnReasonDialog } from "@/components/statistics/review/ReturnReasonDialog"; +import { ReviewSubmitDialog } from "@/components/statistics/review/ReviewSubmitDialog"; import { ReviewOpinionDialog, type ReviewOpinionDialogContext, @@ -27,12 +29,13 @@ import { PublicSewageFacilityInfoList } from "@/components/util/column/review/Pu // 하수도 통계 관리 06.공공하수처리시설 const PublicSewageFacilityRvw = () => { const { setMessage: alert } = useAlertStore((state) => state); - const { confirm } = useConfirm(); + const userInfo = useUserStore((state) => state.userInfo); const [rowData, setRowData] = useState[]>([]); const [isLoading, setIsLoading] = useState(false); const [statsStatusInfo, setStatsStatusInfo] = useState({}); const [reviewOpinionOpen, setReviewOpinionOpen] = useState(false); const [returnDialogOpen, setReturnDialogOpen] = useState(false); + const [submitDialogOpen, setSubmitDialogOpen] = useState(false); const [reviewOpinionContext, setReviewOpinionContext] = useState(null); const [latestReviewOpinion, setLatestReviewOpinion] = useState(""); @@ -67,6 +70,24 @@ const PublicSewageFacilityRvw = () => { const showActionBar = showReviewOpinionButton || showReturnButton || showSubmitButton; + const submitDialogContext = useMemo(() => { + if (reviewOpinionContext == null) { + return null; + } + + const instSeCd = userInfo?.instSeCd ?? ""; + if (!instSeCd) { + return null; + } + + return { + statsYr: reviewOpinionContext.statsYr, + sewerBzmnCd: reviewOpinionContext.sewerBzmnCd, + statsCd: reviewOpinionContext.statsCd, + instSeCd, + }; + }, [reviewOpinionContext, userInfo?.instSeCd]); + const handleReset = () => { setRowData([]); setStatsStatusInfo({}); @@ -74,6 +95,7 @@ const PublicSewageFacilityRvw = () => { setLatestReviewOpinion(""); setLatestProcessHistory(""); setLastSearchValues(null); + setSubmitDialogOpen(false); }; const handleSearch = async (values: SearchFormValues) => { @@ -134,37 +156,13 @@ const PublicSewageFacilityRvw = () => { } }; - const submitFunc = async () => { - if (reviewOpinionContext == null) { + const submitFunc = () => { + if (submitDialogContext == null) { alert("조회 후 제출할 수 있습니다."); return; } - if (!(await confirm("제출 하시겠습니까?"))) { - return; - } - - try { - const userInfo = await getUserInfo(); - const response = await statisticsApi.submitReview({ - statsYr: reviewOpinionContext.statsYr, - sewerBzmnCd: reviewOpinionContext.sewerBzmnCd, - statsCd: reviewOpinionContext.statsCd, - mdfrId: userInfo.userUnqId, - }); - - if (response.status !== "success") { - alert(response.message || "제출에 실패했습니다."); - return; - } - - alert(response.message || "제출되었습니다."); - if (lastSearchValues != null) { - await handleSearch(lastSearchValues); - } - } catch { - alert("제출 중 오류가 발생했습니다."); - } + setSubmitDialogOpen(true); }; const reviewOpinionFunc = async () => { @@ -261,6 +259,16 @@ const PublicSewageFacilityRvw = () => { initialProcessHistory={latestProcessHistory} onSave={saveReviewOpinion} /> + { + if (lastSearchValues != null) { + await handleSearch(lastSearchValues); + } + }} + />