Merge remote-tracking branch 'origin/iipbs/st/dev'

This commit is contained in:
2026-08-28 17:12:54 +09:00
52 changed files with 1344 additions and 728 deletions
+63 -22
View File
@@ -5,6 +5,11 @@ import { useEffect, useState } from "react";
import useSWR from "swr";
import { useAlertStore, useUserStore } from "@/shared/lib";
import {
fetchSearchCondition,
fetchSearchConditionInput,
SEARCH_CONDITION_SWR_KEY,
} from "@/shared/lib/use-search-condition-sido-list";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import type {
ApiResponse,
@@ -23,28 +28,28 @@ import {
SelectValue,
} from "@common/components";
import { doList } from "@/components/util/column/input/DoList";
import {
defaultSearchFormProps,
type SearchFormClsfCd,
type SearchFormProps,
type SearchFormValues,
} from "../type/SearchForm.type";
import { doList } from "../util/column/input/DoList";
const getSidoListFetcher = async (
key: [string, string],
): Promise<ApiResponse<SearchCondition>> => {
return statisticsApi.getSidoList({ instSeCd: key[1] });
};
function isDoListBsnm(bsnmCd: string | undefined): boolean {
return Boolean(bsnmCd && doList.includes(bsnmCd));
}
const getSggListFetcher = async (
key: [string, string, string, string],
key: [string, string, string, "Y" | "N"],
): Promise<ApiResponse<Sgg[]>> => {
const gugunSkipYn = doList.includes(key[3]) ? "N" : "Y";
return statisticsApi.getSggList(key[1], {
instSeCd: key[2],
gugunSkipYn,
});
const [, sidoName, instSeCd, gugunSkipYn] = key;
const params = { instSeCd, gugunSkipYn };
if (gugunSkipYn === "N") {
return statisticsApi.getSggAllList(sidoName, params);
}
return statisticsApi.getSggList(sidoName, params);
};
function buildSearchFormValues(
@@ -59,9 +64,15 @@ function buildSearchFormValues(
: "ALL";
if (d?.sido) {
const sidoItem = sidoList?.data?.sidoList?.find(
(item) => item.sido === d.sido,
);
const gugun =
d.gugun ||
(sidoItem && !isDoListBsnm(sidoItem.bsnmCd) ? "ALL" : "");
return {
sido: d.sido,
gugun: d.gugun || "",
gugun,
year: d.year || defaultYear.toString(),
order: d.order || "",
fctName: d.fctName || "",
@@ -93,7 +104,8 @@ function buildSearchFormValues(
return {
sido: useFirstSido ? first.sido : "",
gugun: "",
gugun:
useFirstSido && first && !isDoListBsnm(first.bsnmCd) ? "ALL" : "",
year: d?.year || defaultYear.toString(),
order: d?.order || "",
fctName: d?.fctName || "",
@@ -117,10 +129,12 @@ const SearchForm = (props: SearchFormProps = {}) => {
const currentYear = new Date().getFullYear();
const defaultYear = currentYear - 1;
const isInput = Boolean(props.isInput);
const sidoListKind = isInput ? "input" : "default";
const { data: sidoList } = useSWR<ApiResponse<SearchCondition>>(
["sido-list", instSeCd],
getSidoListFetcher,
SEARCH_CONDITION_SWR_KEY(instSeCd, sidoListKind),
isInput ? fetchSearchConditionInput : fetchSearchCondition,
{
revalidateOnFocus: false,
revalidateOnReconnect: false,
@@ -157,7 +171,7 @@ const SearchForm = (props: SearchFormProps = {}) => {
: {
...prev,
sido: first.sido,
gugun: "",
gugun: isDoListBsnm(first.bsnmCd) ? "" : "ALL",
bsnmCd: first.bsnmCd,
},
);
@@ -168,9 +182,18 @@ const SearchForm = (props: SearchFormProps = {}) => {
mergedProps.sido,
]);
const sggKey: [string, string, string, string] | null =
values.sido && values.sido !== "ALL"
? ["sgg-list", values.sido, instSeCd, values.bsnmCd || ""]
const selectedSido = sidoList?.data?.sidoList?.find(
(item) => item.sido === values.sido,
);
const gugunSkipYn = selectedSido?.gugunSkipYn ?? "Y";
const sggKey: [string, string, string, "Y" | "N"] | null =
values.sido && values.sido !== "ALL" && sidoList?.data
? [
gugunSkipYn === "N" ? "sgg-all" : "sgg-list",
values.sido,
instSeCd,
gugunSkipYn,
]
: null;
const { data: sggList } = useSWR<ApiResponse<Sgg[]>>(
sggKey,
@@ -195,12 +218,25 @@ const SearchForm = (props: SearchFormProps = {}) => {
);
const sggOptions = sggList?.data ?? [];
// 시도 설정 후 시군구 목록이 오면 첫 번째 항목 자동 선택
// 도(doList): 시군구 목록 첫 항목. 광역시·세종·제주: 전체
useEffect(() => {
if (!mergedProps.gugun) return;
if (props.defaultRegionAll) return;
if (props.defaultValues?.gugun) return;
if (!values.sido || values.sido === "ALL") return;
if (!selectedSido) return;
if (!isDoListBsnm(selectedSido.bsnmCd)) {
setValues((prev) => {
if (prev.gugun) return prev;
return {
...prev,
gugun: "ALL",
bsnmCd: selectedSido?.bsnmCd || prev.bsnmCd,
};
});
return;
}
const firstSgg = sggList?.data?.[0];
if (!firstSgg) return;
@@ -216,6 +252,7 @@ const SearchForm = (props: SearchFormProps = {}) => {
}, [
sggList,
values.sido,
selectedSido?.bsnmCd,
mergedProps.gugun,
props.defaultValues?.gugun,
props.defaultRegionAll,
@@ -237,10 +274,14 @@ const SearchForm = (props: SearchFormProps = {}) => {
? ""
: sidoList?.data?.sidoList?.find((v) => v.sido === value)
?.bsnmCd || "";
const gugunAll =
props.defaultRegionAll ||
value === "ALL" ||
!isDoListBsnm(selectedBsnm);
setValues((prev) => ({
...prev,
sido: value,
gugun: props.defaultRegionAll || value === "ALL" ? "ALL" : "",
gugun: gugunAll ? "ALL" : "",
bsnmCd: selectedBsnm,
}));
};
@@ -8,6 +8,7 @@ import useSWR from "swr";
import { useAlertStore } from "@/shared/lib";
import { statisticsInputSewPipeApiService } from "@/entities/statistics/api/input/statistics-sewage-pipe-input-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { isGugunRequiredSido } from "@/entities/statistics";
import type {
SeweragePipeFacilitiesListRequest,
SeweragePipeFacilityAdminAreaListRequest,
@@ -34,7 +35,6 @@ import {
import { getUserInfo } from "../../../../../packages/common/components/src/api";
import type { ColumnInfo } from "../type/DataGrid.type";
import type { SearchFormValues } from "../type/SearchForm.type";
import { doNameList } from "../util/column/input/DoList";
import CompactDataGrid from "./CompactDataGrid";
@@ -115,9 +115,9 @@ const getSidoListFetcher = async (): Promise<ApiResponse<SearchCondition>> => {
};
const getSggAllListFetcher = async (
key: [string, string],
key: [string, string, "Y" | "N"],
): Promise<ApiResponse<Sgg[]>> => {
return statisticsApi.getSggList(key[1]);
return statisticsApi.getSggList(key[1], { gugunSkipYn: key[2] });
};
type SidoSggFacilityNameSearchFormProps = {
@@ -185,8 +185,14 @@ const SidoSggFacilityNameSearchForm = ({
const didAutoFetchRef = useRef(false);
const isSavingConnectionRef = useRef(false);
const sggKey: [string, string] | null =
sido && sido !== "ALL" ? ["sgg-all", sido] : null;
const selectedSido = sidoList?.data?.sidoList?.find(
(item) => item.sido === sido,
);
const gugunSkipYn = selectedSido?.gugunSkipYn ?? "Y";
const sggKey: [string, string, "Y" | "N"] | null =
sido && sido !== "ALL" && sidoList?.data
? ["sgg-all", sido, gugunSkipYn]
: null;
const { data: sggList } = useSWR<ApiResponse<Sgg[]>>(
sggKey,
getSggAllListFetcher,
@@ -299,7 +305,11 @@ const SidoSggFacilityNameSearchForm = ({
swtrmtFcltCd,
sewerBzmnCd: initialSearchValues?.bsnmCd || '',
ctpvNm: initialSidoValue || '',
...(doNameList.includes(initialSidoValue) ? { sggNm: initialGugunValue } : {}),
...(isGugunRequiredSido(sidoList?.data?.sidoList, {
sido: initialSidoValue,
})
? { sggNm: initialGugunValue }
: {}),
};
try {
@@ -130,7 +130,7 @@ const verificationSubmitResultColumnInfo: ColumnInfo = {
export interface VerificationResultDialogQueryContext {
sewerBzmnCd: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
statsYr: string;
statsCd: string;
}
@@ -12,7 +12,7 @@ import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { ValueType } from "realgrid";
import type { RealGridReact } from "realgrid-react";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate, useSearchConditionSidoList } from "@/shared/lib";
import { STATISTICS_INPUT_URL_PATH } from "@/shared/lib/stats-cd-input-navigation";
import { useStatisticsInputPendingNavigation } from "@/shared/lib/use-statistics-input-pending-navigation";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
@@ -35,7 +35,16 @@ import type {
ImrStatusResponse,
SewerageStatusInfo,
Sgg,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
resolveNoApplicableForRequest,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -61,7 +70,6 @@ import type {
ColumnConfig,
} from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { ExcretaTreatFactInpColumn } from "@/components/util/column/input/ExcretaTreatFactInpColumn";
import { toGridRows } from "@/components/util/grid/gridFunction";
import { formatCompactYmdForGrid, toCompactYmdOrNull } from "@/components/util/grid/ymdFormat";
@@ -94,10 +102,22 @@ const computeTabEnterable = (
return periodEnterable;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function buildNoApplicableForRequest(
values: SearchFormValues | null,
checked: boolean,
isSaveListEmpty: boolean,
): boolean {
return resolveNoApplicableForRequest(sidoListRef.current, values, checked, isSaveListEmpty);
}
function computeTabToolbarSnapshot(
@@ -131,9 +151,8 @@ function buildExcretaTreatmentFacilitySearchParam(
const fcltNm = values.fctName?.trim();
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
...(fcltNm ? { fcltNm } : {}),
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
@@ -315,6 +334,8 @@ const EXCRETA_TREATMENT_FACILITY_NAME_GUIDE_MESSAGE =
// 하수도 통계 관리 > 07. 분뇨처리시설
const ExcretaTreatmentFacilityInp = () => {
const { sidoList } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
@@ -470,7 +491,11 @@ const ExcretaTreatmentFacilityInp = () => {
statusInfo: nextStatusInfo,
excretaTreatmentFacilityList,
} = response.data;
const list = excretaTreatmentFacilityList ?? [];
const list = filterBySelectedAdmdstCd(
excretaTreatmentFacilityList ?? [],
sidoListRef.current,
values,
);
const gridRows = mapExcretaTreatmentFacilityListToGridRows(list, commonInfo.ctpvNm);
setRowData(gridRows);
@@ -587,12 +612,8 @@ const ExcretaTreatmentFacilityInp = () => {
const userInfo = await getUserInfo();
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCd || EXCRETA_TREATMENT_FACILITY_STATS_CD,
@@ -628,7 +649,7 @@ const ExcretaTreatmentFacilityInp = () => {
if (!(await confirm("저장 하시겠습니까?"))) return;
commitGrid();
const noApplicable = isNotApplicable;
const noApplicable = buildNoApplicableForRequest(lastSearchedValues, isNotApplicable, getCurrentRows().length === 0);
const currentData = getCurrentRows();
const saveList = noApplicable
? []
@@ -639,7 +660,7 @@ const ExcretaTreatmentFacilityInp = () => {
.filter((row) => String(row.fcltSn ?? "").trim() !== "")
.map((row) => buildExcretaTreatmentFacilitySaveItem(row, sggOptions));
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -652,11 +673,7 @@ const ExcretaTreatmentFacilityInp = () => {
{
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
userId: userInfo.userUnqId,
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
noApplicable,
@@ -692,9 +709,9 @@ const ExcretaTreatmentFacilityInp = () => {
return;
}
const noApplicable = isNotApplicable;
const noApplicable = buildNoApplicableForRequest(lastSearchedValues, isNotApplicable, getCurrentRows().length === 0);
const currentData = getCurrentRows();
if (!noApplicable && currentData.length === 0) {
if (!noApplicable && currentData.length === 0 && buildSubmitOkForToolbar(lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -719,7 +736,7 @@ const ExcretaTreatmentFacilityInp = () => {
}
commitGrid();
const noApplicable = isNotApplicable;
const noApplicable = buildNoApplicableForRequest(lastSearchedValues, isNotApplicable, getCurrentRows().length === 0);
const currentData = getCurrentRows();
const submitList = noApplicable
? []
@@ -730,19 +747,15 @@ const ExcretaTreatmentFacilityInp = () => {
.filter((row) => String(row.fcltSn ?? "").trim() !== "")
.map((row) => buildExcretaTreatmentFacilitySaveItem(row, sggOptions));
if (!noApplicable && submitList.length === 0) {
if (!noApplicable && submitList.length === 0 && buildSubmitOkForToolbar(lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
const statsCdForSubmit = statsCd || EXCRETA_TREATMENT_FACILITY_STATS_CD;
const verificationQuery = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForSubmit,
};
@@ -761,11 +774,7 @@ const ExcretaTreatmentFacilityInp = () => {
{
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
userId: userInfo.userUnqId,
laePicNm: submitInfo.laePicNm,
laePicTelNo: submitInfo.laePicTelNo,
@@ -795,7 +804,7 @@ const ExcretaTreatmentFacilityInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? "",
stats_cd: statsCdForSubmit,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd:
@@ -959,7 +968,7 @@ const ExcretaTreatmentFacilityInp = () => {
}
};
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const searchContextForToolbar = lastSearchedValues;
const toolbarSnapshot = computeTabToolbarSnapshot(
statusInfo,
rowData.length > 0,
@@ -979,23 +988,23 @@ const ExcretaTreatmentFacilityInp = () => {
hasSearched &&
toolbarGroupAllowed &&
(showSaveInToolbar || showEnterableToolbarButtons);
const tabEnterableForGrid =
(computeTabEnterable(statusInfo, inputPeriodEnterable) || isAdminInfo) && inputPeriodEnterable;
const gugunOk = buildGugunOkForToolbar(searchContextForToolbar);
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const showNotApplicable =
searchLoaded &&
hasSearched &&
rowData.length === 0 &&
computeTabEnterable(statusInfo, inputPeriodEnterable);
const tabEnterableForGrid =
(computeTabEnterable(statusInfo, inputPeriodEnterable) || isAdminInfo) && inputPeriodEnterable;
const gugunOk = buildGugunOkForToolbar(lastSearchedValues);
computeTabEnterable(statusInfo, inputPeriodEnterable) &&
submitOk;
const verificationQueryContext = (() => {
const ctx = lastSearchedValues ?? searchValues;
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
@@ -1011,6 +1020,7 @@ const ExcretaTreatmentFacilityInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
fctName={true}
@@ -1030,10 +1040,10 @@ const ExcretaTreatmentFacilityInp = () => {
<LastYearDataButton
getLastYearDataFunc={() => void getLastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{statusInfo?.laeSbmsnSttsCd === "2" && (
{statusInfo?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { useAlertStore, useGlobalProgressStore } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useSearchConditionSidoList } from "@/shared/lib";
import { STATISTICS_INPUT_URL_PATH } from "@/shared/lib/stats-cd-input-navigation";
import { useStatisticsInputPendingNavigation } from "@/shared/lib/use-statistics-input-pending-navigation";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
@@ -31,7 +31,15 @@ import type {
cancelSubmitRequest,
ImrStatusResponse,
SewerageStatusInfo,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -50,7 +58,6 @@ import { VerificationResultDialog } from "@/components/module/VerificationResult
import { VerificationResultHistoryDialog } from "@/components/module/VerificationResultHistoryDialog";
import type { ColumnConfig, ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { PrivateSewageDesigStatusInpColumn } from "@/components/util/column/input/privateSewageFacility/PrivateSewageDesigStatusInpColumn";
import { SepticTankStatusInpColumn } from "@/components/util/column/input/privateSewageFacility/SepticTankStatusInpColumn";
import { SewageTreatFactStatusInpColumn } from "@/components/util/column/input/privateSewageFacility/SewageTreatFactStatusInpColumn";
@@ -102,9 +109,8 @@ function buildPrivateSewageFacilitySearchParam(
): SewageTreatFactStatusRequestParams {
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
}
@@ -122,10 +128,14 @@ const computeTabEnterable = (
return periodEnterable;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function computeTabToolbarSnapshot(
@@ -161,8 +171,8 @@ function tabRowDataExists(
function mapRegionalListToGridRows<
T extends { verificationResult?: PrivateSewageFacilityVerificationResult },
>(list: T[], ctpvNm: string): GridRowWithVerification[] {
return list.map((item) => {
>(list: T[], ctpvNm: string, values?: SearchFormValues | null): GridRowWithVerification[] {
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const { verificationResult, ...rest } = item;
const row = toGridRows([{ ctpvNm, ...rest }])[0] as GridRowWithVerification;
if (verificationResult) row.verificationResult = verificationResult;
@@ -314,6 +324,8 @@ function resolvePrivateSewageFacilityTab(tabKey: string) {
// 하수도 통계 관리 > 08. 개인하수처리시설
const PrivateSewageFacilityInp = () => {
const { sidoList } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
@@ -340,9 +352,12 @@ const PrivateSewageFacilityInp = () => {
const [footerVerificationByTab, setFooterVerificationByTab] = useState<
Map<string, VerificationResultLikeMap | null>
>(new Map());
const [toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
const [_toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
Map<string, TabToolbarSnapshot>
>(new Map());
const [searchContextByTab, setSearchContextByTab] = useState<Map<string, SearchFormValues>>(
() => new Map(),
);
const [verificationHistoryOpen, setVerificationHistoryOpen] = useState(false);
const [verificationResultDialogOpen, setVerificationResultDialogOpen] = useState(false);
const [showSubmitDialog, setShowSubmitDialog] = useState(false);
@@ -403,6 +418,13 @@ const PrivateSewageFacilityInp = () => {
);
return next;
});
if (searchContext) {
setSearchContextByTab((prev) => {
const next = new Map(prev);
next.set(tabKey, searchContext);
return next;
});
}
};
const applyRowMap = (
@@ -445,6 +467,7 @@ const PrivateSewageFacilityInp = () => {
const gridRows = mapRegionalListToGridRows(
(sewageTreatFactStatusList ?? []) as SewageTreatFactStatusItem[],
commonInfo.ctpvNm,
searchContext,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -476,6 +499,7 @@ const PrivateSewageFacilityInp = () => {
const gridRows = mapRegionalListToGridRows(
(septicTankStatusList ?? []) as SepticTankStatusItem[],
commonInfo.ctpvNm,
searchContext,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -507,6 +531,7 @@ const PrivateSewageFacilityInp = () => {
const gridRows = mapRegionalListToGridRows(
(privateSewageDesigStatusList ?? []) as PrivateSewageDesigStatusItem[],
commonInfo.ctpvNm,
searchContext,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -539,6 +564,7 @@ const PrivateSewageFacilityInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setActiveTab(firstTabKey);
activeTabRef.current = firstTabKey;
setLastSearchedValues(null);
@@ -582,6 +608,7 @@ const PrivateSewageFacilityInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
activeTabRef.current = firstTabKey;
setActiveTab(firstTabKey);
}
@@ -691,12 +718,8 @@ const PrivateSewageFacilityInp = () => {
}
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCdForTab,
@@ -779,11 +802,7 @@ const PrivateSewageFacilityInp = () => {
const submitInfoBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
laePicNm: submitInfo.laePicNm,
laePicTelNo: submitInfo.laePicTelNo,
laeKbrdrDeptNm: submitInfo.laeKbrdrDeptNm,
@@ -795,12 +814,8 @@ const PrivateSewageFacilityInp = () => {
};
const verificationQuery = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
};
@@ -868,7 +883,7 @@ const PrivateSewageFacilityInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? "",
stats_cd: statsCdForTab,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd: activeTabStatus?.laeSbmsnSttsCd === "4" ? "4" : "2",
@@ -956,11 +971,7 @@ const PrivateSewageFacilityInp = () => {
const baseRequest = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
};
try {
@@ -1062,9 +1073,8 @@ const PrivateSewageFacilityInp = () => {
const statsCd = tabStatsCdMap.get(activeTab);
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
@@ -1095,19 +1105,20 @@ const PrivateSewageFacilityInp = () => {
return map;
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo, columnGuidesByTab]);
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabToolbar =
toolbarSnapshotByTab.get(activeTab) ??
computeTabToolbarSnapshot(
activeTabStatus,
tabRowDataExists(rowDataMap, activeTab),
hasSearched,
lastSearchedValues,
searchContextForToolbar,
inputPeriodEnterable,
isAdminInfo,
);
const { showSaveInToolbar, showEnterableToolbarButtons, showActionToolbarCard } =
activeTabToolbar;
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const verificationCellStyle = ({
fieldName,
@@ -1166,6 +1177,7 @@ const PrivateSewageFacilityInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
onSubmit={handleSearch}
@@ -1186,10 +1198,10 @@ const PrivateSewageFacilityInp = () => {
<LastYearDataButton
getLastYearDataFunc={() => void lastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{activeTabStatus?.laeSbmsnSttsCd === "2" && (
{activeTabStatus?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -1225,7 +1237,7 @@ const PrivateSewageFacilityInp = () => {
tabStatusInfoMap.get(tabKey) ?? null,
inputPeriodEnterable,
) || isAdminInfo) && inputPeriodEnterable;
const gugunOk = buildGugunOkForToolbar(lastSearchedValues);
const gugunOk = buildGugunOkForToolbar(searchContextForToolbar);
const tabStatusInfo = tabStatusInfoMap.get(tabKey) ?? null;
return (
@@ -2,7 +2,7 @@
import { useConfirm } from '@frontend-opensource/use-react-hooks';
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from '@/shared/lib';
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate, useSearchConditionSidoList } from '@/shared/lib';
import { STATISTICS_INPUT_URL_PATH } from '@/shared/lib/stats-cd-input-navigation';
import { useStatisticsInputPendingNavigation } from '@/shared/lib/use-statistics-input-pending-navigation';
import { statisticsInputSewSupplyRateApi } from '@/entities/statistics/api/input/statistics-input-sew-supply-rate-service';
@@ -23,7 +23,9 @@ import {
type SewerageSupplyRateVerificationResult,
} from '@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface';
import type { StatisticsInputSubmitInfoFormData } from '@/entities/statistics/model/statistics-input-submit-info-schema';
import type { cancelSubmitRequest, ImrStatusResponse, SewerageStatusInfo } from '@/entities/statistics/model/statistics-interface';
import type { cancelSubmitRequest, ImrStatusResponse, SewerageStatusInfo, Sido } from '@/entities/statistics/model/statistics-interface';
import { filterBySelectedAdmdstCd, isGugunOkForToolbar, isSubmitOkForToolbar, resolveNoApplicableForRequest, toInputSearchRegion } from '@/entities/statistics';
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from '@common/components';
import { getUserInfo } from '../../../../../../packages/common/components/src/api';
@@ -45,7 +47,6 @@ import { VerificationResultDialog } from '@/components/module/VerificationResult
import { VerificationResultHistoryDialog } from '@/components/module/VerificationResultHistoryDialog';
import type { CellStyleCallbackParams, ColumnInfo, ColumnInfoList } from '@/components/type/DataGrid.type';
import type { SearchFormValues } from '@/components/type/SearchForm.type';
import { doList } from '@/components/util/column/input/DoList';
import { PublicSewageFactInpColumnList } from '@/components/util/column/input/PublicSewageFactInpColumn/PublicSewageFactInpColumn';
import { fetchColumnGuidesRecord } from '@/components/util/grid/columnGuides';
import { toGridRows } from '@/components/util/grid/gridFunction';
@@ -66,10 +67,22 @@ type TabToolbarSnapshot = {
showActionToolbarCard: boolean;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || '')
? Boolean(values?.gugun && values.gugun !== 'ALL')
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function buildNoApplicableForRequest(
values: SearchFormValues | null,
checked: boolean,
isSaveListEmpty: boolean,
): boolean {
return resolveNoApplicableForRequest(sidoListRef.current, values, checked, isSaveListEmpty);
}
const computeTabEnterable = (
@@ -181,6 +194,8 @@ function validatePrxyCtrtCnInRows(rows: Record<string, string | number>[]): stri
// 하수도 통계 관리 > 06. 공공하수처리시설
const PublicSewageFacilityInp = () => {
const { sidoList } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
@@ -209,6 +224,9 @@ const PublicSewageFacilityInp = () => {
/** 탭키별 `engColNm -> columnGuide` 캐시 */
const [columnGuidesByTab, setColumnGuidesByTab] = useState<Record<string, Record<string, string>>>({});
const [_toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<Map<string, TabToolbarSnapshot>>(() => new Map());
const [searchContextByTab, setSearchContextByTab] = useState<Map<string, SearchFormValues>>(
() => new Map(),
);
const [inputPeriodEnterable, setInputPeriodEnterable] = useState(true);
const [periodCheckList, setPeriodCheckList] = useState<CheckInputPeriodItem[]>([]);
const [showDailog, setShowDailog] = useState<boolean>(false);
@@ -247,10 +265,10 @@ const PublicSewageFacilityInp = () => {
alert('먼저 조회를 진행해주세요.');
return;
}
const treatAsNoApplicable = getNoApplicableForRequest() || isNotApplicable;
const treatAsNoApplicable = getNoApplicableForRequest();
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
if (!treatAsNoApplicable && (!currentData || currentData.length === 0)) {
if (!treatAsNoApplicable && (!currentData || currentData.length === 0) && buildSubmitOkForToolbar(searchContextByTab.get(activeTab) ?? lastSearchedValues)) {
alert('해당사항 없음을 체크해주세요.');
return;
}
@@ -298,13 +316,14 @@ const PublicSewageFacilityInp = () => {
};
const getNoApplicableForRequest = (): boolean => {
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const gugunOk = buildGugunOkForToolbar(searchContextForToolbar);
const enterable = computeTabEnterable(activeTabStatus, inputPeriodEnterable);
const showEnterableToolbarButtons = enterable && gugunOk;
const noApplicableVisible = showEnterableToolbarButtons && showIsNotApplicable;
return noApplicableVisible ? Boolean(isNotApplicable) : false;
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
return buildNoApplicableForRequest(
searchContextForToolbar,
Boolean(isNotApplicable),
buildFacilitySaveList(currentData).length === 0,
);
};
const isCheckedRow = (row: Record<string, string | number>): boolean => {
@@ -349,7 +368,7 @@ const PublicSewageFacilityInp = () => {
return;
}
const noApplicable = getNoApplicableForRequest();
const treatAsNoApplicable = noApplicable || isNotApplicable;
const treatAsNoApplicable = noApplicable;
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
@@ -360,7 +379,7 @@ const PublicSewageFacilityInp = () => {
}
const submitList = treatAsNoApplicable ? [] : buildFacilitySaveList(currentData);
if (!treatAsNoApplicable && submitList.length === 0) {
if (!treatAsNoApplicable && submitList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(activeTab) ?? lastSearchedValues)) {
alert('해당사항 없음을 체크해주세요.');
return;
}
@@ -373,8 +392,7 @@ const PublicSewageFacilityInp = () => {
const requestData = {
statsYr: lastSearchedValues.year ?? '',
ctpvNm: lastSearchedValues.sido ?? '',
sggNm: lastSearchedValues.gugun === 'ALL' ? '' : lastSearchedValues.gugun ?? '',
sewerBzmnCd: lastSearchedValues.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
userId: userInfo.userUnqId,
noApplicable,
laePicNm: submitInfo.laePicNm,
@@ -407,7 +425,7 @@ const PublicSewageFacilityInp = () => {
setGlobalProgressLabel('시계열 검증 진행 중...');
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? '',
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? '',
stats_cd: statsCdForTab,
stats_yr: lastSearchedValues.year ?? '',
lae_sbmsn_stts_cd: activeTabStatus?.laeSbmsnSttsCd === '4' ? '4' : '2',
@@ -435,9 +453,8 @@ const PublicSewageFacilityInp = () => {
);
await openVerificationResultDialogIfNeeded(
{
sewerBzmnCd: lastSearchedValues.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? '',
sggNm: lastSearchedValues.gugun === 'ALL' ? '' : (lastSearchedValues.gugun ?? ''),
statsYr: lastSearchedValues.year ?? '',
statsCd: statsCdForTab,
},
@@ -452,9 +469,8 @@ const PublicSewageFacilityInp = () => {
);
await openVerificationResultDialogIfNeeded(
{
sewerBzmnCd: lastSearchedValues.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? '',
sggNm: lastSearchedValues.gugun === 'ALL' ? '' : (lastSearchedValues.gugun ?? ''),
statsYr: lastSearchedValues.year ?? '',
statsCd: statsCdForTab,
},
@@ -476,9 +492,8 @@ const PublicSewageFacilityInp = () => {
);
await openVerificationResultDialogIfNeeded(
{
sewerBzmnCd: lastSearchedValues.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? '',
sggNm: lastSearchedValues.gugun === 'ALL' ? '' : (lastSearchedValues.gugun ?? ''),
statsYr: lastSearchedValues.year ?? '',
statsCd: statsCdForTab,
},
@@ -508,7 +523,7 @@ const PublicSewageFacilityInp = () => {
if (activeTab === 'ST00060002') {
const statusCodeResponse = await statisticsApi.CheckStatusCode({
sewerBzmnCd: lastSearchedValues.bsnmCd ?? '',
sewerBzmnCd: toInputSearchRegion(sidoListRef.current, lastSearchedValues).sewerBzmnCd,
statsCd: 'ST00060001',
statsYr: lastSearchedValues.year ?? ''
});
@@ -585,7 +600,7 @@ const PublicSewageFacilityInp = () => {
mainFacilityMappingSaveDraftRef.current = {
statsYr: lastSearchedValues.year ?? '',
sewerBzmnCd: lastSearchedValues.bsnmCd ?? '',
sewerBzmnCd: toInputSearchRegion(sidoListRef.current, lastSearchedValues).sewerBzmnCd,
admdstCd: String(selectedMain.admdstCd ?? ''),
admdstNm: String(selectedMain.admdstNm ?? ''),
mainFcltSn: String(selectedMain.fcltSn ?? ''),
@@ -616,7 +631,7 @@ const PublicSewageFacilityInp = () => {
if (activeTab === 'ST00060001') {
const noApplicable = getNoApplicableForRequest();
const treatAsNoApplicable = noApplicable || isNotApplicable;
const treatAsNoApplicable = noApplicable;
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
const prxyCtrtCnError = validatePrxyCtrtCnInRows(currentData);
@@ -625,7 +640,7 @@ const PublicSewageFacilityInp = () => {
return;
}
const saveList = treatAsNoApplicable ? [] : buildFacilitySaveList(currentData);
if (!treatAsNoApplicable && saveList.length === 0) {
if (!treatAsNoApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(activeTab) ?? lastSearchedValues)) {
alert('해당사항 없음을 체크해주세요.');
return;
}
@@ -636,8 +651,7 @@ const PublicSewageFacilityInp = () => {
const param: PublicSewageFacilitySaveRequest = {
statsYr: lastSearchedValues.year ?? '',
ctpvNm: lastSearchedValues.sido ?? '',
sggNm: lastSearchedValues.gugun === 'ALL' ? '' : lastSearchedValues.gugun ?? '',
sewerBzmnCd: lastSearchedValues.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
userId: userInfo.userUnqId,
...(isAdminInfo ? { superAdmin: 'superAdmin' } : {}),
noApplicable,
@@ -724,12 +738,18 @@ const PublicSewageFacilityInp = () => {
);
return next;
});
if (searchContext) {
setSearchContextByTab((prev) => {
const next = new Map(prev);
next.set(tabKey, searchContext);
return next;
});
}
};
const buildPublicSewageFacilityParams = (values: SearchFormValues): PublicSewageFacilityRequestParams => ({
ctpvNm: values.sido,
sggNm: values.gugun === 'ALL' ? '' : values.gugun,
sewerBzmnCd: values.bsnmCd,
...toInputSearchRegion(sidoListRef.current, values),
fcltNm: values.fctName,
statsYr: values.year,
});
@@ -759,7 +779,11 @@ const PublicSewageFacilityInp = () => {
const statusInfo = response.data.statusInfo ?? null;
const statsCd = response.data.commonInfo.statsCd || tabKey;
const commonInfo = response.data.commonInfo;
const list = response.data.publicSewageFacilityList ?? [];
const list = filterBySelectedAdmdstCd(
response.data.publicSewageFacilityList ?? [],
sidoListRef.current,
searchContext,
);
const convertedData = list.map((item) => ({
ctpvNm: commonInfo.ctpvNm,
...item,
@@ -872,8 +896,7 @@ const PublicSewageFacilityInp = () => {
const request = {
statsYr: lastSearchedValues.year ?? '',
ctpvNm: lastSearchedValues.sido ?? '',
sggNm: lastSearchedValues.gugun === 'ALL' ? '' : lastSearchedValues.gugun ?? '',
sewerBzmnCd: lastSearchedValues.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
userId: userInfo.userUnqId,
};
@@ -1005,6 +1028,7 @@ const PublicSewageFacilityInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
activeTabRef.current = firstTabKey;
setActiveTab(firstTabKey);
}
@@ -1069,6 +1093,7 @@ const PublicSewageFacilityInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setInputPeriodEnterable(true);
periodEnterableRef.current = true;
@@ -1095,9 +1120,8 @@ const PublicSewageFacilityInp = () => {
const userInfo = await getUserInfo();
const statsCdForCancel = tabStatsCdMap.get(activeTab) ?? activeTab;
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues?.sido ?? '',
sggNm: lastSearchedValues?.gugun === 'ALL' ? '' : lastSearchedValues?.gugun ?? '',
statsYr: lastSearchedValues?.year ?? '',
mdfrId: userInfo.userUnqId,
statsCd: statsCdForCancel,
@@ -1155,7 +1179,9 @@ const PublicSewageFacilityInp = () => {
const param: PublicSewageMappingTargetsRequestParams = {
sewerBzmnCd: searchParamRef.current.sewerBzmnCd ?? '',
ctpvNm: searchParamRef.current.ctpvNm ?? '',
sggNm: searchParamRef.current.sggNm ?? '',
...(searchParamRef.current.sggNm != null
? { sggNm: searchParamRef.current.sggNm }
: {}),
statsYr: searchParamRef.current.statsYr ?? '',
fcltSn,
};
@@ -1195,8 +1221,9 @@ const PublicSewageFacilityInp = () => {
return String(row.fcltSn ?? '') === selectedMainFacilitySn ? 'rg-cell-selected-row' : undefined;
};
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const gugunOkOrNotDoList = buildGugunOkForToolbar(searchContextForToolbar);
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabToolbar = computeTabToolbarSnapshot(
activeTabStatus,
@@ -1241,9 +1268,8 @@ const PublicSewageFacilityInp = () => {
const statsCd = tabStatsCdMap.get(activeTab) ?? activeTab;
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === 'ALL' ? '' : (ctx.gugun ?? ''),
statsYr: ctx.year,
statsCd,
};
@@ -1264,6 +1290,7 @@ const PublicSewageFacilityInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
fctName={true}
@@ -1284,12 +1311,12 @@ const PublicSewageFacilityInp = () => {
)}
{showEnterableToolbarButtons && !isMainFacilityTab && (
<>
{showIsNotApplicable && <NotApplicableCheckButton isCheck={isNotApplicable} setIsCheck={setIsNotApplicable} />}
{showIsNotApplicable && submitOk && <NotApplicableCheckButton isCheck={isNotApplicable} setIsCheck={setIsNotApplicable} />}
<UpdateOperateDataButton updateOperateDataButtonFunc={updateOperateDataButtonFunc} />
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{!isMainFacilityTab && activeTabStatus?.laeSbmsnSttsCd === '2' && (
{!isMainFacilityTab && activeTabStatus?.laeSbmsnSttsCd === '2' && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -12,7 +12,7 @@ import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { ValueType } from "realgrid";
import type { RealGridReact } from "realgrid-react";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate, useSearchConditionSidoList } from "@/shared/lib";
import { STATISTICS_INPUT_URL_PATH } from "@/shared/lib/stats-cd-input-navigation";
import { useStatisticsInputPendingNavigation } from "@/shared/lib/use-statistics-input-pending-navigation";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
@@ -35,7 +35,16 @@ import type {
ImrStatusResponse,
SewerageStatusInfo,
Sgg,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
resolveNoApplicableForRequest,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -61,7 +70,6 @@ import type {
ColumnConfig,
} from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { PumpingStationInpColumn } from "@/components/util/column/input/PumpingStationInpColumn";
import { toGridRows } from "@/components/util/grid/gridFunction";
import { formatCompactYmdForGrid, toCompactYmdOrNull } from "@/components/util/grid/ymdFormat";
@@ -94,10 +102,22 @@ const computeTabEnterable = (
return periodEnterable;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function buildNoApplicableForRequest(
values: SearchFormValues | null,
checked: boolean,
isSaveListEmpty: boolean,
): boolean {
return resolveNoApplicableForRequest(sidoListRef.current, values, checked, isSaveListEmpty);
}
function computeTabToolbarSnapshot(
@@ -131,9 +151,8 @@ function buildPumpingStationSearchParam(
const fcltNm = values.fctName?.trim();
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
...(fcltNm ? { fcltNm } : {}),
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
@@ -309,6 +328,8 @@ const PUMPING_STATION_FACILITY_NAME_GUIDE_MESSAGE =
// 하수도 통계 관리 > 05. 펌프장 (SewageReservoirPumpInp 단일 탭 패턴)
const PumpingStationInp = () => {
const { sidoList } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
@@ -460,7 +481,11 @@ const PumpingStationInp = () => {
const { commonInfo, statusInfo: nextStatusInfo, pumpingStationList } =
response.data;
const list = pumpingStationList ?? [];
const list = filterBySelectedAdmdstCd(
pumpingStationList ?? [],
sidoListRef.current,
values,
);
const gridRows = mapPumpingStationListToGridRows(list, commonInfo.ctpvNm);
setPumpingStationDeleteList([]);
@@ -578,12 +603,8 @@ const PumpingStationInp = () => {
const userInfo = await getUserInfo();
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCd || PUMPING_STATION_STATS_CD,
@@ -618,9 +639,9 @@ const PumpingStationInp = () => {
return;
}
const noApplicable = isNotApplicable;
const noApplicable = buildNoApplicableForRequest(lastSearchedValues, isNotApplicable, getCurrentRows().length === 0);
const currentData = getCurrentRows();
if (!noApplicable && currentData.length === 0) {
if (!noApplicable && currentData.length === 0 && buildSubmitOkForToolbar(lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -647,7 +668,7 @@ const PumpingStationInp = () => {
if (!(await confirm("저장 하시겠습니까?"))) return;
commitGrid();
const noApplicable = isNotApplicable;
const noApplicable = buildNoApplicableForRequest(lastSearchedValues, isNotApplicable, getCurrentRows().length === 0);
const currentData = getCurrentRows();
const saveList = noApplicable
? []
@@ -658,7 +679,7 @@ const PumpingStationInp = () => {
.filter((row) => String(row.fcltSn ?? "").trim() !== "")
.map((row) => buildPumpingStationSaveItem(row, sggOptions));
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -669,11 +690,7 @@ const PumpingStationInp = () => {
const response = await statisticsInputPumpingStationApi.savePumpingStationList({
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
userId: userInfo.userUnqId,
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
noApplicable,
@@ -707,7 +724,7 @@ const PumpingStationInp = () => {
}
commitGrid();
const noApplicable = isNotApplicable;
const noApplicable = buildNoApplicableForRequest(lastSearchedValues, isNotApplicable, getCurrentRows().length === 0);
const currentData = getCurrentRows();
const submitList = noApplicable
? []
@@ -716,19 +733,15 @@ const PumpingStationInp = () => {
.filter((row) => String(row.fcltSn ?? "").trim() !== "")
.map((row) => buildPumpingStationSaveItem(row, sggOptions));
if (!noApplicable && submitList.length === 0) {
if (!noApplicable && submitList.length === 0 && buildSubmitOkForToolbar(lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
const statsCdForSubmit = statsCd || PUMPING_STATION_STATS_CD;
const verificationQuery = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForSubmit,
};
@@ -745,11 +758,7 @@ const PumpingStationInp = () => {
const response = await statisticsInputPumpingStationApi.submitPumpingStationList({
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
userId: userInfo.userUnqId,
laePicNm: submitInfo.laePicNm,
laePicTelNo: submitInfo.laePicTelNo,
@@ -778,7 +787,7 @@ const PumpingStationInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? "",
stats_cd: statsCdForSubmit,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd:
@@ -942,7 +951,7 @@ const PumpingStationInp = () => {
}
};
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const searchContextForToolbar = lastSearchedValues;
const toolbarSnapshot = computeTabToolbarSnapshot(
statusInfo,
rowData.length > 0,
@@ -962,23 +971,23 @@ const PumpingStationInp = () => {
hasSearched &&
toolbarGroupAllowed &&
(showSaveInToolbar || showEnterableToolbarButtons);
const tabEnterableForGrid =
(computeTabEnterable(statusInfo, inputPeriodEnterable) || isAdminInfo) && inputPeriodEnterable;
const gugunOk = buildGugunOkForToolbar(searchContextForToolbar);
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const showNotApplicable =
searchLoaded &&
hasSearched &&
rowData.length === 0 &&
computeTabEnterable(statusInfo, inputPeriodEnterable);
const tabEnterableForGrid =
(computeTabEnterable(statusInfo, inputPeriodEnterable) || isAdminInfo) && inputPeriodEnterable;
const gugunOk = buildGugunOkForToolbar(lastSearchedValues);
computeTabEnterable(statusInfo, inputPeriodEnterable) &&
submitOk;
const verificationQueryContext = (() => {
const ctx = lastSearchedValues ?? searchValues;
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
@@ -994,6 +1003,7 @@ const PumpingStationInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
fctName={true}
@@ -1013,10 +1023,10 @@ const PumpingStationInp = () => {
<LastYearDataButton
getLastYearDataFunc={() => void getLastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{statusInfo?.laeSbmsnSttsCd === "2" && (
{statusInfo?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -5,7 +5,7 @@ import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { ValueType } from "realgrid";
import type { RealGridReact } from "realgrid-react";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate, useSearchConditionSidoList } from "@/shared/lib";
import { STATISTICS_INPUT_URL_PATH } from "@/shared/lib/stats-cd-input-navigation";
import { useStatisticsInputPendingNavigation } from "@/shared/lib/use-statistics-input-pending-navigation";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
@@ -34,7 +34,16 @@ import type {
ImrStatusResponse,
SewerageStatusInfo,
Sgg,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
resolveNoApplicableForRequest,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -63,7 +72,6 @@ import type {
ColumnInfo,
} from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { ExcretaRelatedIndustryInpColumn } from "@/components/util/column/input/sewageExcretaRelated/ExcretaRelatedIndustryInpColumn";
import { PrivateSewageTreatRelatedIndustryInpColumn } from "@/components/util/column/input/sewageExcretaRelated/PrivateSewageTreatRelatedIndustryInpColumn";
import { PublicInstitutionExcretaIndustryInpColumn } from "@/components/util/column/input/sewageExcretaRelated/PublicInstitutionExcretaIndustryInpColumn";
@@ -158,10 +166,22 @@ const computeTabEnterable = (
return periodEnterable;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function buildNoApplicableForRequest(
values: SearchFormValues | null,
checked: boolean,
isSaveListEmpty: boolean,
): boolean {
return resolveNoApplicableForRequest(sidoListRef.current, values, checked, isSaveListEmpty);
}
function computeTabToolbarSnapshot(
@@ -543,9 +563,8 @@ function buildSewageExcretaRelatedSearchParam(
): SewageExcretaRelatedRequestParams {
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
}
@@ -553,8 +572,9 @@ function buildSewageExcretaRelatedSearchParam(
function mapListToGridRows<T extends { verificationResult?: SewageExcretaRelatedVerificationResult }>(
list: T[] | undefined,
ctpvNm: string,
values?: SearchFormValues | null,
): GridRowWithVerification[] {
return (list ?? []).map((item) => {
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const row = toGridRows([{ ctpvNm, ...item }])[0] as GridRowWithVerification;
if (item.verificationResult) {
row.verificationResult = item.verificationResult;
@@ -590,6 +610,8 @@ async function waitForImrJobCompleted(
// 하수도 통계 관리 > 12. 하수 및 분뇨관련산업
const SewageExcretaRelatedInp = () => {
const { sidoList } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const isAdminInfo = useAdminStore((state) => state.isAdminInfo);
@@ -606,7 +628,7 @@ const SewageExcretaRelatedInp = () => {
const [tabStatusInfoMap, setTabStatusInfoMap] = useState<
Map<string, SewerageStatusInfo | null>
>(new Map());
const [toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
const [_toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
Map<string, TabToolbarSnapshot>
>(new Map());
const [columnGuidesByTab, setColumnGuidesByTab] = useState<
@@ -616,6 +638,9 @@ const SewageExcretaRelatedInp = () => {
const [lastSearchedValues, setLastSearchedValues] = useState<SearchFormValues | null>(
null,
);
const [searchContextByTab, setSearchContextByTab] = useState<Map<string, SearchFormValues>>(
() => new Map(),
);
const [hasSearched, setHasSearched] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [inputPeriodEnterable, setInputPeriodEnterable] = useState(true);
@@ -739,6 +764,13 @@ const SewageExcretaRelatedInp = () => {
);
return next;
});
if (searchContext) {
setSearchContextByTab((prev) => {
const next = new Map(prev);
next.set(tabKey, searchContext);
return next;
});
}
};
const markTabLoaded = (tabKey: string) => {
@@ -854,6 +886,7 @@ const SewageExcretaRelatedInp = () => {
const gridRows = mapListToGridRows(
publicInstitutionSewerIndustryList,
commonInfo.ctpvNm,
searchContext,
);
syncPublicInstitutionArticleTracking(
tabKey,
@@ -900,6 +933,7 @@ const SewageExcretaRelatedInp = () => {
const gridRows = mapListToGridRows(
publicInstitutionExcretaIndustryList,
commonInfo.ctpvNm,
searchContext,
);
syncPublicInstitutionArticleTracking(
tabKey,
@@ -942,7 +976,7 @@ const SewageExcretaRelatedInp = () => {
return false;
}
const { commonInfo, statusInfo, excretaRelatedIndustryList } = response.data;
const gridRows = mapListToGridRows(excretaRelatedIndustryList, commonInfo.ctpvNm);
const gridRows = mapListToGridRows(excretaRelatedIndustryList, commonInfo.ctpvNm, searchContext);
applyRowMap(mergeIntoExistingMap, (m) => {
m.set(ExcretaRelatedIndustryInpColumn.tabKey, gridRows);
});
@@ -979,6 +1013,7 @@ const SewageExcretaRelatedInp = () => {
const gridRows = mapListToGridRows(
privateSewageTreatRelatedIndustryList,
commonInfo.ctpvNm,
searchContext,
);
applyRowMap(mergeIntoExistingMap, (m) => {
m.set(PrivateSewageTreatRelatedIndustryInpColumn.tabKey, gridRows);
@@ -1010,6 +1045,7 @@ const SewageExcretaRelatedInp = () => {
setRowDataMap(new Map());
setTabStatusInfoMap(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setActiveTab(firstTabKey);
activeTabRef.current = firstTabKey;
setLastSearchedValues(null);
@@ -1075,6 +1111,7 @@ const SewageExcretaRelatedInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setIsNotApplicableByTab(new Map());
setDeleteListByTab(new Map());
clearArticleOgnzSnTracking(
@@ -1139,9 +1176,8 @@ const SewageExcretaRelatedInp = () => {
const statsCdForTab = tabStatsCdMap.get(tabKey) ?? tabKey;
const requestData: cancelSubmitRequest = {
sewerBzmnCd: ctx.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido ?? "",
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCdForTab,
@@ -1232,10 +1268,10 @@ const SewageExcretaRelatedInp = () => {
}
commitActiveGrid();
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
if (NOT_APPLICABLE_TAB_KEYS.has(tabKey)) {
if (!noApplicable && currentData.length === 0) {
if (!noApplicable && currentData.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1269,18 +1305,14 @@ const SewageExcretaRelatedInp = () => {
const isDistrictRowTab = DISTRICT_ROW_TAB_KEYS.has(tabKey);
const isPublicInstitutionTab = ARTICLE_EDIT_TAB_KEYS.has(tabKey);
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const mapRows = rowDataMap.get(tabKey) ?? [];
const searchContextBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
};
let submitList:
@@ -1341,7 +1373,7 @@ const SewageExcretaRelatedInp = () => {
)
.map((row) => buildPublicInstitutionIndustrySaveItem(row, list));
if (!noApplicable && submitList.length === 0) {
if (!noApplicable && submitList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1448,7 +1480,7 @@ const SewageExcretaRelatedInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? "",
stats_cd: statsCdForTab,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd:
@@ -1527,18 +1559,14 @@ const SewageExcretaRelatedInp = () => {
const isDistrictRowTab = DISTRICT_ROW_TAB_KEYS.has(tabKey);
const isPublicInstitutionTab = ARTICLE_EDIT_TAB_KEYS.has(tabKey);
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const mapRows = rowDataMap.get(tabKey) ?? [];
const baseSaveRequest = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
...(isPublicInstitutionTab && noApplicable
? { noApplicable: true as const }
: {}),
@@ -1621,7 +1649,7 @@ const SewageExcretaRelatedInp = () => {
)
.map((row) => buildPublicInstitutionIndustrySaveItem(row, list));
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1885,11 +1913,10 @@ const SewageExcretaRelatedInp = () => {
[publicInstitutionArticleCellStyleCallback],
);
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabHasRowData = tabRowDataExists(rowDataMap, activeTab);
const activeTabToolbar =
toolbarSnapshotByTab.get(activeTab) ??
computeTabToolbarSnapshot(
activeTabStatus,
activeTabHasRowData,
@@ -1907,10 +1934,13 @@ const SewageExcretaRelatedInp = () => {
(activeTabToolbar.showActionToolbarCard || (hasSearched && loadedTabKeys.has(activeTab)));
const showArticleEditButtons =
ARTICLE_EDIT_TAB_KEYS.has(activeTab) && showEnterableToolbarButtons;
const gugunOk = buildGugunOkForToolbar(searchContextForToolbar);
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const showNotApplicableForActiveTab =
NOT_APPLICABLE_TAB_KEYS.has(activeTab) &&
loadedTabKeys.has(activeTab) &&
shouldShowNotApplicable(activeTab, activeTabHasRowData, activeTabStatus);
shouldShowNotApplicable(activeTab, activeTabHasRowData, activeTabStatus) &&
submitOk;
const showSecondaryToolbarCard =
hasSearched &&
toolbarGroupAllowed &&
@@ -1928,16 +1958,14 @@ const SewageExcretaRelatedInp = () => {
},
[isNotApplicableByTab, setIsNotApplicableForTab],
);
const gugunOk = buildGugunOkForToolbar(lastSearchedValues);
const verificationQueryContext = (() => {
const ctx = lastSearchedValues ?? searchValues;
const statsCd = tabStatsCdMap.get(activeTab) ?? activeTab;
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
@@ -1958,6 +1986,7 @@ const SewageExcretaRelatedInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
onSubmit={handleSearch}
@@ -1978,10 +2007,10 @@ const SewageExcretaRelatedInp = () => {
<LastYearDataButton
getLastYearDataFunc={() => void lastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{activeTabStatus?.laeSbmsnSttsCd === "2" && (
{activeTabStatus?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -13,7 +13,7 @@ import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { ValueType } from "realgrid";
import type { RealGridReact } from "realgrid-react";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate, useSearchConditionSidoList } from "@/shared/lib";
import { STATISTICS_INPUT_URL_PATH } from "@/shared/lib/stats-cd-input-navigation";
import { useStatisticsInputPendingNavigation } from "@/shared/lib/use-statistics-input-pending-navigation";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
@@ -37,7 +37,15 @@ import {
} from "@/entities/statistics/model/input/statistics-input-sewage-excreta-slurge-interface";
import { isInputPeriodCurrent } from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface";
import type { StatisticsInputSubmitInfoFormData } from "@/entities/statistics/model/statistics-input-submit-info-schema";
import type { ApiResponse, cancelSubmitRequest, ImrStatusResponse, SewerageStatusInfo, Sgg } from "@/entities/statistics/model/statistics-interface";
import type { ApiResponse, cancelSubmitRequest, ImrStatusResponse, SewerageStatusInfo, Sgg, Sido } from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
resolveNoApplicableForRequest,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -67,7 +75,6 @@ import type {
ColumnInfo,
} from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import {
DIGESTOR_OPERATING_STATUS_EN_YN_FIELD,
DigestorOperatingStatusInpColumn,
@@ -163,9 +170,8 @@ function buildSewageExcretaSlurgeSearchParam(
): SewageExcretaSlurgeRequestParams {
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
fcltNm: values.fctName?.trim() || undefined,
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
@@ -174,8 +180,9 @@ function buildSewageExcretaSlurgeSearchParam(
function mapListToGridRows<T extends { verificationResult?: SewageExcretaSlurgeVerificationResult }>(
list: T[] | undefined,
ctpvNm: string,
values?: SearchFormValues | null,
): GridRowWithVerification[] {
return (list ?? []).map((item) => {
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const row = toGridRows([{ ctpvNm, ...item }])[0] as GridRowWithVerification;
if (item.verificationResult) {
row.verificationResult = item.verificationResult;
@@ -345,10 +352,22 @@ const computeTabEnterable = (
return periodEnterable;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function buildNoApplicableForRequest(
values: SearchFormValues | null,
checked: boolean,
isSaveListEmpty: boolean,
): boolean {
return resolveNoApplicableForRequest(sidoListRef.current, values, checked, isSaveListEmpty);
}
function computeTabToolbarSnapshot(
@@ -491,7 +510,7 @@ function buildSlurgeProcessingFacStatusSaveItem(
itslfTs1AltdQnt: toGridNumber(row.itslfTs1AltdQnt),
itslfTs1DryQnt: toGridNumber(row.itslfTs1DryQnt),
itslfTs1FuelQnt: toGridNumber(row.itslfTs1FuelQnt),
itslfTs2CmpstgQnt: toGridNumber(row.itslfTs2CmpstgQnt),
itslfTs1CmpstgQnt: toGridNumber(row.itslfTs1CmpstgQnt),
cmpsPrcsFcltBzentyNm: nullableTrimmedString(row.cmpsPrcsFcltBzentyNm),
incnrPrcsLfvlm: toGridNumber(row.incnrPrcsLfvlm),
incnrPrcsPrdctRmtrlQnt: toGridNumber(row.incnrPrcsPrdctRmtrlQnt),
@@ -644,6 +663,8 @@ async function waitForImrJobCompleted(
// 하수도 통계 관리 > 11. 하수 및 분뇨찌꺼기(슬러지)처리
const SewageExcretaSlurgeInp = () => {
const { sidoList } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { isAdminInfo } = useAdminStore();
const { confirm } = useConfirm();
const { setMessage: alert } = useAlertStore((state) => state);
@@ -678,6 +699,9 @@ const SewageExcretaSlurgeInp = () => {
const [_toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
Map<string, TabToolbarSnapshot>
>(new Map());
const [searchContextByTab, setSearchContextByTab] = useState<Map<string, SearchFormValues>>(
() => new Map(),
);
const [sggOptions, setSggOptions] = useState<Sgg[]>([]);
const [loadedTabKeys, setLoadedTabKeys] = useState<Set<string>>(() => new Set());
const [verificationHistoryOpen, setVerificationHistoryOpen] = useState(false);
@@ -790,6 +814,13 @@ const SewageExcretaSlurgeInp = () => {
);
return next;
});
if (searchContext) {
setSearchContextByTab((prev) => {
const next = new Map(prev);
next.set(tabKey, searchContext);
return next;
});
}
};
const getTabGridRows = (tabKey: string): Record<string, string | number>[] =>
@@ -923,7 +954,7 @@ const SewageExcretaSlurgeInp = () => {
return false;
}
const { commonInfo, statusInfo, slurgeProcessingFacStatusList } = response.data;
const gridRows = mapListToGridRows(slurgeProcessingFacStatusList, commonInfo.ctpvNm);
const gridRows = mapListToGridRows(slurgeProcessingFacStatusList, commonInfo.ctpvNm, searchContext);
syncSlurgeFacStatusArticleFcltSnTracking(gridRows, fromPreYear);
applyRowMap(mergeIntoExistingMap, (m) => {
m.set(SlurgeProcessingFacStatusInpColumn.tabKey, gridRows);
@@ -958,7 +989,7 @@ const SewageExcretaSlurgeInp = () => {
return false;
}
const { commonInfo, statusInfo, digestorOperatingStatusList } = response.data;
const gridRows = mapListToGridRows(digestorOperatingStatusList, commonInfo.ctpvNm);
const gridRows = mapListToGridRows(digestorOperatingStatusList, commonInfo.ctpvNm, searchContext);
if (!fromPreYear) {
publicSewageFacilitiesAvailableRef.current = gridRows.length > 0;
if (gridRows.length === 0 && overrides?.alertIfDigestorEmpty) {
@@ -1002,6 +1033,7 @@ const SewageExcretaSlurgeInp = () => {
const gridRows = mapListToGridRows(
sewageSlurgeProcessingFacStatusList,
commonInfo.ctpvNm,
searchContext,
);
applyRowMap(mergeIntoExistingMap, (m) => {
m.set(SewageSlurgeProcessingFacStatusInpColumn.tabKey, gridRows);
@@ -1039,6 +1071,7 @@ const SewageExcretaSlurgeInp = () => {
const gridRows = mapListToGridRows(
excretaSlurgeProcessingStatusList,
commonInfo.ctpvNm,
searchContext,
);
if (!fromPreYear) {
excretaTreatmentFacilitiesAvailableRef.current = gridRows.length > 0;
@@ -1080,6 +1113,7 @@ const SewageExcretaSlurgeInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setActiveTab(firstTabKey);
activeTabRef.current = firstTabKey;
setLastSearchedValues(null);
@@ -1139,6 +1173,7 @@ const SewageExcretaSlurgeInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
loadedTabKeysRef.current.clear();
setLoadedTabKeys(new Set());
setDeleteListByTab(new Map());
@@ -1247,9 +1282,9 @@ const SewageExcretaSlurgeInp = () => {
}
commitActiveGrid();
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
if (!noApplicable && currentData.length === 0) {
if (!noApplicable && currentData.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1277,7 +1312,7 @@ const SewageExcretaSlurgeInp = () => {
commitActiveGrid();
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const mapRows = rowDataMap.get(tabKey) ?? [];
@@ -1335,6 +1370,7 @@ const SewageExcretaSlurgeInp = () => {
if (
!noApplicable &&
buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues) &&
((isSlurgeFacTab && slurgeSubmitList.length === 0) ||
(isDigestorTab && digestorSubmitList.length === 0) ||
(isSewageSlurgeFacTab && sewageSlurgeFacSubmitList.length === 0) ||
@@ -1356,12 +1392,8 @@ const SewageExcretaSlurgeInp = () => {
: SEWAGE_EXCRETA_SLURGE_PROCESSING_FAC_STATUS_STATS_CD);
const verificationQuery = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForSubmit,
};
@@ -1390,11 +1422,7 @@ const SewageExcretaSlurgeInp = () => {
const submitRequestBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
userId: "",
laePicNm: submitInfo.laePicNm,
laePicTelNo: submitInfo.laePicTelNo,
@@ -1455,7 +1483,7 @@ const SewageExcretaSlurgeInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? "",
stats_cd: statsCdForSubmit,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd:
@@ -1532,12 +1560,8 @@ const SewageExcretaSlurgeInp = () => {
}
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCdForTab,
@@ -1585,7 +1609,7 @@ const SewageExcretaSlurgeInp = () => {
commitActiveGrid();
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const mapRows = rowDataMap.get(tabKey) ?? [];
@@ -1603,11 +1627,7 @@ const SewageExcretaSlurgeInp = () => {
const baseSaveRequest = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
...(noApplicable ? { noApplicable: true } : {}),
};
@@ -1638,7 +1658,7 @@ const SewageExcretaSlurgeInp = () => {
.filter((row) => isPersistedArticleRowForTab(tabKey, row))
.map((row) => buildSlurgeProcessingFacStatusSaveItem(row, list));
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1665,7 +1685,7 @@ const SewageExcretaSlurgeInp = () => {
buildDigestorOperatingStatusSaveItem(row, list),
);
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1691,7 +1711,7 @@ const SewageExcretaSlurgeInp = () => {
.filter((row) => isPersistedArticleRowForTab(tabKey, row))
.map((row) => buildSewageSlurgeProcessingFacStatusSaveItem(row, list));
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1718,7 +1738,7 @@ const SewageExcretaSlurgeInp = () => {
buildExcretaSlurgeProcessingStatusSaveItem(row, list),
);
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1977,7 +1997,7 @@ const SewageExcretaSlurgeInp = () => {
[digestorOperatingCellStyleCallback],
);
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabHasRowData = tabRowDataExists(rowDataMap, activeTab);
const activeTabToolbar = computeTabToolbarSnapshot(
@@ -1998,6 +2018,7 @@ const SewageExcretaSlurgeInp = () => {
const showSaveInToolbar = baseShowSaveInToolbar && toolbarGroupAllowed;
const showEnterableToolbarButtons =
baseShowEnterableToolbarButtons && toolbarGroupAllowed;
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const showActionToolbarCard =
toolbarGroupAllowed &&
(baseShowActionToolbarCard || (hasSearched && loadedTabKeys.has(activeTab)));
@@ -2005,7 +2026,8 @@ const SewageExcretaSlurgeInp = () => {
ARTICLE_EDIT_TAB_KEYS.has(activeTab) && showEnterableToolbarButtons;
const showNotApplicableForActiveTab =
loadedTabKeys.has(activeTab) &&
shouldShowNotApplicable(activeTabHasRowData, activeTabStatus);
shouldShowNotApplicable(activeTabHasRowData, activeTabStatus) &&
submitOk;
const showSecondaryToolbarCard =
hasSearched &&
toolbarGroupAllowed &&
@@ -2039,9 +2061,8 @@ const SewageExcretaSlurgeInp = () => {
const statsCd = tabStatsCdMap.get(activeTab);
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
@@ -2062,6 +2083,7 @@ const SewageExcretaSlurgeInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
fctName
@@ -2083,10 +2105,10 @@ const SewageExcretaSlurgeInp = () => {
<LastYearDataButton
getLastYearDataFunc={() => void lastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{activeTabStatus?.laeSbmsnSttsCd === "2" && (
{activeTabStatus?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -2,7 +2,7 @@ import { useEffect, useLayoutEffect,useMemo, useRef, useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate, useSearchConditionSidoList } from "@/shared/lib";
import {
STATISTICS_INPUT_URL_PATH,
verificationSearchToFormValues,
@@ -40,7 +40,15 @@ import type {
ImrStatusResponse,
SewerageStatusInfo,
SubmitExceptionRequest,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -61,7 +69,6 @@ import { VerificationResultDialog } from "@/components/module/VerificationResult
import { VerificationResultHistoryDialog } from "@/components/module/VerificationResultHistoryDialog";
import type { ColumnConfig, ColumnInfo, ColumnInfoList, ColumnLayoutItem } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { FactInstallStatusInpColumn } from "@/components/util/column/input/sewagePipe/FactInstallStatusInpColumn";
import { ManholeStatusInpColumn } from "@/components/util/column/input/sewagePipe/ManholeStatusInpColumn";
import { PipeDiameterStatusInpColumn } from "@/components/util/column/input/sewagePipe/PipeDiameterStatusInpColumn";
@@ -101,9 +108,8 @@ function buildSewagePipeSearchParam(
): SeweragePipeRequestParams {
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
}
@@ -431,10 +437,14 @@ type TabToolbarSnapshot = {
showActionToolbarCard: boolean;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
/**
@@ -466,6 +476,8 @@ function computeTabToolbarSnapshot(
// 하수도 통계 관리 > 02. 하수관로
const SewagePipeInp = () => {
const { sidoList, isReady } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const { setMessage: alert } = useAlertStore((state) => state);
const [activeTab, setActiveTab] = useState<string>(SewagePipeInpColumnInfoList[0]?.tabKey ?? "ST00020001");
@@ -494,7 +506,10 @@ const SewagePipeInp = () => {
/** checkInputPeriod — columnInfo useMemo가 입력기간 변경을 반영하도록 state로도 유지 */
const [inputPeriodEnterable, setInputPeriodEnterable] = useState(true);
/** 탭별 조회 시점의 상단 버튼 노출 조건(해당 탭을 다시 조회할 때만 그 탭 항목 갱신) */
const [toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<Map<string, TabToolbarSnapshot>>(
const [_toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<Map<string, TabToolbarSnapshot>>(
() => new Map(),
);
const [searchContextByTab, setSearchContextByTab] = useState<Map<string, SearchFormValues>>(
() => new Map(),
);
const { confirm } = useConfirm();
@@ -539,6 +554,7 @@ const SewagePipeInp = () => {
const inputFocusNonce = useStatisticsInputFocusStore((s) => s.nonce);
useLayoutEffect(() => {
if (activeTabId !== STATISTICS_INPUT_URL_PATH.SEWAGE_PIPE) return;
if (!isReady) return;
const pending = useStatisticsInputFocusStore
.getState()
.takePendingForUrl(STATISTICS_INPUT_URL_PATH.SEWAGE_PIPE);
@@ -547,12 +563,12 @@ const SewagePipeInp = () => {
activeTabRef.current = tabKey;
setActiveTab(tabKey);
if (search) {
const values = verificationSearchToFormValues(search);
const values = verificationSearchToFormValues(search, sidoList);
setVerificationSearchDefaults(values);
postVerificationSearchRef.current = { values, tabKey };
setSearchFormRemountKey((k) => k + 1);
}
}, [activeTabId, inputFocusNonce]);
}, [activeTabId, inputFocusNonce, isReady, sidoList]);
useEffect(() => {
if (activeTabId !== STATISTICS_INPUT_URL_PATH.SEWAGE_PIPE) return;
@@ -592,6 +608,7 @@ const SewagePipeInp = () => {
setHasSearched(false);
setInputPeriodEnterable(true);
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
searchParamRef.current = null;
searchDoneRef.current = false;
lazyTabLoggedRef.current.clear();
@@ -681,6 +698,13 @@ const SewagePipeInp = () => {
);
return next;
});
if (searchContext) {
setSearchContextByTab((prev) => {
const next = new Map(prev);
next.set(tabKey, searchContext);
return next;
});
}
};
/** mergeIntoExistingMap: true면 기존 맵에 병합(다른 탭 rowData·검색 후 다른 탭 캐시 유지). false면 맵 전체 교체 */
@@ -694,7 +718,7 @@ const SewagePipeInp = () => {
const searchConditionKey = JSON.stringify({
statsYr: searchParam.statsYr ?? "",
ctpvNm: searchParam.ctpvNm ?? "",
sggNm: searchParam.sggNm ?? "",
sggNm: values?.gugun === "ALL" ? "" : (values?.gugun ?? searchParam.sggNm ?? ""),
sewerBzmnCd: searchParam.sewerBzmnCd ?? "",
});
const applyRowMap = (apply: (m: Map<string, Record<string, string | number>[]>) => void) => {
@@ -710,12 +734,14 @@ const SewagePipeInp = () => {
setRowDataMap(next);
}
};
const filterFetched = <T,>(list: readonly T[] | null | undefined) =>
filterBySelectedAdmdstCd(list, sidoListRef.current, values);
if (tabKey === 'ST00020001') {
const response = await statisticsInputSewPipeApiService.getRegionalFactStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.regionalFactStatusList ?? []).map((d) => ({
const convertedData = filterFetched(response.data.regionalFactStatusList).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
@@ -745,7 +771,7 @@ const SewagePipeInp = () => {
const response = await statisticsInputSewPipeApiService.getFactInstallStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.factInstallStatusList ?? []).map((d) => ({
const convertedData = filterFetched(response.data.factInstallStatusList).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
@@ -792,7 +818,7 @@ const SewagePipeInp = () => {
const response = await statisticsInputSewPipeApiService.getPipeTypeStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.pipeTypeStatusList ?? []).map((d) =>
const convertedData = filterFetched(response.data.pipeTypeStatusList).map((d) =>
normalizePipeTypeStatusRow(d, commonInfo.ctpvNm),
);
applyRowMap((m) => m.set('ST00020003', toGridRows(convertedData)));
@@ -825,7 +851,7 @@ const SewagePipeInp = () => {
const response = await statisticsInputSewPipeApiService.getPipeDiameterStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.pipeDiameterStatusList ?? []).map((d) => ({
const convertedData = filterFetched(response.data.pipeDiameterStatusList).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
@@ -855,7 +881,7 @@ const SewagePipeInp = () => {
const response = await statisticsInputSewPipeApiService.getManholeStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.manholeStatusList ?? []).map((d) =>
const convertedData = filterFetched(response.data.manholeStatusList).map((d) =>
normalizeManholeStatusRow(d, commonInfo.ctpvNm),
);
applyRowMap((m) => m.set('ST00020005', toGridRows(convertedData)));
@@ -884,10 +910,10 @@ const SewagePipeInp = () => {
const response = await statisticsInputSewPipeApiService.getConnectionStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const facilityConvertedData = (response.data.facilityConnectionList ?? []).map((d) => ({
const facilityConvertedData = filterFetched(response.data.facilityConnectionList).map((d) => ({
...d,
}));
const adminConvertedData = (response.data.adminAreaConnectionList ?? []).map((d) => ({
const adminConvertedData = filterFetched(response.data.adminAreaConnectionList).map((d) => ({
...d,
ctpvNm: d.ctpvNm || commonInfo.ctpvNm,
}));
@@ -958,6 +984,7 @@ const SewagePipeInp = () => {
setFooterVerificationByTab(new Map());
setStatsCd("");
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
}
lazyTabLoggedRef.current.clear();
@@ -1119,9 +1146,8 @@ const SewagePipeInp = () => {
const params: SubmitExceptionRequest = {
...submitInfo,
sewerBzmnCd: searchValues?.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues?.sido ?? "",
sggNm: searchValues?.gugun === "ALL" ? "" : (searchValues?.gugun ?? ""),
statsYr: searchValues?.year ?? "",
userId: userInfo.userUnqId,
statsCd: statsCdForTab,
@@ -1204,8 +1230,7 @@ const SewagePipeInp = () => {
const submitBase = {
statsYr: values.year ?? "",
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
sewerBzmnCd: values.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, values),
userId: userInfo.userUnqId,
laePicNm: submitInfo.laePicNm,
laeKbrdrJbgdNm: submitInfo.laeKbrdrJbgdNm,
@@ -1463,7 +1488,7 @@ const SewagePipeInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: values.bsnmCd ?? "",
sewer_bzmn_cd: values?.bsnmCd ?? '',
stats_cd: statsCdForTab,
stats_yr: values.year ?? "",
lae_sbmsn_stts_cd: statusForTab?.laeSbmsnSttsCd === "4" ? "4" : "2",
@@ -1493,9 +1518,8 @@ const SewagePipeInp = () => {
);
await openVerificationResultDialogIfNeeded(
{
sewerBzmnCd: values.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, values),
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
statsYr: values.year ?? "",
statsCd: statsCdForTab,
},
@@ -1512,9 +1536,8 @@ const SewagePipeInp = () => {
);
await openVerificationResultDialogIfNeeded(
{
sewerBzmnCd: values.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, values),
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
statsYr: values.year ?? "",
statsCd: statsCdForTab,
},
@@ -1538,9 +1561,8 @@ const SewagePipeInp = () => {
);
await openVerificationResultDialogIfNeeded(
{
sewerBzmnCd: values.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, values),
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
statsYr: values.year ?? "",
statsCd: statsCdForTab,
},
@@ -1567,9 +1589,8 @@ const SewagePipeInp = () => {
const userInfo = await getUserInfo();
const requestData: cancelSubmitRequest = {
sewerBzmnCd: searchValues?.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues?.sido ?? '',
sggNm: searchValues?.gugun === "ALL" ? "" : (searchValues?.gugun ?? ""),
statsYr: searchValues?.year ?? '',
mdfrId: userInfo.userUnqId,
statsCd: tabStatsCdMap.get(activeTab) ?? statsCd,
@@ -1699,8 +1720,7 @@ const SewagePipeInp = () => {
const param: SeweragePipeInputRegionalFactStatusSaveRequest = {
statsYr: values.year ?? "",
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
sewerBzmnCd: values.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, values),
userId: userInfo.userUnqId,
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
saveList,
@@ -1779,8 +1799,7 @@ const SewagePipeInp = () => {
const param: SeweragePipeInputFactInstallStatusSaveRequest = {
statsYr: values.year ?? "",
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
sewerBzmnCd: values.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, values),
userId: userInfo.userUnqId,
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
saveList,
@@ -1864,8 +1883,7 @@ const SewagePipeInp = () => {
const param: SeweragePipeInputPipeTypeStatusSaveRequest = {
statsYr: values.year ?? "",
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
sewerBzmnCd: values.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, values),
userId: userInfo.userUnqId,
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
saveList,
@@ -1927,8 +1945,7 @@ const SewagePipeInp = () => {
const param: SeweragePipeInputPipeDiameterStatusSaveRequest = {
statsYr: values.year ?? "",
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
sewerBzmnCd: values.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, values),
userId: userInfo.userUnqId,
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
saveList,
@@ -2006,8 +2023,7 @@ const SewagePipeInp = () => {
const param: SeweragePipeInputManholeStatusSaveRequest = {
statsYr: values.year ?? "",
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
sewerBzmnCd: values.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, values),
userId: userInfo.userUnqId,
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
saveList,
@@ -2036,12 +2052,12 @@ const SewagePipeInp = () => {
}
};
const gugunOkOrNotDoList = buildGugunOkForToolbar(lastSearchedValues);
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const gugunOkOrNotDoList = buildGugunOkForToolbar(searchContextForToolbar);
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabToolbar =
toolbarSnapshotByTab.get(activeTab) ??
computeTabToolbarSnapshot(
activeTabStatus,
tabRowDataExists(rowDataMap, activeTab),
@@ -2067,6 +2083,7 @@ const SewagePipeInp = () => {
return (
<>
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
onSubmit={handleSearch}
@@ -2085,10 +2102,10 @@ const SewagePipeInp = () => {
{showEnterableToolbarButtons && (
<>
{activeTab !== 'ST00020002' && <LastYearDataButton getLastYearDataFunc={() => void getLastYearDataFunc()} />}
<SubmitButton submitFunc={() => void submitFunc(false)} />
{submitOk && <SubmitButton submitFunc={() => void submitFunc(false)} />}
</>
)}
{activeTabStatus?.laeSbmsnSttsCd === "2" && (
{activeTabStatus?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -2306,9 +2323,8 @@ const SewagePipeInp = () => {
searchValues?.bsnmCd &&
(tabStatsCdMap.get(activeTab) ?? statsCd)
? {
sewerBzmnCd: searchValues.bsnmCd,
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues.sido,
sggNm: searchValues.gugun === "ALL" ? "" : (searchValues.gugun ?? ""),
statsYr: searchValues.year,
statsCd: tabStatsCdMap.get(activeTab) ?? statsCd,
}
@@ -2342,9 +2358,8 @@ const SewagePipeInp = () => {
searchValues?.bsnmCd &&
(tabStatsCdMap.get(activeTab) ?? statsCd)
? {
sewerBzmnCd: searchValues.bsnmCd,
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues.sido,
sggNm: searchValues.gugun === "ALL" ? "" : (searchValues.gugun ?? ""),
statsYr: searchValues.year,
statsCd: tabStatsCdMap.get(activeTab) ?? statsCd,
}
@@ -2,7 +2,7 @@ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate, useSearchConditionSidoList } from "@/shared/lib";
import {
STATISTICS_INPUT_URL_PATH,
verificationSearchToFormValues,
@@ -27,7 +27,15 @@ import type {
cancelSubmitRequest,
ImrStatusResponse,
SewerageStatusInfo,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
toInputSearchRegion,
} from "@/entities/statistics";
import {
LoadingWithAnimation,
Tabs,
@@ -52,7 +60,6 @@ import { VerificationResultDialog } from "@/components/module/VerificationResult
import { VerificationResultHistoryDialog } from "@/components/module/VerificationResultHistoryDialog";
import type { ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { PipeDredgingStatusInpColumn } from "@/components/util/column/input/sewagePipeMaintain/PipeDredgingStatusInpColumn";
import { RepairStatusInpColumn } from "@/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn";
import { fetchColumnGuidesRecord } from "@/components/util/grid/columnGuides";
@@ -74,9 +81,8 @@ function buildSewagePipeMaintainSearchParam(
): SewagePipeMaintainRequestParams {
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
}
@@ -116,10 +122,14 @@ const computeTabEnterable = (
return periodEnterable;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function computeTabToolbarSnapshot(
@@ -263,6 +273,8 @@ function buildDredgingRowList(
// 하수도 통계 관리 > 03. 하수관로 유지관리
const SewagePipeMaintainInp = () => {
const { sidoList, isReady } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const { isAdminInfo } = useAdminStore();
const toolbarGroupAllowed = useStatisticsInputToolbarGroupGate();
@@ -288,9 +300,12 @@ const SewagePipeMaintainInp = () => {
const [footerVerificationByTab, setFooterVerificationByTab] = useState<
Map<string, VerificationResultLikeMap | null>
>(new Map());
const [toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
const [_toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
Map<string, TabToolbarSnapshot>
>(new Map());
const [searchContextByTab, setSearchContextByTab] = useState<Map<string, SearchFormValues>>(
() => new Map(),
);
const [verificationHistoryOpen, setVerificationHistoryOpen] = useState(false);
const [verificationResultDialogOpen, setVerificationResultDialogOpen] = useState(false);
const [showSubmitDialog, setShowSubmitDialog] = useState(false);
@@ -320,6 +335,7 @@ const SewagePipeMaintainInp = () => {
useLayoutEffect(() => {
if (activeTabId !== STATISTICS_INPUT_URL_PATH.SEWAGE_PIPE_MAINTAIN) return;
if (!isReady) return;
const pending = useStatisticsInputFocusStore
.getState()
.takePendingForUrl(STATISTICS_INPUT_URL_PATH.SEWAGE_PIPE_MAINTAIN);
@@ -327,12 +343,12 @@ const SewagePipeMaintainInp = () => {
const { tabKey, search } = pending;
setActiveTab(tabKey);
if (search) {
const values = verificationSearchToFormValues(search);
const values = verificationSearchToFormValues(search, sidoList);
setVerificationSearchDefaults(values);
postVerificationSearchRef.current = { values, tabKey };
setSearchFormRemountKey((k) => k + 1);
}
}, [activeTabId, inputFocusNonce]);
}, [activeTabId, inputFocusNonce, isReady, sidoList]);
useEffect(() => {
if (activeTabId !== STATISTICS_INPUT_URL_PATH.SEWAGE_PIPE_MAINTAIN) return;
@@ -377,6 +393,13 @@ const SewagePipeMaintainInp = () => {
);
return next;
});
if (searchContext) {
setSearchContextByTab((prev) => {
const next = new Map(prev);
next.set(tabKey, searchContext);
return next;
});
}
};
const applyRowMap = (
@@ -418,7 +441,11 @@ const SewagePipeMaintainInp = () => {
return false;
}
const { commonInfo, statusInfo, repairStatusList } = response.data;
const convertedData = (repairStatusList ?? []).map((d) => ({
const convertedData = filterBySelectedAdmdstCd(
repairStatusList ?? [],
sidoListRef.current,
searchContext,
).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
@@ -457,7 +484,11 @@ const SewagePipeMaintainInp = () => {
return false;
}
const { commonInfo, statusInfo, pipeDredgingStatusList } = response.data;
const convertedData = (pipeDredgingStatusList ?? []).map((d) => ({
const convertedData = filterBySelectedAdmdstCd(
pipeDredgingStatusList ?? [],
sidoListRef.current,
searchContext,
).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
@@ -581,11 +612,7 @@ const SewagePipeMaintainInp = () => {
const submitInfoBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
laePicNm: submitInfo.laePicNm,
laeKbrdrJbgdNm: submitInfo.laeKbrdrJbgdNm,
laeKbrdrDeptNm: submitInfo.laeKbrdrDeptNm,
@@ -597,12 +624,8 @@ const SewagePipeMaintainInp = () => {
};
const verificationQuery = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
};
@@ -647,7 +670,7 @@ const SewagePipeMaintainInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? "",
stats_cd: statsCdForTab,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd: activeTabStatus?.laeSbmsnSttsCd === "4" ? "4" : "2",
@@ -727,11 +750,7 @@ const SewagePipeMaintainInp = () => {
const baseRequest = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
};
try {
@@ -833,12 +852,8 @@ const SewagePipeMaintainInp = () => {
const statsCdForTab = tabStatsCdMap.get(tabKey) ?? tabKey;
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCdForTab,
@@ -871,6 +886,7 @@ const SewagePipeMaintainInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setActiveTab(firstTabKey);
setLastSearchedValues(null);
setHasSearched(false);
@@ -917,6 +933,7 @@ const SewagePipeMaintainInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
activeTabRef.current = firstTabKey;
setActiveTab(firstTabKey);
}
@@ -1012,10 +1029,9 @@ const SewagePipeMaintainInp = () => {
return map;
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo, columnGuidesByTab]);
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabToolbar =
toolbarSnapshotByTab.get(activeTab) ??
computeTabToolbarSnapshot(
activeTabStatus,
tabRowDataExists(rowDataMap, activeTab),
@@ -1036,6 +1052,7 @@ const SewagePipeMaintainInp = () => {
baseShowEnterableToolbarButtons && toolbarGroupAllowed;
const showActionToolbarCard =
baseShowActionToolbarCard && toolbarGroupAllowed;
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const openVerificationHistoryDialog = () => {
const ctx = lastSearchedValues ?? searchValues;
@@ -1052,9 +1069,8 @@ const SewagePipeMaintainInp = () => {
const statsCd = tabStatsCdMap.get(activeTab);
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
@@ -1102,6 +1118,7 @@ const SewagePipeMaintainInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
onSubmit={handleSearch}
@@ -1122,10 +1139,10 @@ const SewagePipeMaintainInp = () => {
<LastYearDataButton
getLastYearDataFunc={() => void getLastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{activeTabStatus?.laeSbmsnSttsCd === "2" && (
{activeTabStatus?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -1161,7 +1178,7 @@ const SewagePipeMaintainInp = () => {
tabStatusInfoMap.get(tabKey) ?? null,
inputPeriodEnterable,
) || isAdminInfo) && inputPeriodEnterable;
const gugunOk = buildGugunOkForToolbar(lastSearchedValues);
const gugunOk = buildGugunOkForToolbar(searchContextForToolbar);
const tabStatusInfo = tabStatusInfoMap.get(tabKey) ?? null;
return (
@@ -13,7 +13,7 @@ import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { ValueType } from "realgrid";
import type { RealGridReact } from "realgrid-react";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate, useSearchConditionSidoList } from "@/shared/lib";
import { STATISTICS_INPUT_URL_PATH } from "@/shared/lib/stats-cd-input-navigation";
import { useStatisticsInputPendingNavigation } from "@/shared/lib/use-statistics-input-pending-navigation";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
@@ -38,7 +38,16 @@ import type {
ImrStatusResponse,
SewerageStatusInfo,
Sgg,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
resolveNoApplicableForRequest,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -66,7 +75,6 @@ import type {
ColumnInfoList,
} from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { RainfallPumpStationInpColumn } from "@/components/util/column/input/sewageReservoirPump/RainfallPumpStationInpColumn";
import { SewageReservoirInpColumn } from "@/components/util/column/input/sewageReservoirPump/SewageReservoirInpColumn";
import { fetchColumnGuidesRecord } from "@/components/util/grid/columnGuides";
@@ -126,10 +134,22 @@ const computeTabEnterable = (
return periodEnterable;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function buildNoApplicableForRequest(
values: SearchFormValues | null,
checked: boolean,
isSaveListEmpty: boolean,
): boolean {
return resolveNoApplicableForRequest(sidoListRef.current, values, checked, isSaveListEmpty);
}
function computeTabToolbarSnapshot(
@@ -171,9 +191,8 @@ function buildSewageReservoirSearchParam(
const fcltNm = values.fctName?.trim();
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
...(fcltNm ? { fcltNm } : {}),
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
@@ -246,8 +265,9 @@ function buildSggNmColumn(
function mapSewageReservoirListToGridRows(
list: SewageReservoirItem[],
ctpvNm: string,
values?: SearchFormValues | null,
): GridRowWithVerification[] {
return list.map((item) => {
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const row = toGridRows([
{
...item,
@@ -399,8 +419,9 @@ function buildRainfallPumpStationSaveItem(
function mapRainfallPumpStationListToGridRows(
list: RainfallPumpStationItem[],
ctpvNm: string,
values?: SearchFormValues | null,
): GridRowWithVerification[] {
return list.map((item) => {
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const row = toGridRows([
{
...item,
@@ -447,6 +468,8 @@ async function waitForImrJobCompleted(
// 하수도 통계 관리 > 04. 하수저류 시설 및 빗물 펌프장
const SewageReservoirPumpInp = () => {
const { sidoList } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
@@ -478,9 +501,9 @@ const SewageReservoirPumpInp = () => {
alert("시군구를 선택해주세요.");
return;
}
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getCurrentRowsForTab(tabKey).length === 0);
const currentData = getCurrentRowsForTab(tabKey);
if (!noApplicable && currentData.length === 0) {
if (!noApplicable && currentData.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -505,12 +528,8 @@ const SewageReservoirPumpInp = () => {
const userInfo = await getUserInfo();
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCdForTab,
@@ -663,7 +682,7 @@ const SewageReservoirPumpInp = () => {
if (!(await confirm("저장 하시겠습니까?"))) return;
commitGridForTab(tabKey);
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getCurrentRowsForTab(tabKey).length === 0);
const currentData = getCurrentRowsForTab(tabKey);
const isReservoirTab = tabKey === SEWAGE_RESERVOIR_STATS_CD;
const saveList = noApplicable
@@ -682,7 +701,7 @@ const SewageReservoirPumpInp = () => {
: rainfallPumpStationDeleteList
.filter((row) => String(row.fcltSn ?? "").trim() !== "")
.map((row) => buildRainfallPumpStationSaveItem(row, sggOptions));
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -690,11 +709,7 @@ const SewageReservoirPumpInp = () => {
const requestBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
...(noApplicable ? { noApplicable: true } : {}),
};
@@ -754,7 +769,7 @@ const SewageReservoirPumpInp = () => {
}
commitGridForTab(tabKey);
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getCurrentRowsForTab(tabKey).length === 0);
const currentData = getCurrentRowsForTab(tabKey);
const isReservoirTab = tabKey === SEWAGE_RESERVOIR_STATS_CD;
const submitList = noApplicable
@@ -773,12 +788,8 @@ const SewageReservoirPumpInp = () => {
const activeTabStatus = tabStatusInfoMap.get(tabKey) ?? null;
const statsCdForTab = tabStatsCdMap.get(tabKey) ?? tabKey;
const verificationQuery = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
};
@@ -792,7 +803,7 @@ const SewageReservoirPumpInp = () => {
await loadActiveTabData(tabKey, lastSearchedValues, true, false);
};
if (!noApplicable && submitList.length === 0) {
if (!noApplicable && submitList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -800,11 +811,7 @@ const SewageReservoirPumpInp = () => {
const submitRequestBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
...(noApplicable ? { noApplicable: true } : {}),
laePicNm: submitInfo.laePicNm,
laeKbrdrJbgdNm: submitInfo.laeKbrdrJbgdNm,
@@ -847,7 +854,7 @@ const SewageReservoirPumpInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? "",
stats_cd: statsCdForTab,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd:
@@ -926,9 +933,12 @@ const SewageReservoirPumpInp = () => {
const [tabStatusInfoMap, setTabStatusInfoMap] = useState<
Map<string, SewerageStatusInfo | null>
>(new Map());
const [toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
const [_toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
Map<string, TabToolbarSnapshot>
>(new Map());
const [searchContextByTab, setSearchContextByTab] = useState<Map<string, SearchFormValues>>(
() => new Map(),
);
const [tabStatsCdMap, setTabStatsCdMap] = useState<Map<string, string>>(new Map());
const [isVerificationFailedByTab, setIsVerificationFailedByTab] = useState<
Map<string, boolean>
@@ -1024,6 +1034,13 @@ const SewageReservoirPumpInp = () => {
);
return next;
});
if (searchContext) {
setSearchContextByTab((prev) => {
const next = new Map(prev);
next.set(tabKey, searchContext);
return next;
});
}
};
const applyRowMap = (
@@ -1068,6 +1085,7 @@ const SewageReservoirPumpInp = () => {
const gridRows = mapSewageReservoirListToGridRows(
list,
commonInfo.ctpvNm,
values,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -1116,6 +1134,7 @@ const SewageReservoirPumpInp = () => {
const gridRows = mapRainfallPumpStationListToGridRows(
list,
commonInfo.ctpvNm,
values,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -1198,6 +1217,7 @@ const SewageReservoirPumpInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setLastSearchedValues(null);
setSggOptions([]);
sggOptionsSidoRef.current = null;
@@ -1249,6 +1269,7 @@ const SewageReservoirPumpInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
loadedTabKeysRef.current.clear();
setActiveTab(firstTabKey);
}
@@ -1318,10 +1339,9 @@ const SewageReservoirPumpInp = () => {
}
};
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabToolbar =
toolbarSnapshotByTab.get(activeTab) ??
computeTabToolbarSnapshot(
activeTabStatus,
tabRowDataExists(rowDataMap, activeTab),
@@ -1342,6 +1362,7 @@ const SewageReservoirPumpInp = () => {
baseShowEnterableToolbarButtons && toolbarGroupAllowed;
const showActionToolbarCard =
baseShowActionToolbarCard && toolbarGroupAllowed;
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const isReservoirOrRainfallTab =
activeTab === SEWAGE_RESERVOIR_STATS_CD || activeTab === rainfallPumpTabKey;
const showArticleToolbarCard =
@@ -1356,7 +1377,8 @@ const SewageReservoirPumpInp = () => {
activeTab,
tabRowDataExists(rowDataMap, activeTab),
tabStatusInfoMap.get(activeTab),
);
) &&
submitOk;
const setIsNotApplicableForActiveTab: Dispatch<SetStateAction<boolean>> = useCallback(
(next) => {
const tabKey = activeTabRef.current;
@@ -1514,9 +1536,8 @@ const SewageReservoirPumpInp = () => {
const statsCd = tabStatsCdMap.get(activeTab);
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
@@ -1537,6 +1558,7 @@ const SewageReservoirPumpInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
fctName={true}
@@ -1558,10 +1580,10 @@ const SewageReservoirPumpInp = () => {
<LastYearDataButton
getLastYearDataFunc={() => void getLastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{activeTabStatus?.laeSbmsnSttsCd === "2" && (
{activeTabStatus?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -1607,7 +1629,7 @@ const SewageReservoirPumpInp = () => {
tabStatusInfoMap.get(tabKey) ?? null,
inputPeriodEnterable,
) || isAdminInfo) && inputPeriodEnterable;
const gugunOk = buildGugunOkForToolbar(lastSearchedValues);
const gugunOk = buildGugunOkForToolbar(searchContextForToolbar);
const tabStatusInfo = tabStatusInfoMap.get(tabKey) ?? null;
const isReservoirTab = tabKey === SEWAGE_RESERVOIR_STATS_CD;
return (
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { useAlertStore, useGlobalProgressStore } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useSearchConditionSidoList } from "@/shared/lib";
import { STATISTICS_INPUT_URL_PATH } from "@/shared/lib/stats-cd-input-navigation";
import { useStatisticsInputPendingNavigation } from "@/shared/lib/use-statistics-input-pending-navigation";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
@@ -35,7 +35,15 @@ import type {
cancelSubmitRequest,
ImrStatusResponse,
SewerageStatusInfo,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -54,7 +62,6 @@ import { VerificationResultDialog } from "@/components/module/VerificationResult
import { VerificationResultHistoryDialog } from "@/components/module/VerificationResultHistoryDialog";
import type { ColumnConfig, ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { CauserBurdenInpColumn } from "@/components/util/column/input/sewerageFinance/CauserBurdenInpColumn";
import { SewerageChargeInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageChargeInpColumn";
import { SewerageFinanceSettleInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageFinanceSettleInpColumn";
@@ -101,9 +108,8 @@ function buildSewerageFinanceSearchParam(
): SewerageFinanceRequestParams {
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
}
@@ -121,10 +127,14 @@ const computeTabEnterable = (
return periodEnterable;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function computeTabToolbarSnapshot(
@@ -160,8 +170,8 @@ function tabRowDataExists(
function mapRegionalListToGridRows<
T extends { verificationResult?: SewerageFinanceVerificationResult },
>(list: T[], ctpvNm: string): GridRowWithVerification[] {
return list.map((item) => {
>(list: T[], ctpvNm: string, values?: SearchFormValues | null): GridRowWithVerification[] {
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const { verificationResult, ...rest } = item;
const row = toGridRows([{ ctpvNm, ...rest }])[0] as GridRowWithVerification;
if (verificationResult) row.verificationResult = verificationResult;
@@ -342,6 +352,8 @@ async function waitForImrJobCompleted(
// 하수도 통계 관리 > 09. 하수도재정
const SewerageFinanceInp = () => {
const { sidoList } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
@@ -369,9 +381,12 @@ const SewerageFinanceInp = () => {
const [footerVerificationByTab, setFooterVerificationByTab] = useState<
Map<string, VerificationResultLikeMap | null>
>(new Map());
const [toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
const [_toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
Map<string, TabToolbarSnapshot>
>(new Map());
const [searchContextByTab, setSearchContextByTab] = useState<Map<string, SearchFormValues>>(
() => new Map(),
);
const [columnGuidesByTab, setColumnGuidesByTab] = useState<
Record<string, Record<string, string>>
>({});
@@ -431,6 +446,13 @@ const SewerageFinanceInp = () => {
);
return next;
});
if (searchContext) {
setSearchContextByTab((prev) => {
const next = new Map(prev);
next.set(tabKey, searchContext);
return next;
});
}
};
const applyRowMap = (
@@ -471,6 +493,7 @@ const SewerageFinanceInp = () => {
const gridRows = mapRegionalListToGridRows(
(sewerageFinanceSettleList ?? []) as SewerageFinanceSettleItem[],
commonInfo.ctpvNm,
searchContext,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -500,6 +523,7 @@ const SewerageFinanceInp = () => {
const gridRows = mapRegionalListToGridRows(
(sewerageChargeList ?? []) as SewerageChargeItem[],
commonInfo.ctpvNm,
searchContext,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -529,6 +553,7 @@ const SewerageFinanceInp = () => {
const gridRows = mapRegionalListToGridRows(
(sewerageUsageFeeList ?? []) as SewerageUsageFeeItem[],
commonInfo.ctpvNm,
searchContext,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -558,6 +583,7 @@ const SewerageFinanceInp = () => {
const gridRows = mapRegionalListToGridRows(
(causerBurdenList ?? []) as CauserBurdenItem[],
commonInfo.ctpvNm,
searchContext,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -590,6 +616,7 @@ const SewerageFinanceInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setActiveTab(firstTabKey);
activeTabRef.current = firstTabKey;
setLastSearchedValues(null);
@@ -633,6 +660,7 @@ const SewerageFinanceInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
activeTabRef.current = firstTabKey;
setActiveTab(firstTabKey);
}
@@ -742,12 +770,8 @@ const SewerageFinanceInp = () => {
}
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCdForTab,
@@ -830,11 +854,7 @@ const SewerageFinanceInp = () => {
const submitInfoBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
laePicNm: submitInfo.laePicNm,
laePicTelNo: submitInfo.laePicTelNo,
laeKbrdrDeptNm: submitInfo.laeKbrdrDeptNm,
@@ -846,12 +866,8 @@ const SewerageFinanceInp = () => {
};
const verificationQuery = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
};
@@ -924,7 +940,7 @@ const SewerageFinanceInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? "",
stats_cd: statsCdForTab,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd: activeTabStatus?.laeSbmsnSttsCd === "4" ? "4" : "2",
@@ -1012,11 +1028,7 @@ const SewerageFinanceInp = () => {
const baseRequest = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
};
try {
@@ -1137,9 +1149,8 @@ const SewerageFinanceInp = () => {
const statsCd = tabStatsCdMap.get(activeTab);
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
@@ -1170,19 +1181,20 @@ const SewerageFinanceInp = () => {
return map;
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo, columnGuidesByTab]);
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabToolbar =
toolbarSnapshotByTab.get(activeTab) ??
computeTabToolbarSnapshot(
activeTabStatus,
tabRowDataExists(rowDataMap, activeTab),
hasSearched,
lastSearchedValues,
searchContextForToolbar,
inputPeriodEnterable,
isAdminInfo,
);
const { showSaveInToolbar, showEnterableToolbarButtons, showActionToolbarCard } =
activeTabToolbar;
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const verificationCellStyle = ({
fieldName,
@@ -1241,6 +1253,7 @@ const SewerageFinanceInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
onSubmit={handleSearch}
@@ -1261,10 +1274,10 @@ const SewerageFinanceInp = () => {
<LastYearDataButton
getLastYearDataFunc={() => void lastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{activeTabStatus?.laeSbmsnSttsCd === "2" && (
{activeTabStatus?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -1300,7 +1313,7 @@ const SewerageFinanceInp = () => {
tabStatusInfoMap.get(tabKey) ?? null,
inputPeriodEnterable,
) || isAdminInfo) && inputPeriodEnterable;
const gugunOk = buildGugunOkForToolbar(lastSearchedValues);
const gugunOk = buildGugunOkForToolbar(searchContextForToolbar);
const tabStatusInfo = tabStatusInfoMap.get(tabKey) ?? null;
return (
@@ -2,7 +2,7 @@ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useSearchConditionSidoList, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import {
STATISTICS_INPUT_URL_PATH,
verificationSearchToFormValues,
@@ -25,8 +25,15 @@ import type {
cancelSubmitRequest,
ImrStatusResponse,
SewerageStatusInfo,
Sido,
SubmitExceptionRequest,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -45,7 +52,6 @@ import SearchForm from "@/components/module/SearchForm";
import { VerificationResultDialog } from "@/components/module/VerificationResultDialog";
import { VerificationResultHistoryDialog } from "@/components/module/VerificationResultHistoryDialog";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { SeweragePenetrationRateInpColumn } from "@/components/util/column/input/SeweragePenetrationRateInpColumn";
import { getFooterSummary } from "@/components/util/grid/footerSummary";
import { toGridRows } from "@/components/util/grid/gridFunction";
@@ -118,14 +124,15 @@ async function waitForImrJobCompleted(
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
}
const sidoListRef: { current: Sido[] } = { current: [] };
function getSewerageSupplyRateParams(
values: SearchFormValues,
overrides?: { yr?: string; demoYr?: string; mode?: string }
): SewerageSupplyRateRequestParams {
return {
ctpvNm: values.sido,
sggNm: values.gugun === 'ALL' ? '' : values.gugun,
sewerBzmnCd: values.bsnmCd,
...toInputSearchRegion(sidoListRef.current, values),
statsYr: overrides?.yr ?? values.year,
...(overrides?.mode && { mode: overrides.mode }),
};
@@ -133,6 +140,8 @@ function getSewerageSupplyRateParams(
// 하수도 통계 관리 > 01. 하수도 보급률
const SeweragePenetrationRateInp = () => {
const { sidoList, isReady } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
@@ -174,15 +183,16 @@ const SeweragePenetrationRateInp = () => {
if (activeTabId !== STATISTICS_INPUT_URL_PATH.SEWERAGE_PENETRATION_RATE) {
return;
}
if (!isReady) return;
const pending = useStatisticsInputFocusStore
.getState()
.takePendingForUrl(STATISTICS_INPUT_URL_PATH.SEWERAGE_PENETRATION_RATE);
if (!pending?.search) return;
const values = verificationSearchToFormValues(pending.search);
const values = verificationSearchToFormValues(pending.search, sidoList);
setVerificationSearchDefaults(values);
postVerificationSearchRef.current = values;
setSearchFormRemountKey((k) => k + 1);
}, [activeTabId, inputFocusNonce]);
}, [activeTabId, inputFocusNonce, isReady, sidoList]);
useEffect(() => {
if (activeTabId !== STATISTICS_INPUT_URL_PATH.SEWERAGE_PENETRATION_RATE) {
@@ -269,14 +279,22 @@ const SeweragePenetrationRateInp = () => {
setSearchFormRemountKey((k) => k + 1);
};
const fetchSewerageSupplyRate = async (params: SewerageSupplyRateRequestParams, isEnterable: boolean) => {
const fetchSewerageSupplyRate = async (
params: SewerageSupplyRateRequestParams,
isEnterable: boolean,
values?: SearchFormValues | null,
) => {
try {
setIsLoading(true);
const response = await statisticsInputSewSupplyRateApi.getSewerageSupplyRateList(params);
if (response.status === 'success' && response.data) {
const commonInfo = response.data.commonInfo;
const statusInfo = response.data.statusInfo;
const convertedData: SewerageSupplyRateItem[] = response.data.sewerageSupplyRateList.map((d: SewerageSupplyRateItem) => ({
const convertedData: SewerageSupplyRateItem[] = filterBySelectedAdmdstCd(
response.data.sewerageSupplyRateList,
sidoListRef.current,
values,
).map((d: SewerageSupplyRateItem) => ({
...d,
ctpvNm: commonInfo.ctpvNm,
spcCtpnRgnCn: d.spcCtpnRgnCn === '' || d.spcCtpnRgnCn === null ? '해당없음' : d.spcCtpnRgnCn,
@@ -346,7 +364,7 @@ const SeweragePenetrationRateInp = () => {
setPeriodCheckList(periods);
const checkEnterable = isInputPeriodCurrent(periods);
setLastSearchedYear(values.year || null);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(values), checkEnterable);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(values), checkEnterable, values);
setLastSearchedValues(values);
} else {
alert(response.message);
@@ -374,9 +392,8 @@ const SeweragePenetrationRateInp = () => {
const params: SubmitExceptionRequest = {
...submitInfo,
sewerBzmnCd: searchValues?.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues?.sido ?? '',
sggNm: searchValues?.gugun === "ALL" ? "" : (searchValues?.gugun ?? ''),
statsYr: searchValues?.year ?? '',
userId: userInfo.userUnqId,
statsCd: statsCd,
@@ -392,7 +409,7 @@ const SeweragePenetrationRateInp = () => {
const response = await statisticsApi.submitException(params);
if (response.status === 'success') {
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), false);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), false, searchValues ?? {});
alert('제출되었습니다.');
} else {
alert(response.message || '데이터 제출 중 오류가 발생했습니다.');
@@ -416,7 +433,7 @@ const SeweragePenetrationRateInp = () => {
return;
}
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(values, { mode: 'PRE' }), true);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(values, { mode: 'PRE' }), true, values);
};
const submitFunc = async (isException: boolean) => {
@@ -512,9 +529,8 @@ const SeweragePenetrationRateInp = () => {
}));
const requestData: SewerageSupplyRateSaveRequest = {
sewerBzmnCd: searchValues?.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues?.sido ?? '',
sggNm: searchValues?.gugun === "ALL" ? "" : (searchValues?.gugun ?? ''),
statsYr: searchValues?.year ?? '',
userId: userInfo.userUnqId,
submitList: convertedData,
@@ -558,13 +574,12 @@ const SeweragePenetrationRateInp = () => {
);
}
setGlobalProgress(100);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true, searchValues ?? {});
await openVerificationResultDialogIfNeeded(
searchValues?.year && searchValues?.sido && searchValues?.bsnmCd && statsCd
? {
sewerBzmnCd: searchValues.bsnmCd,
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues.sido,
sggNm: searchValues.gugun === "ALL" ? "" : (searchValues.gugun ?? ""),
statsYr: searchValues.year,
statsCd,
}
@@ -573,14 +588,13 @@ const SeweragePenetrationRateInp = () => {
{ notify: alert },
);
} catch (e) {
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true, searchValues ?? {});
setIsLoading(false);
await openVerificationResultDialogIfNeeded(
searchValues?.year && searchValues?.sido && searchValues?.bsnmCd && statsCd
? {
sewerBzmnCd: searchValues.bsnmCd,
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues.sido,
sggNm: searchValues.gugun === "ALL" ? "" : (searchValues.gugun ?? ""),
statsYr: searchValues.year,
statsCd,
}
@@ -597,13 +611,12 @@ const SeweragePenetrationRateInp = () => {
}
} else {
setGlobalProgress(100);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), false);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), false, searchValues ?? {});
await openVerificationResultDialogIfNeeded(
searchValues?.year && searchValues?.sido && searchValues?.bsnmCd && statsCd
? {
sewerBzmnCd: searchValues.bsnmCd,
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues.sido,
sggNm: searchValues.gugun === "ALL" ? "" : (searchValues.gugun ?? ""),
statsYr: searchValues.year,
statsCd,
}
@@ -702,9 +715,8 @@ const SeweragePenetrationRateInp = () => {
}));
const requestData: SewerageSupplyRateSaveRequest = {
sewerBzmnCd: searchValues?.bsnmCd ?? '', // 하수도 사업자 코드
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues?.sido ?? '', // 시도
sggNm: searchValues?.gugun === "ALL" ? "" : (searchValues?.gugun ?? ''),
statsYr: searchValues?.year ?? '', // 통계 연도
userId: userInfo.userUnqId, // 사용자
saveList: convertedData, // 저장 데이터
@@ -720,7 +732,7 @@ const SeweragePenetrationRateInp = () => {
if (response.status === 'success') {
setGlobalProgress(50);
setGlobalProgress(100);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true);
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true, searchValues ?? {});
alert('저장되었습니다.');
} else {
alert(response.message || '데이터 저장 중 오류가 발생했습니다.');
@@ -747,9 +759,8 @@ const SeweragePenetrationRateInp = () => {
const userInfo = await getUserInfo();
const requestData: cancelSubmitRequest = {
sewerBzmnCd: searchValues?.bsnmCd ?? '',
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues?.sido ?? '',
sggNm: searchValues?.gugun === "ALL" ? "" : (searchValues?.gugun ?? ""),
statsYr: searchValues?.year ?? '',
mdfrId: userInfo.userUnqId,
statsCd: statsCd,
@@ -758,7 +769,7 @@ const SeweragePenetrationRateInp = () => {
const response = await statisticsApi.cancelSubmit(requestData);
if (response.status === 'success') {
fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true);
fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true, searchValues ?? {});
alert('제출 취소되었습니다.');
} else {
alert(response.message ?? '제출 취소 중 오류가 발생했습니다.');
@@ -777,9 +788,9 @@ const SeweragePenetrationRateInp = () => {
setVerificationHistoryOpen(true);
};
const gugunOkOrNotDoList = doList.includes(lastSearchedValues?.bsnmCd || '')
? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== 'ALL')
: true;
const toolbarSearchContext = lastSearchedValues;
const gugunOkOrNotDoList = isGugunOkForToolbar(sidoList, toolbarSearchContext);
const submitOk = isSubmitOkForToolbar(sidoList, toolbarSearchContext);
const inputPeriodCurrent = isInputPeriodCurrent(periodCheckList);
const isSubmitted = statusInfo?.laeSbmsnSttsCd === '2';
@@ -802,6 +813,7 @@ const SeweragePenetrationRateInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
onSubmit={handleSearch}
@@ -818,10 +830,10 @@ const SeweragePenetrationRateInp = () => {
{showEnterableToolbarButtons && (
<>
<LastYearDataButton getLastYearDataFunc={() => getLastYearDataFunc(searchValues ?? {})} />
<SubmitButton submitFunc={() => submitFunc(false)} />
{submitOk && <SubmitButton submitFunc={() => submitFunc(false)} />}
</>
)}
{statusInfo?.laeSbmsnSttsCd === '2' && <CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />}
{statusInfo?.laeSbmsnSttsCd === '2' && submitOk && <CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
</div>
</div>
@@ -836,7 +848,7 @@ const SeweragePenetrationRateInp = () => {
<DataGrid
key={((isEnterable || isAdminInfo) && inputPeriodCurrent) ? 'enterable' : 'readonly'}
columnInfo={columnInfo}
isExcelImport={(rowData.length > 0) && isEnterable && (doList.includes(lastSearchedValues?.bsnmCd || '') ? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== 'ALL') : true)}
isExcelImport={(rowData.length > 0) && isEnterable && gugunOkOrNotDoList}
excelImportSewerBzmnCd={(lastSearchedValues ?? searchValues)?.bsnmCd}
excelExportSearchContext={toExcelExportSearchContextFromSearchForm(lastSearchedValues ?? searchValues)}
rowStartNum={3}
@@ -892,9 +904,8 @@ const SeweragePenetrationRateInp = () => {
queryContext={
searchValues?.year && searchValues?.sido && searchValues?.bsnmCd && statsCd
? {
sewerBzmnCd: searchValues.bsnmCd,
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues.sido,
sggNm: searchValues.gugun === "ALL" ? "" : (searchValues.gugun ?? ""),
statsYr: searchValues.year,
statsCd,
}
@@ -912,12 +923,8 @@ const SeweragePenetrationRateInp = () => {
queryContext={
searchValues?.year && searchValues?.sido && searchValues?.bsnmCd && statsCd
? {
sewerBzmnCd: searchValues.bsnmCd,
...toInputSearchRegion(sidoListRef.current, searchValues),
ctpvNm: searchValues.sido,
sggNm:
searchValues.gugun === "ALL"
? ""
: (searchValues.gugun ?? ""),
statsYr: searchValues.year,
statsCd,
}
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate, useSearchConditionSidoList } from "@/shared/lib";
import { STATISTICS_INPUT_URL_PATH } from "@/shared/lib/stats-cd-input-navigation";
import { useStatisticsInputPendingNavigation } from "@/shared/lib/use-statistics-input-pending-navigation";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
@@ -25,7 +25,16 @@ import type {
cancelSubmitRequest,
ImrStatusResponse,
SewerageStatusInfo,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
resolveNoApplicableForRequest,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -45,7 +54,6 @@ import { VerificationResultDialog } from "@/components/module/VerificationResult
import { VerificationResultHistoryDialog } from "@/components/module/VerificationResultHistoryDialog";
import type { ColumnConfig, ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { ComplaintInpColumn } from "@/components/util/column/input/sewerageServiceLevel/ComplaintInpColumn";
import { ResidentFriendlyFactInpColumn } from "@/components/util/column/input/sewerageServiceLevel/ResidentFriendlyFactInpColumn";
import { fetchColumnGuidesRecord } from "@/components/util/grid/columnGuides";
@@ -120,10 +128,22 @@ const computeTabEnterable = (
return periodEnterable;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function buildNoApplicableForRequest(
values: SearchFormValues | null,
checked: boolean,
isSaveListEmpty: boolean,
): boolean {
return resolveNoApplicableForRequest(sidoListRef.current, values, checked, isSaveListEmpty);
}
function computeTabToolbarSnapshot(
@@ -155,17 +175,16 @@ function buildSewerageServiceLevelSearchParam(
): SewerageServiceLevelComplaintRequestParams {
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
}
function mapServiceLevelListToGridRows<
T extends { verificationResult?: SewerageServiceLevelVerificationResult },
>(list: T[], ctpvNm: string): GridRowWithVerification[] {
return list.map((item) => {
>(list: T[], ctpvNm: string, values?: SearchFormValues | null): GridRowWithVerification[] {
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const { verificationResult, ...rest } = item;
const row = toGridRows([{ ctpvNm, ...rest }])[0] as GridRowWithVerification;
if (verificationResult) row.verificationResult = verificationResult;
@@ -271,6 +290,8 @@ async function waitForImrJobCompleted(
// 하수도 통계 관리 > 13. 하수도 서비스 수준
const SewerageServiceLevelInp = () => {
const { sidoList } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
@@ -301,9 +322,12 @@ const SewerageServiceLevelInp = () => {
const [footerVerificationByTab, setFooterVerificationByTab] = useState<
Map<string, VerificationResultLikeMap | null>
>(new Map());
const [toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
const [_toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
Map<string, TabToolbarSnapshot>
>(new Map());
const [searchContextByTab, setSearchContextByTab] = useState<Map<string, SearchFormValues>>(
() => new Map(),
);
const [columnGuidesByTab, setColumnGuidesByTab] = useState<
Record<string, Record<string, string>>
>({});
@@ -366,6 +390,13 @@ const SewerageServiceLevelInp = () => {
);
return next;
});
if (searchContext) {
setSearchContextByTab((prev) => {
const next = new Map(prev);
next.set(tabKey, searchContext);
return next;
});
}
};
const applyRowMap = (
@@ -407,6 +438,7 @@ const SewerageServiceLevelInp = () => {
const gridRows = mapServiceLevelListToGridRows(
(complaintList ?? []) as SewerageServiceLevelComplaintItem[],
commonInfo.ctpvNm,
searchContext,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -439,6 +471,7 @@ const SewerageServiceLevelInp = () => {
const gridRows = mapServiceLevelListToGridRows(
(residentFriendlyFactList ?? []) as SewerageServiceLevelResidentFriendlyFactItem[],
commonInfo.ctpvNm,
searchContext,
);
if (!fromPreYear) {
publicSewageFacilitiesAvailableRef.current = gridRows.length > 0;
@@ -483,6 +516,7 @@ const SewerageServiceLevelInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setActiveTab(firstTabKey);
activeTabRef.current = firstTabKey;
setLastSearchedValues(null);
@@ -529,6 +563,7 @@ const SewerageServiceLevelInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setIsNotApplicable(false);
activeTabRef.current = firstTabKey;
setActiveTab(firstTabKey);
@@ -647,9 +682,19 @@ const SewerageServiceLevelInp = () => {
const tabKey = activeTabRef.current;
const isResidentTab = tabKey === ResidentFriendlyFactInpColumn.tabKey;
const latestRows = getTabGridRows(tabKey);
const noApplicable = isResidentTab && isNotApplicable;
const noApplicable =
isResidentTab &&
buildNoApplicableForRequest(
searchContextByTab.get(tabKey) ?? lastSearchedValues,
isNotApplicable,
latestRows.length === 0,
);
if (latestRows.length === 0) {
if (isResidentTab && !noApplicable) {
if (
isResidentTab &&
!noApplicable &&
buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)
) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -690,7 +735,13 @@ const SewerageServiceLevelInp = () => {
return next;
});
const noApplicable = isResidentTab && isNotApplicable;
const noApplicable =
isResidentTab &&
buildNoApplicableForRequest(
searchContextByTab.get(tabKey) ?? lastSearchedValues,
isNotApplicable,
latestRows.length === 0,
);
const complaintSubmitList = buildComplaintSaveList(latestRows);
const residentSubmitList = noApplicable
? []
@@ -700,7 +751,12 @@ const SewerageServiceLevelInp = () => {
alert("제출할 데이터가 없습니다.");
return;
}
if (isResidentTab && !noApplicable && residentSubmitList.length === 0) {
if (
isResidentTab &&
!noApplicable &&
residentSubmitList.length === 0 &&
buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)
) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -715,11 +771,7 @@ const SewerageServiceLevelInp = () => {
const submitInfoBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
laePicNm: submitInfo.laePicNm,
laePicTelNo: submitInfo.laePicTelNo,
laeKbrdrDeptNm: submitInfo.laeKbrdrDeptNm,
@@ -731,12 +783,8 @@ const SewerageServiceLevelInp = () => {
};
const verificationQuery = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
};
@@ -784,7 +832,7 @@ const SewerageServiceLevelInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? "",
stats_cd: statsCdForTab,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd: activeTabStatus?.laeSbmsnSttsCd === "4" ? "4" : "2",
@@ -865,15 +913,17 @@ const SewerageServiceLevelInp = () => {
return next;
});
const noApplicable = isResidentTab && isNotApplicable;
const noApplicable =
isResidentTab &&
buildNoApplicableForRequest(
searchContextByTab.get(tabKey) ?? lastSearchedValues,
isNotApplicable,
latestRows.length === 0,
);
const baseRequest = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
};
try {
@@ -903,7 +953,11 @@ const SewerageServiceLevelInp = () => {
const saveList = noApplicable
? []
: buildResidentFriendlyFactSaveList(latestRows);
if (!noApplicable && saveList.length === 0) {
if (
!noApplicable &&
saveList.length === 0 &&
buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)
) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -963,12 +1017,8 @@ const SewerageServiceLevelInp = () => {
}
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCdForTab,
@@ -1026,15 +1076,15 @@ const SewerageServiceLevelInp = () => {
return map;
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo, columnGuidesByTab]);
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabRowData = rowDataMap.get(activeTab) ?? [];
const activeTabToolbar =
toolbarSnapshotByTab.get(activeTab) ??
computeTabToolbarSnapshot(
activeTabStatus,
tabRowDataExists(rowDataMap, activeTab),
hasSearched,
lastSearchedValues,
searchContextForToolbar,
inputPeriodEnterable,
isAdminInfo,
);
@@ -1045,12 +1095,14 @@ const SewerageServiceLevelInp = () => {
activeTabToolbar.showEnterableToolbarButtons && toolbarGroupAllowed;
const showActionToolbarCard =
activeTabToolbar.showActionToolbarCard && toolbarGroupAllowed;
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const showNotApplicableForResidentTab =
activeTab === ResidentFriendlyFactInpColumn.tabKey &&
searchLoaded &&
hasSearched &&
activeTabRowData.length === 0 &&
computeTabEnterable(activeTabStatus, inputPeriodEnterable);
computeTabEnterable(activeTabStatus, inputPeriodEnterable) &&
submitOk;
const showSecondaryToolbarCard =
hasSearched &&
toolbarGroupAllowed &&
@@ -1061,9 +1113,8 @@ const SewerageServiceLevelInp = () => {
const statsCd = tabStatsCdMap.get(activeTab);
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
@@ -1126,6 +1177,7 @@ const SewerageServiceLevelInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
onSubmit={handleSearch}
@@ -1146,10 +1198,10 @@ const SewerageServiceLevelInp = () => {
<LastYearDataButton
getLastYearDataFunc={() => void getLastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{activeTabStatus?.laeSbmsnSttsCd === "2" && (
{activeTabStatus?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -1197,7 +1249,7 @@ const SewerageServiceLevelInp = () => {
tabStatusInfoMap.get(tabKey) ?? null,
inputPeriodEnterable,
) || isAdminInfo) && inputPeriodEnterable;
const gugunOk = buildGugunOkForToolbar(lastSearchedValues);
const gugunOk = buildGugunOkForToolbar(searchContextForToolbar);
const tabStatusInfo = tabStatusInfoMap.get(tabKey) ?? null;
return (
@@ -70,7 +70,7 @@ const StatisticsReviewHistory = () => {
return (
<div className="relative">
<SearchForm onSubmit={handleSearch} onReset={handleReset} />
<SearchForm isInput onSubmit={handleSearch} onReset={handleReset} />
{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]">
@@ -4,7 +4,7 @@ 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 { useActiveTabRegAuthorityAllowed, useAlertStore, useSearchConditionSidoList } 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";
@@ -17,6 +17,7 @@ import {
isInputPeriodCurrent,
} from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface";
import type { StatsInputStatusApproveItem } from "@/entities/statistics/model/statistics-interface";
import { isGugunOkForToolbar } from "@/entities/statistics";
import { LoadingWithAnimation } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -30,7 +31,6 @@ import {
import SearchForm from "@/components/module/SearchForm";
import type { DataCellClickCallbackParams } from "@/components/type/DataGrid.type";
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";
@@ -44,6 +44,7 @@ const getStatsInputStatusFetcher = async (
// 하수도 통계 관리 > 00. 통계 입력 현황
const StatsInputStatusInp = () => {
const { sidoList } = useSearchConditionSidoList();
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const activeTabId = useTabStore((state) => state.activeTabId);
@@ -254,9 +255,7 @@ const StatsInputStatusInp = () => {
await mutate(["stats-input-status", searchParams]);
}, [mutate, searchParams]);
const gugunOkOrNotDoList = doList.includes(lastSearchedValues?.bsnmCd || "")
? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== "ALL")
: true;
const gugunOkOrNotDoList = isGugunOkForToolbar(sidoList, lastSearchedValues);
const allRowsLaeSbmsnApproved = useMemo(
() =>
@@ -13,7 +13,7 @@ import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { ValueType } from "realgrid";
import type { RealGridReact } from "realgrid-react";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate, useSearchConditionSidoList } from "@/shared/lib";
import { STATISTICS_INPUT_URL_PATH } from "@/shared/lib/stats-cd-input-navigation";
import { useStatisticsInputPendingNavigation } from "@/shared/lib/use-statistics-input-pending-navigation";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
@@ -48,7 +48,16 @@ import type {
ImrStatusResponse,
SewerageStatusInfo,
Sgg,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import {
filterBySelectedAdmdstCd,
isGugunOkForToolbar,
isSubmitOkForToolbar,
resolveNoApplicableForRequest,
toInputSearchRegion,
} from "@/entities/statistics";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
@@ -71,7 +80,6 @@ import { VerificationResultDialog } from "@/components/module/VerificationResult
import { VerificationResultHistoryDialog } from "@/components/module/VerificationResultHistoryDialog";
import type { ColumnConfig, ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { PublicSewageTreatWaterReuseInpColumn } from "@/components/util/column/input/waterReuse/PublicSewageTreatWaterReuseInpColumn";
import { RainFallUtilFactStatusInpColumn } from "@/components/util/column/input/waterReuse/RainFallUtilFactStatusInpColumn";
import { RecycledWaterStatusInpColumn } from "@/components/util/column/input/waterReuse/RecycledWaterStatusInpColumn";
@@ -159,10 +167,22 @@ const computeTabEnterable = (
return periodEnterable;
};
const sidoListRef: { current: Sido[] } = { current: [] };
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
return isGugunOkForToolbar(sidoListRef.current, values);
}
function buildSubmitOkForToolbar(values: SearchFormValues | null): boolean {
return isSubmitOkForToolbar(sidoListRef.current, values);
}
function buildNoApplicableForRequest(
values: SearchFormValues | null,
checked: boolean,
isSaveListEmpty: boolean,
): boolean {
return resolveNoApplicableForRequest(sidoListRef.current, values, checked, isSaveListEmpty);
}
function computeTabToolbarSnapshot(
@@ -194,9 +214,8 @@ function buildWaterReuseSearchParam(
): WaterReuseRequestParams {
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...toInputSearchRegion(sidoListRef.current, values),
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
}
@@ -510,6 +529,8 @@ async function waitForImrJobCompleted(
// 하수도 통계 관리 > 10. 물 재이용
const WaterReuseInp = () => {
const { sidoList } = useSearchConditionSidoList({ isInput: true });
sidoListRef.current = sidoList;
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
@@ -541,9 +562,12 @@ const WaterReuseInp = () => {
const [footerVerificationByTab, setFooterVerificationByTab] = useState<
Map<string, VerificationResultLikeMap | null>
>(new Map());
const [toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
const [_toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
Map<string, TabToolbarSnapshot>
>(new Map());
const [searchContextByTab, setSearchContextByTab] = useState<Map<string, SearchFormValues>>(
() => new Map(),
);
const [columnGuidesByTab, setColumnGuidesByTab] = useState<
Record<string, Record<string, string>>
>({});
@@ -644,6 +668,13 @@ const WaterReuseInp = () => {
);
return next;
});
if (searchContext) {
setSearchContextByTab((prev) => {
const next = new Map(prev);
next.set(tabKey, searchContext);
return next;
});
}
};
const applyRowMap = (
@@ -673,6 +704,8 @@ const WaterReuseInp = () => {
): Promise<boolean> => {
const requestParam = overrides?.mode ? { ...param, mode: overrides.mode } : param;
const fromPreYear = overrides?.mode === "PRE";
const filterFetched = <T,>(list: T[] | undefined) =>
filterBySelectedAdmdstCd(list, sidoListRef.current, searchContext);
const applyNotApplicableForLoadedTab = (
gridRows: readonly Record<string, string | number>[],
@@ -704,7 +737,7 @@ const WaterReuseInp = () => {
}
const { commonInfo, statusInfo, publicSewageTreatWaterReuseList } = response.data;
const gridRows = mapPublicSewageTreatWaterReuseListToGridRows(
(publicSewageTreatWaterReuseList ?? []) as PublicSewageTreatWaterReuseItem[],
filterFetched(publicSewageTreatWaterReuseList) as PublicSewageTreatWaterReuseItem[],
commonInfo.ctpvNm,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -735,7 +768,7 @@ const WaterReuseInp = () => {
}
const { commonInfo, statusInfo, recycledWaterList } = response.data;
const gridRows = mapRecycledWaterStatusListToGridRows(
(recycledWaterList ?? []) as RecycledWaterStatusItem[],
filterFetched(recycledWaterList) as RecycledWaterStatusItem[],
commonInfo.ctpvNm,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -766,7 +799,7 @@ const WaterReuseInp = () => {
}
const { commonInfo, statusInfo, rainFallUtilFactStatusList } = response.data;
const gridRows = mapRainFallUtilFactStatusListToGridRows(
(rainFallUtilFactStatusList ?? []) as RainFallUtilFactStatusItem[],
filterFetched(rainFallUtilFactStatusList) as RainFallUtilFactStatusItem[],
commonInfo.ctpvNm,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -798,8 +831,7 @@ const WaterReuseInp = () => {
}
const { commonInfo, statusInfo, treatWaterReuseDesignConstStatusList } = response.data;
const gridRows = mapTreatWaterReuseStatusListToGridRows(
(treatWaterReuseDesignConstStatusList ??
[]) as TreatWaterReuseDesignConstStatusItem[],
filterFetched(treatWaterReuseDesignConstStatusList) as TreatWaterReuseDesignConstStatusItem[],
commonInfo.ctpvNm,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -832,8 +864,7 @@ const WaterReuseInp = () => {
const { commonInfo, statusInfo, treatWaterReuseBusinessApprStatusList } =
response.data;
const gridRows = mapTreatWaterReuseStatusListToGridRows(
(treatWaterReuseBusinessApprStatusList ??
[]) as TreatWaterReuseBusinessApprStatusItem[],
filterFetched(treatWaterReuseBusinessApprStatusList) as TreatWaterReuseBusinessApprStatusItem[],
commonInfo.ctpvNm,
);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -906,6 +937,7 @@ const WaterReuseInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setActiveTab(firstTabKey);
activeTabRef.current = firstTabKey;
setLastSearchedValues(null);
@@ -954,6 +986,7 @@ const WaterReuseInp = () => {
setIsVerificationFailedByTab(new Map());
setFooterVerificationByTab(new Map());
setToolbarSnapshotByTab(new Map());
setSearchContextByTab(new Map());
setDeleteListByTab(new Map());
loadedTabKeysRef.current.clear();
activeTabRef.current = firstTabKey;
@@ -1076,9 +1109,9 @@ const WaterReuseInp = () => {
return;
}
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
if (!noApplicable && currentData.length === 0) {
if (!noApplicable && currentData.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1111,11 +1144,7 @@ const WaterReuseInp = () => {
const submitRequestBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
laePicNm: submitInfo.laePicNm,
laePicTelNo: submitInfo.laePicTelNo,
laeKbrdrDeptNm: submitInfo.laeKbrdrDeptNm,
@@ -1135,12 +1164,8 @@ const WaterReuseInp = () => {
WATER_REUSE_COLUMN_GUIDE_STATS_CD_BY_TAB_KEY[tabKey] ??
tabKey;
const verificationQuery = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
};
@@ -1174,12 +1199,12 @@ const WaterReuseInp = () => {
};
if (isPublicSewageTab) {
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const submitList = noApplicable
? []
: currentData.map(buildPublicSewageTreatWaterReuseSaveItem);
if (!noApplicable && submitList.length === 0) {
if (!noApplicable && submitList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1191,7 +1216,7 @@ const WaterReuseInp = () => {
submitList,
});
} else {
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const sido = lastSearchedValues.sido ?? "";
@@ -1222,7 +1247,7 @@ const WaterReuseInp = () => {
.filter((row) => isPersistedArticleRow(tabKey, row))
.map((row) => buildRecycledWaterStatusSaveItem(row, list));
if (!noApplicable && submitList.length === 0) {
if (!noApplicable && submitList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1242,7 +1267,7 @@ const WaterReuseInp = () => {
.filter((row) => isPersistedArticleRow(tabKey, row))
.map((row) => buildRainFallUtilFactStatusSaveItem(row, list));
if (!noApplicable && submitList.length === 0) {
if (!noApplicable && submitList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1264,7 +1289,7 @@ const WaterReuseInp = () => {
buildTreatWaterReuseDesignConstStatusSaveItem(row, list),
);
if (!noApplicable && submitList.length === 0) {
if (!noApplicable && submitList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1289,7 +1314,7 @@ const WaterReuseInp = () => {
buildTreatWaterReuseBusinessApprStatusSaveItem(row, list),
);
if (!noApplicable && submitList.length === 0) {
if (!noApplicable && submitList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1321,7 +1346,7 @@ const WaterReuseInp = () => {
setGlobalProgressLabel("시계열 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
sewer_bzmn_cd: lastSearchedValues?.bsnmCd ?? "",
stats_cd: statsCdForTab,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd:
@@ -1396,11 +1421,7 @@ const WaterReuseInp = () => {
const requestBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
};
@@ -1418,12 +1439,12 @@ const WaterReuseInp = () => {
};
if (tabKey === PublicSewageTreatWaterReuseInpColumn.tabKey) {
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const saveList = noApplicable
? []
: currentData.map(buildPublicSewageTreatWaterReuseSaveItem);
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1454,7 +1475,7 @@ const WaterReuseInp = () => {
}
if (tabKey === RecycledWaterStatusInpColumn.tabKey) {
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const sido = lastSearchedValues.sido ?? "";
@@ -1477,7 +1498,7 @@ const WaterReuseInp = () => {
.filter((row) => isPersistedArticleRow(tabKey, row))
.map((row) => buildRecycledWaterStatusSaveItem(row, list));
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1514,7 +1535,7 @@ const WaterReuseInp = () => {
}
if (tabKey === RainFallUtilFactStatusInpColumn.tabKey) {
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const sido = lastSearchedValues.sido ?? "";
@@ -1537,7 +1558,7 @@ const WaterReuseInp = () => {
.filter((row) => isPersistedArticleRow(tabKey, row))
.map((row) => buildRainFallUtilFactStatusSaveItem(row, list));
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1575,7 +1596,7 @@ const WaterReuseInp = () => {
}
if (tabKey === TreatWaterReuseDesignConstStatusInpColumn.tabKey) {
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const sido = lastSearchedValues.sido ?? "";
@@ -1600,7 +1621,7 @@ const WaterReuseInp = () => {
buildTreatWaterReuseDesignConstStatusSaveItem(row, list),
);
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1640,7 +1661,7 @@ const WaterReuseInp = () => {
}
if (tabKey === TreatWaterReuseBusinessApprStatusInpColumn.tabKey) {
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const noApplicable = buildNoApplicableForRequest(searchContextByTab.get(tabKey) ?? lastSearchedValues, isNotApplicableByTab.get(tabKey) ?? false, getTabGridRows(tabKey).length === 0);
const currentData = getTabGridRows(tabKey);
const sido = lastSearchedValues.sido ?? "";
@@ -1665,7 +1686,7 @@ const WaterReuseInp = () => {
buildTreatWaterReuseBusinessApprStatusSaveItem(row, list),
);
if (!noApplicable && saveList.length === 0) {
if (!noApplicable && saveList.length === 0 && buildSubmitOkForToolbar(searchContextByTab.get(tabKey) ?? lastSearchedValues)) {
alert("해당사항 없음을 체크해주세요.");
return;
}
@@ -1719,11 +1740,7 @@ const WaterReuseInp = () => {
const request = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
userId: userInfo.userUnqId,
};
@@ -1881,12 +1898,8 @@ const WaterReuseInp = () => {
}
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...toInputSearchRegion(sidoListRef.current, lastSearchedValues),
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCdForTab,
@@ -1963,14 +1976,14 @@ const WaterReuseInp = () => {
return map;
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo, columnGuidesByTab, sggOptions]);
const searchContextForToolbar = searchContextByTab.get(activeTab) ?? lastSearchedValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabToolbar =
toolbarSnapshotByTab.get(activeTab) ??
computeTabToolbarSnapshot(
activeTabStatus,
tabRowDataExists(rowDataMap, activeTab),
hasSearched,
lastSearchedValues,
searchContextForToolbar,
inputPeriodEnterable,
isAdminInfo,
);
@@ -1981,6 +1994,7 @@ const WaterReuseInp = () => {
activeTabToolbar.showEnterableToolbarButtons && toolbarGroupAllowed;
const showActionToolbarCard =
activeTabToolbar.showActionToolbarCard && toolbarGroupAllowed;
const submitOk = buildSubmitOkForToolbar(searchContextForToolbar);
const showSecondaryToolbarCard =
hasSearched &&
toolbarGroupAllowed &&
@@ -1995,7 +2009,8 @@ const WaterReuseInp = () => {
activeTab,
tabRowDataExists(rowDataMap, activeTab),
activeTabStatus,
);
) &&
submitOk;
const setIsNotApplicableForActiveTab: Dispatch<SetStateAction<boolean>> = useCallback(
(next) => {
const tabKey = activeTabRef.current;
@@ -2014,9 +2029,8 @@ const WaterReuseInp = () => {
const statsCd = tabStatsCdMap.get(activeTab);
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
...toInputSearchRegion(sidoListRef.current, ctx),
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
@@ -2079,6 +2093,7 @@ const WaterReuseInp = () => {
<>
<div className="relative">
<SearchForm
isInput
key={searchFormRemountKey}
defaultValues={verificationSearchDefaults ?? undefined}
onSubmit={handleSearch}
@@ -2099,10 +2114,10 @@ const WaterReuseInp = () => {
<LastYearDataButton
getLastYearDataFunc={() => void getLastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
{submitOk && <SubmitButton submitFunc={submitFunc} />}
</>
)}
{activeTabStatus?.laeSbmsnSttsCd === "2" && (
{activeTabStatus?.laeSbmsnSttsCd === "2" && submitOk && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
@@ -2161,7 +2176,7 @@ const WaterReuseInp = () => {
tabStatusInfoMap.get(tabKey) ?? null,
inputPeriodEnterable,
) || isAdminInfo) && inputPeriodEnterable;
const gugunOk = buildGugunOkForToolbar(lastSearchedValues);
const gugunOk = buildGugunOkForToolbar(searchContextForToolbar);
const tabStatusInfo = tabStatusInfoMap.get(tabKey) ?? null;
return (
@@ -130,9 +130,9 @@ const getSidoListFetcher = async (): Promise<ApiResponse<SearchCondition>> => {
};
const getSggListFetcher = async (
key: [string, string],
key: [string, string, "Y" | "N"],
): Promise<ApiResponse<Sgg[]>> => {
return statisticsApi.getSggList(key[1]);
return statisticsApi.getSggList(key[1], { gugunSkipYn: key[2] });
};
const LogHistory = () => {
@@ -214,8 +214,14 @@ const LogHistory = () => {
},
);
const sggKey: [string, string] | null =
sido && sido !== "ALL" ? ["log-history-sgg-list", sido] : null;
const selectedSido = sidoList?.data?.sidoList?.find(
(item) => item.sido === sido,
);
const gugunSkipYn = selectedSido?.gugunSkipYn ?? "Y";
const sggKey: [string, string, "Y" | "N"] | null =
sido && sido !== "ALL" && sidoList?.data
? ["log-history-sgg-list", sido, gugunSkipYn]
: null;
const { data: sggList } = useSWR<ApiResponse<Sgg[]>>(
sggKey,
getSggListFetcher,
@@ -3,7 +3,7 @@ import { useCallback, useMemo, useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
import { useAlertStore, useUserStore } from "@/shared/lib";
import { useAlertStore, useSearchConditionSidoList, useUserStore } from "@/shared/lib";
import { statisticsManagementApi } from "@/entities/statistics/api/statistics-management-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import type {
@@ -14,6 +14,7 @@ import type {
ReviewerInfoRequest,
StatsInputStatusApproveItem,
} from "@/entities/statistics/model/statistics-interface";
import { isGugunOkForToolbar } from "@/entities/statistics";
import {
Button,
DataTable,
@@ -33,7 +34,6 @@ import SearchForm from "@/components/module/SearchForm";
import { ReviewSubmitDialog } from "@/components/statistics/review/ReviewSubmitDialog";
import type { DataCellClickCallbackParams } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { StatsInputStatusInfoList } from "@/components/util/column/review/StatsInputStatusRvwColumn";
import { toGridRows } from "@/components/util/grid/gridFunction";
@@ -60,6 +60,7 @@ const mapSewerageListToGridRows = (rawList: StatsInputStatusReviewItem[]) =>
// 하수도 통계 검토 00. 통계 입력 현황
const StatsInputStatusRvw = () => {
const { sidoList } = useSearchConditionSidoList();
const { setMessage: alert } = useAlertStore((state) => state);
const userInfo = useUserStore((state) => state.userInfo);
const { confirm } = useConfirm();
@@ -232,9 +233,7 @@ const StatsInputStatusRvw = () => {
}
};
const gugunOkOrNotDoList = doList.includes(lastSearchedValues?.bsnmCd || "")
? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== "ALL")
: true;
const gugunOkOrNotDoList = isGugunOkForToolbar(sidoList, lastSearchedValues);
const allSubmitStatusApproved = useMemo(
() =>
@@ -3,6 +3,7 @@
import { useEffect, useMemo, useState } from "react";
import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
import * as XLSX from "xlsx";
import { useAlertStore } from "@/shared/lib";
import { NoData } from "@/shared/ui/no-data";
@@ -43,6 +44,7 @@ const GeneralVerificationManagement = () => {
{},
);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isExporting, setIsExporting] = useState<boolean>(false);
const [selectedVrfcRow, setSelectedVrfcRow] = useState<number | null>(null);
const [showRegistForm, setShowRegistForm] = useState<boolean>(false);
@@ -108,6 +110,47 @@ const GeneralVerificationManagement = () => {
setShowRegistForm(false);
};
const handleExportExcel = async () => {
try {
setIsExporting(true);
const response = await generalverificationApiService.getVerifyExcelList();
if (response.status !== "success") {
alert(response.message || "엑셀 데이터 조회 중 오류가 발생했습니다.");
return;
}
const list = response.data ?? [];
if (list.length === 0) {
alert("내보낼 데이터가 없습니다.");
return;
}
const header = [
"지표명",
"검증명",
"검증타입",
"소수점 자리수",
"소수점 자리 처리 방식",
"검증식",
];
const rows = list.map((item) => [
item.trgtStatsNm,
item.vrfcNm,
item.vrfcClsfNm,
item.dcptDigtCnt,
item.dcptPrcsMthNm,
item.vrfcEqtnCn,
]);
const worksheet = XLSX.utils.aoa_to_sheet([header, ...rows]);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "일반검증목록");
XLSX.writeFile(workbook, "일반검증목록.xlsx");
} catch {
alert("엑셀 내보내기 중 오류가 발생했습니다.");
} finally {
setIsExporting(false);
}
};
const handleReset = () => {
const resetValues = { statsCd: "", useYn: "", vrfcClsfCd: "" };
setSearchValues(resetValues);
@@ -210,16 +253,27 @@ const GeneralVerificationManagement = () => {
<h3 className="text-2xl font-medium text-label">
일반 검증 목록
</h3>
<RegistDialog
open={showRegistForm}
onOpenChange={setShowRegistForm}
onSuccess={() => void handleSearch()}
>
<Button>등록</Button>
</RegistDialog>
<div className="flex items-center gap-2">
<Button
className="h-[3.2rem] w-[96px] bg-gradient-green px-6 hover:bg-gradient-green-hover"
type="button"
disabled={isExporting}
onClick={() => void handleExportExcel()}
>
<i className="diveicon di-file-excel" />
<span>내보내기</span>
</Button>
<RegistDialog
open={showRegistForm}
onOpenChange={setShowRegistForm}
onSuccess={() => void handleSearch()}
>
<Button>등록</Button>
</RegistDialog>
</div>
</div>
<div className="card !mx-2 !my-4 h-full overflow-auto !p-0">
{isLoading && (
{(isLoading || isExporting) && (
<div className="[data-theme='black']:bg-transparent absolute inset-0 z-50 flex items-center justify-center rounded-lg p-10">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
@@ -36,10 +36,12 @@ export type SearchFormProps = {
isReset?: boolean;
/** true면 시도·시군구 기본값을 계정 첫 항목이 아니라 「전체」로 둠 (집계표 등) */
defaultRegionAll?: boolean;
/** true면 입력 화면용 시도 목록 API(`getSidoListInput`)를 사용 */
isInput?: boolean;
};
export const defaultSearchFormProps: Required<
Omit<SearchFormProps, "onSubmit" | "onReset" | "onValuesChange" | "defaultValues" | "sidoSelectText" | "searchButtonText" | "defaultRegionAll">
Omit<SearchFormProps, "onSubmit" | "onReset" | "onValuesChange" | "defaultValues" | "sidoSelectText" | "searchButtonText" | "defaultRegionAll" | "isInput">
> = {
sido: true,
gugun: true,
@@ -42,15 +42,15 @@ export const ExcretaTreatFactInpColumn = {
},
// 사업비/용량 정보
{ name: 'wholPjtcoAmt', fieldName: 'wholPjtcoAmt', header: { text: '사업비(백만원)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '시설용량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'wholPjtcoAmt', fieldName: 'wholPjtcoAmt', header: { text: '사업비(백만원)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '시설용량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'cpct', fieldName: 'cpct', header: { text: '처리량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'oprtngDayCnt', fieldName: 'oprtngDayCnt', header: { text: '가동일수(일/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 처리 공법 및 연계
{ name: 'prcsMthdExpln', fieldName: 'prcsMthdExpln', header: { text: '처리공법' } },
{ name: 'linkTmplntCn', fieldName: 'linkTmplntCn', header: { text: '연계처리장명' } },
{ name: 'linkPrcsQnt', fieldName: 'linkPrcsQnt', header: { text: '연계처리량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'linkPrcsQnt', fieldName: 'linkPrcsQnt', header: { text: '연계처리량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 유입수질
{ name: 'inflwBodWqVl', fieldName: 'inflwBodWqVl', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
@@ -77,7 +77,7 @@ export const ExcretaTreatFactInpColumn = {
// 운영 정보
{ name: 'operMthdCdCn', fieldName: 'operMthdCdCn', header: { text: '운영주체(자체/공기업/민간위탁)' } },
{ name: 'wholNope', fieldName: 'wholNope', header: { text: '직원총수(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sftyAcdntEvntcsCnt', fieldName: 'sftyAcdntEvntcsCnt', header: { text: '안전사고건수(건/인/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sftyAcdntEvntcsCnt', fieldName: 'sftyAcdntEvntcsCnt', header: { text: '안전사고건수(건/인/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
@@ -1,6 +1,6 @@
import { ValueType } from "realgrid";
import { footerDecimal1, footerDecimal3,footerInt } from "@/components/util/grid/footerSummary";
import { footerAvgDecimal, footerDecimal1, footerDecimal3, footerInt } from "@/components/util/grid/footerSummary";
/**
* 하수도 통계 관리 > 06. 공공하수처리시설
@@ -97,7 +97,7 @@ export const PublicSewageFactColumn = {
{ name: 'telNo', fieldName: 'telNo', header: { text: '전화번호' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'cmptnStrtYmd', fieldName: 'cmptnStrtYmd', header: { text: '준공일' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'oprtngStrtYmd', fieldName: 'oprtngStrtYmd', header: { text: '가동개시일' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'wholBizVl', fieldName: 'wholBizVl', header: { text: '사업비' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
{ name: 'wholBizVl', fieldName: 'wholBizVl', header: { text: '사업비' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 방류수 소독방법
{ name: 'eflntDsnfMthdExpln', fieldName: 'eflntDsnfMthdExpln', header: { text: '방류수 소독방법' }, editable: false, styleName: 'rg-cell-readonly' },
@@ -118,6 +118,7 @@ export const PublicSewageFactColumn = {
fieldName: 'engySrlneRt',
header: { text: '에너지자립률(%)' },
editable: false,
footer: footerAvgDecimal,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
@@ -163,7 +164,7 @@ export const PublicSewageFactColumn = {
// 기타
{ name: 'intgPrcsFcltCn', fieldName: 'intgPrcsFcltCn', header: { text: '중심처리시설' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'wholNope', fieldName: 'wholNope', header: { text: '직원총수(명)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0', styleName: 'rg-cell-readonly' },
{ name: 'sftyAcdntEvntcsCnt', fieldName: 'sftyAcdntEvntcsCnt', header: { text: '안전사고건수' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sftyAcdntEvntcsCnt', fieldName: 'sftyAcdntEvntcsCnt', header: { text: '안전사고건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false, editable: false, styleName: 'rg-cell-readonly' },
@@ -1,6 +1,6 @@
import { ValueType } from "realgrid";
import { footerDecimal1 } from "@/components/util/grid/footerSummary";
import { footerAvgDecimal, footerDecimal1 } from "@/components/util/grid/footerSummary";
// 하수도 통계 관리 > 11. 하수 및 분뇨찌꺼기(슬러지)처리 > tab 1: 찌꺼기(슬러지)처리시설현황
export const SlurgeProcessingFacStatusInpColumn = {
@@ -37,7 +37,7 @@ export const SlurgeProcessingFacStatusInpColumn = {
{ name: 'transfPrcsQnt', fieldName: 'transfPrcsQnt', header: { text: '이송처리량(m³/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 함수율
{ name: 'srpFncRt', fieldName: 'srpFncRt', header: { text: '함수율(%, 탈수기준)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'srpFncRt', fieldName: 'srpFncRt', header: { text: '함수율(%, 탈수기준)' }, footer: footerAvgDecimal, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 찌꺼기처리량합계
{
@@ -56,7 +56,7 @@ export const SlurgeProcessingFacStatusInpColumn = {
'itslfTs1AltdQnt',
'itslfTs1DryQnt',
'itslfTs1FuelQnt',
'itslfTs2CmpstgQnt',
'itslfTs1CmpstgQnt',
'otsdCnsgnRclmQnt',
'otsdCnsgnIncnrQnt',
'otsdCnsgnFuelPwplQnt',
@@ -86,7 +86,7 @@ export const SlurgeProcessingFacStatusInpColumn = {
'itslfTs1AltdQnt',
'itslfTs1DryQnt',
'itslfTs1FuelQnt',
'itslfTs2CmpstgQnt',
'itslfTs1CmpstgQnt',
],
type: 'sum' as const,
}
@@ -203,7 +203,7 @@ export const SlurgeProcessingFacStatusInpColumn = {
{ name: 'fuelPrcsFcltNm', fieldName: 'fuelPrcsFcltNm', header: { text: '시설(업체명)' } },
// 자체 찌꺼기 처리량 > 퇴비화 - 쿼리에 없음 (임의 생성)
{ name: 'itslfTs2CmpstgQnt', fieldName: 'itslfTs2CmpstgQnt', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'itslfTs1CmpstgQnt', fieldName: 'itslfTs1CmpstgQnt', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 자체 찌꺼기 처리량 > 퇴비화 > 시설(업체명) - 쿼리에 없음 (임의 생성)
{ name: 'cmpsPrcsFcltBzentyNm', fieldName: 'cmpsPrcsFcltBzentyNm', header: { text: '시설(업체명)' } },
@@ -329,7 +329,7 @@ export const SlurgeProcessingFacStatusInpColumn = {
'fuelPrcsFcltNm'
]
},
'itslfTs2CmpstgQnt',
'itslfTs1CmpstgQnt',
'cmpsPrcsFcltBzentyNm'
]
},
@@ -1,6 +1,7 @@
import { ValueType } from "realgrid";
import type { GridTopButtonConfig } from "@/components/type/DataGrid.type";
import { footerInt } from "@/components/util/grid/footerSummary";
// 하수도 통계 관리 > 02.하수관로 > tab 6. 시설별 현황관리
// Tab 6-1: 행정구역별 연결 현황 (오른쪽 그리드)
@@ -51,7 +52,7 @@ export const TreatPlantConnectionInpColumn = {
columns: [
// 하수처리장 정보
{ name: 'fcltNm', fieldName: 'fcltNm', header: { text: '하수처리장명' }, editable: false },
{ name: 'fcltCpct', fieldName: 'fcltCpct', header: { text: '용량' }, valueType: ValueType.NUMBER, dataFormat: '#,##0', editable: false },
{ name: 'fcltCpct', fieldName: 'fcltCpct', header: { text: '용량' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0', editable: false },
{ name: 'fcltSn', fieldName: 'fcltSn', header: { text: '하수처리장코드' }, editable: false },
// 연결여부
{ name: 'isConnection', fieldName: 'isConnection', header: { text: '연결여부' }, editable: false },
@@ -1,6 +1,6 @@
import { ValueType } from "realgrid";
import { footerDecimal2, footerDecimal3,footerInt } from "@/components/util/grid/footerSummary";
import { footerAvgDecimal, footerDecimal2, footerDecimal3, footerInt } from "@/components/util/grid/footerSummary";
// 하수도 통계 관리 > 02.하수관로 > tab 2. 시설별 설치현황
// SeweragePipeInputFactInstallStatusItem + commonInfo.ctpvNm (그리드 행에 병합)
@@ -14,7 +14,7 @@ export const FactInstallStatusInpColumn = {
// 하수처리장 정보
{ name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '하수처리장명' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '하수처리시설용량(m³/일)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '하수처리시설용량(m³/일)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 처리구역면적(km²)
{
@@ -37,6 +37,7 @@ export const FactInstallStatusInpColumn = {
fieldName: 'sprsysVartnRt',
header: { text: '분류식화율' },
editable: false,
footer: footerAvgDecimal,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
@@ -64,10 +64,10 @@ export const RainfallPumpStationInpColumn = {
{ name: 'rsvrYn', fieldName: 'rsvrYn', header: { text: '유수지유무(유,무)' } },
RAINFALL_PUMP_YN_VALUES,
),
{ name: 'rsvrImpndQnt', fieldName: 'rsvrImpndQnt', header: { text: '저류랑(m³)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'rsvrImpndQnt', fieldName: 'rsvrImpndQnt', header: { text: '저류랑(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 흡수정
{ name: 'abstnRgnImpdQnt', fieldName: 'abstnRgnImpdQnt', header: { text: '저류랑(m³)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'abstnRgnImpdQnt', fieldName: 'abstnRgnImpdQnt', header: { text: '저류랑(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 펌프시설 > 총양수량(m³/min)
{
@@ -145,7 +145,7 @@ export const RainfallPumpStationInpColumn = {
{ name: 'ctmnLastDspsMthdExpln', fieldName: 'ctmnLastDspsMthdExpln', header: { text: '협잡물' } },
// 운영실적
{ name: 'oprtngDayCnt', fieldName: 'oprtngDayCnt', header: { text: '가동일수(일/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 입력값 3자리만 가능
{ name: 'oprtngDayCnt', fieldName: 'oprtngDayCnt', header: { text: '가동일수(일/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 입력값 3자리만 가능
{ name: 'grssMltvQnt', fieldName: 'grssMltvQnt', header: { text: '총배수량(m³/년)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 비고
@@ -221,7 +221,7 @@ export const SewageReservoirInpColumn = {
{ name: 'ctmnLastDspsMthdExpln', fieldName: 'ctmnLastDspsMthdExpln', header: { text: '협잡물' } },
// 운영실적
{ name: 'oprtngDayCnt', fieldName: 'oprtngDayCnt', header: { text: '가동일수(일/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 3자리 수만 입력 가능
{ name: 'oprtngDayCnt', fieldName: 'oprtngDayCnt', header: { text: '가동일수(일/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 3자리 수만 입력 가능
{ name: 'grssMltvQnt', fieldName: 'grssMltvQnt', header: { text: '총배수량(m³/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
@@ -100,7 +100,7 @@ export const RainFallUtilFactStatusInpColumn = {
},
// 설치비(백만원)
{ name: 'instlAmt', fieldName: 'instlAmt', header: { text: '설치비(백만원)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'instlAmt', fieldName: 'instlAmt', header: { text: '설치비(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 집수면 종류
{ name: 'rcptMynCn', fieldName: 'rcptMynCn', header: { text: '집수면 종류' } },
@@ -143,7 +143,7 @@ export const RecycledWaterStatusInpColumn = {
),
// 연간운영비(백만원/년)
{ name: 'ruswtrOperAmt', fieldName: 'ruswtrOperAmt', header: { text: '연간운영비(백만원/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ruswtrOperAmt', fieldName: 'ruswtrOperAmt', header: { text: '연간운영비(백만원/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
@@ -21,6 +21,7 @@ import type {
VerifyDetailUpdateRequest,
VerifyResultListRequest,
VerifyResultListResponse,
VerifyExcelDataResponse,
VrfcOperatorsResponse,
} from "../model/general-verification-interface";
@@ -176,6 +177,13 @@ export class GeneralverificationApiService implements GeneralverificationService
);
return response.data;
}
public async getVerifyExcelList(): Promise<VerifyExcelDataResponse> {
const response = await stClient.get<VerifyExcelDataResponse>(
`${ENDPOINT.ST_SERVICE.INPUTVERIFY}/excel-list`,
);
return response.data;
}
}
export const generalverificationApiService =
+14
View File
@@ -10,6 +10,18 @@ export { dashboardApi } from "./api/dashboard/dashboard-service";
export { useScheduleInfo } from "./api/dashboard/schedule-info-queries";
export { scheduleInfoApi } from "./api/dashboard/schedule-info-service";
export { imrApi } from "./api/imr-api";
export {
filterBySelectedAdmdstCd,
findSidoBySearch,
isDoListSido,
isGugunOkForToolbar,
isGugunRequiredSido,
isSubmitOkForToolbar,
resolveNoApplicableForRequest,
toInputSearchRegion,
type InputSearchRegion,
type SidoGugunLookup,
} from "./lib/sido-gugun";
export { noticeTalkSendingApi } from "./api/notice-talk/notice-talk-sending-service";
export {
useNtftalkTemplateList,
@@ -131,6 +143,8 @@ export {
type RejectRequest,
} from "./model/statistics-approval-interface";
export {
type SearchCondition,
type Sido,
type StatsInputStatusApproveBatchRequest,
type StatsInputStatusApproveItem,
type StatsInputStatusApproveKind,
@@ -0,0 +1,133 @@
import { doList } from "@/components/util/column/input/DoList";
import type { Sido } from "../model/statistics-interface";
export type SidoGugunLookup = {
sido?: string | null;
bsnmCd?: string | null;
gugun?: string | null;
};
export function findSidoBySearch(
sidoList: readonly Sido[] | undefined,
query: SidoGugunLookup | null | undefined,
): Sido | undefined {
if (!query) return undefined;
const list = sidoList ?? [];
const sidoName = query.sido?.trim() ?? "";
if (sidoName && sidoName !== "ALL") {
const byName = list.find((item) => item.sido === sidoName);
if (byName) return byName;
}
const bsnmCd = query.bsnmCd?.trim() ?? "";
if (bsnmCd) {
return list.find((item) => item.bsnmCd === bsnmCd);
}
return undefined;
}
/** `gugunSkipYn === "N"` 이면 시군구 선택 필수 (검색폼·시군구 API용) */
export function isGugunRequiredSido(
sidoList: readonly Sido[] | undefined,
query: SidoGugunLookup | null | undefined,
): boolean {
return findSidoBySearch(sidoList, query)?.gugunSkipYn === "N";
}
function hasSpecificGugun(values: SidoGugunLookup | null | undefined): boolean {
const gugun = values?.gugun?.trim() ?? "";
return Boolean(gugun && gugun !== "ALL");
}
/** 시도 단위 사업자코드가 `doList`(도)에 해당하는지. 시군구 코드는 쓰지 않는다. */
export function isDoListSido(
sidoList: readonly Sido[] | undefined,
values: SidoGugunLookup | null | undefined,
): boolean {
const sido = findSidoBySearch(sidoList, { sido: values?.sido });
if (sido?.bsnmCd) return doList.includes(sido.bsnmCd);
if (hasSpecificGugun(values)) return false;
return doList.includes((values?.bsnmCd ?? "").trim());
}
/** 도: 시군구 필수. 광역시·세종·제주: 시군구와 무관하게 통과 */
export function isGugunOkForToolbar(
sidoList: readonly Sido[] | undefined,
values: SidoGugunLookup | null | undefined,
): boolean {
if (!isDoListSido(sidoList, values)) return true;
return hasSpecificGugun(values);
}
/** 도: 시군구 필수. 광역시·세종·제주: 시군구가 선택돼 있으면 제출/제출취소·해당사항 없음 숨김 */
export function isSubmitOkForToolbar(
sidoList: readonly Sido[] | undefined,
values: SidoGugunLookup | null | undefined,
): boolean {
const hasGugun = hasSpecificGugun(values);
if (isDoListSido(sidoList, values)) return hasGugun;
return !hasGugun;
}
/**
* 저장·제출 `noApplicable`.
* 해당사항 없음 버튼이 보이는 경우(도, 또는 비도+시군구 전체)는 체크 여부.
* 버튼이 숨겨진 비도+시군구에서는 saveList가 비면 true.
*/
export function resolveNoApplicableForRequest(
sidoList: readonly Sido[] | undefined,
values: SidoGugunLookup | null | undefined,
checked: boolean,
isSaveListEmpty: boolean,
): boolean {
if (isSubmitOkForToolbar(sidoList, values)) return checked;
return isSaveListEmpty;
}
export type InputSearchRegion = {
sewerBzmnCd: string;
sggNm?: string;
};
/**
* 입력 화면 조회·저장·제출용 지역 조건.
* `doList`에 없으면 시군구와 무관하게 시도 `sewerBzmnCd`만 쓰고 `sggNm`은 생략한다.
*/
export function toInputSearchRegion(
sidoList: readonly Sido[] | undefined,
values: SidoGugunLookup | null | undefined,
): InputSearchRegion {
const sido = findSidoBySearch(sidoList, { sido: values?.sido });
if (!isDoListSido(sidoList, values)) {
return {
sewerBzmnCd: (sido?.bsnmCd ?? "").trim(),
};
}
const gugun = values?.gugun?.trim() ?? "";
return {
sewerBzmnCd: (values?.bsnmCd || sido?.bsnmCd || "").trim(),
sggNm: gugun && gugun !== "ALL" ? gugun : "",
};
}
/**
* `doList`가 아닌 시도에서 시군구가 선택된 경우,
* 조회 결과를 `admdstCd`가 선택 시군구 코드와 같은 행만 남긴다.
*/
export function filterBySelectedAdmdstCd<T>(
rows: readonly T[] | null | undefined,
sidoList: readonly Sido[] | undefined,
values: SidoGugunLookup | null | undefined,
): T[] {
const list = rows ? [...rows] : [];
if (isDoListSido(sidoList, values)) return list;
if (!hasSpecificGugun(values)) return list;
const selectedCd = (values?.bsnmCd ?? "").trim();
if (!selectedCd) return list;
return list.filter((row) => {
const cd = String(
(row as { admdstCd?: unknown }).admdstCd ?? "",
).trim();
return cd === selectedCd;
});
}
@@ -264,6 +264,22 @@ export interface VerifyResultListResponse {
httpStatus?: number;
}
/** 검증 엑셀 데이터 조회 — 요청 파라미터 없음 */
export interface VerifyExcelDataItem {
trgtStatsNm: string;
vrfcNm: string;
vrfcClsfNm: string;
dcptDigtCnt: number;
dcptPrcsMthNm: string;
vrfcEqtnCn: string;
}
export interface VerifyExcelDataResponse {
status: string;
message?: string;
data: VerifyExcelDataItem[];
}
export interface GeneralverificationService {
getGeneralverificationList(
param: GeneralverificationRequest,
@@ -305,4 +321,5 @@ export interface GeneralverificationService {
getVerifyResultList(
param: VerifyResultListRequest,
): Promise<VerifyResultListResponse>;
getVerifyExcelList(): Promise<VerifyExcelDataResponse>;
}
@@ -136,7 +136,7 @@ export type ExcretaTreatmentFacilitySaveItem = Omit<
export interface ExcretaTreatmentFacilitySaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -157,7 +157,7 @@ export type ExcretaTreatmentFacilitySubmitItem = ExcretaTreatmentFacilitySaveIte
export interface ExcretaTreatmentFacilitySubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
@@ -230,7 +230,7 @@ export interface SewageTreatFactStatusSaveItem {
export interface SewageTreatFactStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -248,7 +248,7 @@ export interface SewageTreatFactStatusSaveResponse {
export interface SewageTreatFactStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -316,7 +316,7 @@ export interface SepticTankStatusSaveItem {
export interface SepticTankStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -334,7 +334,7 @@ export interface SepticTankStatusSaveResponse {
export interface SepticTankStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -379,7 +379,7 @@ export interface PrivateSewageDesigStatusSaveItem {
export interface PrivateSewageDesigStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -397,7 +397,7 @@ export interface PrivateSewageDesigStatusSaveResponse {
export interface PrivateSewageDesigStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -129,7 +129,7 @@ export type PumpingStationSaveItem = Omit<
export interface PumpingStationSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -150,7 +150,7 @@ export type PumpingStationSubmitItem = PumpingStationSaveItem;
export interface PumpingStationSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
@@ -228,7 +228,7 @@ export type PrivateSewageTreatRelatedIndustrySaveItem = Omit<
export interface SewageExcretaRelatedSaveRequestBase {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -90,7 +90,7 @@ export interface SlurgeProcessingFacStatusItem {
fuelPrcsIncnrQnt: number;
fuelPrcsFuelQnt: number;
fuelPrcsFcltNm: string;
itslfTs2CmpstgQnt: number;
itslfTs1CmpstgQnt: number;
cmpsPrcsFcltBzentyNm: string;
otsdCnsgnWhol: number;
otsdCnsgnRclmQnt: number;
@@ -161,7 +161,7 @@ export type SlurgeProcessingFacStatusSaveItem = Omit<
export interface SlurgeProcessingFacStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -180,7 +180,7 @@ export type SlurgeProcessingFacStatusSubmitItem = SlurgeProcessingFacStatusSaveI
export interface SlurgeProcessingFacStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -271,7 +271,7 @@ export type DigestorOperatingStatusSaveItem = Omit<
export interface DigestorOperatingStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -290,7 +290,7 @@ export type DigestorOperatingStatusSubmitItem = DigestorOperatingStatusSaveItem;
export interface DigestorOperatingStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -374,7 +374,7 @@ export type SewageSlurgeProcessingFacStatusSaveItem = Omit<
export interface SewageSlurgeProcessingFacStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -394,7 +394,7 @@ export type SewageSlurgeProcessingFacStatusSubmitItem =
export interface SewageSlurgeProcessingFacStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -483,7 +483,7 @@ export type ExcretaSlurgeProcessingStatusSaveItem = Omit<
export interface ExcretaSlurgeProcessingStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -501,7 +501,7 @@ export type ExcretaSlurgeProcessingStatusSubmitItem = ExcretaSlurgeProcessingSta
export interface ExcretaSlurgeProcessingStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -122,7 +122,7 @@ export interface SewagePipeMaintainRepairSaveItem {
export interface SewagePipeMaintainRepairSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -139,7 +139,7 @@ export interface SewagePipeMaintainRepairSaveResponse {
export interface SewagePipeMaintainRepairSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -193,7 +193,7 @@ export interface SewagePipeMaintainDredgingSaveItem {
export interface SewagePipeMaintainDredgingSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -210,7 +210,7 @@ export interface SewagePipeMaintainDredgingSaveResponse {
export interface SewagePipeMaintainDredgingSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -211,7 +211,7 @@ export type SewageReservoirSaveItem = Omit<
export interface SewageReservoirSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -230,7 +230,7 @@ export interface SewageReservoirSaveResponse {
export interface SewageReservoirSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -301,7 +301,7 @@ export type RainfallPumpStationSaveItem = Omit<
export interface RainfallPumpStationSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -317,7 +317,7 @@ export type RainfallPumpStationSaveResponse = SewageReservoirSaveResponse;
export interface RainfallPumpStationSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -130,7 +130,7 @@ export interface SewerageFinanceSettleSaveItem {
export interface SewerageFinanceSettleSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -147,7 +147,7 @@ export interface SewerageFinanceSettleSaveResponse {
export interface SewerageFinanceSettleSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -175,7 +175,7 @@ export interface SewerageFinanceSettleSubmitResponse {
export interface SewerageFinanceSubmitRequestBase {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -268,7 +268,7 @@ export interface SewerageChargeSaveItem {
export interface SewerageChargeSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -392,7 +392,7 @@ export interface SewerageUsageFeeSaveItem {
export interface SewerageUsageFeeSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -486,7 +486,7 @@ export interface CauserBurdenSaveItem {
export interface CauserBurdenSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -147,7 +147,7 @@ export interface SewerageServiceLevelComplaintSaveItem {
export interface SewerageServiceLevelComplaintSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -183,7 +183,7 @@ export interface SewerageServiceLevelResidentFriendlyFactSaveItem {
export interface SewerageServiceLevelResidentFriendlyFactSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -204,7 +204,7 @@ export type SewerageServiceLevelComplaintSubmitItem = SewerageServiceLevelCompla
export interface SewerageServiceLevelComplaintSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -227,7 +227,7 @@ export type SewerageServiceLevelResidentFriendlyFactSubmitItem =
export interface SewerageServiceLevelResidentFriendlyFactSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -127,7 +127,7 @@ export interface PublicSewageTreatWaterReuseSaveItem {
export interface PublicSewageTreatWaterReuseSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -145,7 +145,7 @@ export type PublicSewageTreatWaterReuseSubmitItem = PublicSewageTreatWaterReuseS
export interface PublicSewageTreatWaterReuseSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -238,7 +238,7 @@ export type RecycledWaterStatusSaveItem = Omit<
export interface RecycledWaterStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -257,7 +257,7 @@ export type RecycledWaterStatusSubmitItem = RecycledWaterStatusSaveItem;
export interface RecycledWaterStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -353,7 +353,7 @@ export type RainFallUtilFactStatusSaveItem = Omit<
export interface RainFallUtilFactStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -372,7 +372,7 @@ export type RainFallUtilFactStatusSubmitItem = RainFallUtilFactStatusSaveItem;
export interface RainFallUtilFactStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -452,7 +452,7 @@ export type TreatWaterReuseDesignConstStatusSaveItem = Omit<
export interface TreatWaterReuseDesignConstStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -472,7 +472,7 @@ export type TreatWaterReuseDesignConstStatusSubmitItem =
export interface TreatWaterReuseDesignConstStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -552,7 +552,7 @@ export type TreatWaterReuseBusinessApprStatusSaveItem = Omit<
export interface TreatWaterReuseBusinessApprStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -572,7 +572,7 @@ export type TreatWaterReuseBusinessApprStatusSubmitItem =
export interface TreatWaterReuseBusinessApprStatusSubmitRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -601,7 +601,7 @@ export interface TreatWaterReuseBusinessApprStatusSubmitResponse {
export interface WaterReuseOperationDataUpdateRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
}
@@ -143,7 +143,7 @@ export interface PublicSewageMainFacilityResponse {
export interface PublicSewageMappingTargetsRequestParams {
sewerBzmnCd: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
statsYr: string;
fcltSn: string;
}
@@ -229,7 +229,7 @@ export interface PublicSewageFacilitySaveItem {
export interface PublicSewageFacilitySaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -253,7 +253,7 @@ export interface PublicSewageFacilitySaveResponse {
export interface PublicSewageFacilitySubmitRequestBase {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
noApplicable?: boolean;
@@ -284,7 +284,7 @@ export interface PublicSewageFacilitySubmitResponse {
export interface PublicSewageFacilityOperationDataUpdateRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
}
@@ -129,7 +129,7 @@ export type SeweragePipeInputFactInstallStatusSaveItem = Omit<
export interface SeweragePipeInputRegionalFactStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -146,7 +146,7 @@ export interface SeweragePipeInputRegionalFactStatusSaveResponse {
export interface SeweragePipeInputSubmitRequestBase {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
laePicNm: string;
@@ -176,7 +176,7 @@ export interface SeweragePipeInputRegionalFactStatusSubmitResponse {
export interface SeweragePipeInputFactInstallStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -317,7 +317,7 @@ export interface SeweragePipeInputPipeTypeStatusSaveItem {
export interface SeweragePipeInputPipeTypeStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -389,7 +389,7 @@ export interface SeweragePipeInputPipeDiameterStatusSaveItem {
export interface SeweragePipeInputPipeDiameterStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -487,7 +487,7 @@ export interface SeweragePipeInputManholeStatusSaveItem {
export interface SeweragePipeInputManholeStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
@@ -602,7 +602,7 @@ export interface SeweragePipeFactConnectionSaveItem {
/** 시설별 연결관계 저장 요청 */
export interface SeweragePipeFactConnectionSaveRequest {
ctpvNm: string;
sggNm: string;
sggNm?: string;
fcltSn: string;
statsYr: string;
swtrmtFcltCd: string;
@@ -120,7 +120,7 @@ export interface SewerageSupplyRateSaveItem {
export interface SewerageSupplyRateSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sggNm?: string;
sewerBzmnCd: string;
userId: string;
sewerBzmnArea?: number;
+6
View File
@@ -11,4 +11,10 @@ export {
useActiveTabRegAuthorityAllowed,
useStatisticsInputToolbarGroupGate,
} from "./use-statistics-input-toolbar-group-gate";
export {
fetchSearchCondition,
fetchSearchConditionInput,
SEARCH_CONDITION_SWR_KEY,
useSearchConditionSidoList,
} from "./use-search-condition-sido-list";
export { useUserStore } from "./use-user-store";
@@ -1,8 +1,9 @@
import { menuMatchesStatisticsTabId } from "@/shared/lib/statistics-menu-url";
import type { MenuItem } from "@/entities/menu";
import { isGugunRequiredSido } from "@/entities/statistics";
import type { Sido } from "@/entities/statistics/model/statistics-interface";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doNameList } from "@/components/util/column/input/DoList";
import {
EXCRETA_TREATMENT_FACILITY_INP_TAB_KEYS,
INPUT_TAB_KEY_BY_STATS_CD,
@@ -30,12 +31,17 @@ export type StatisticsInputPendingSearch = {
export function verificationSearchToFormValues(
s: StatisticsInputPendingSearch,
sidoList?: readonly Sido[],
): SearchFormValues {
const ctpvNmTrimmed = s.ctpvNm?.trim() ?? "";
const sggNmTrimmed = s.sggNm?.trim() ?? "";
/** 시군구 필수 도(`DoList.ts`의 `doNameList`)일 때만 API `sggNm`을 검색폼에 반영 */
/** 시군구 필수 시도(`gugunSkipYn === "N"`)일 때만 API `sggNm`을 검색폼에 반영 */
const useSggNm =
sggNmTrimmed.length > 0 && doNameList.includes(ctpvNmTrimmed);
sggNmTrimmed.length > 0 &&
isGugunRequiredSido(sidoList, {
sido: ctpvNmTrimmed,
bsnmCd: s.sewerBzmnCd,
});
return {
year: s.statsYr,
@@ -0,0 +1,61 @@
"use client";
import useSWR from "swr";
import type {
ApiResponse,
SearchCondition,
Sido,
} from "@/entities/statistics/model/statistics-interface";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { useUserStore } from "./use-user-store";
export type SearchConditionKind = "default" | "input";
export const SEARCH_CONDITION_SWR_KEY = (
instSeCd: string,
kind: SearchConditionKind = "default",
) =>
[
kind === "input" ? "sido-list-input" : "sido-list",
instSeCd,
] as const;
export async function fetchSearchCondition(
key: readonly [string, string],
): Promise<ApiResponse<SearchCondition>> {
return statisticsApi.getSidoList({ instSeCd: key[1] });
}
export async function fetchSearchConditionInput(
key: readonly [string, string],
): Promise<ApiResponse<SearchCondition>> {
return statisticsApi.getSidoListInput({ instSeCd: key[1] });
}
const SIDO_LIST_SWR_OPTIONS = {
revalidateOnFocus: false,
revalidateOnReconnect: false,
revalidateIfStale: false,
dedupingInterval: 2000,
shouldRetryOnError: false,
} as const;
/** SearchForm과 동일한 SWR 키로 시도 목록(`gugunSkipYn` 포함)을 재사용합니다. */
export function useSearchConditionSidoList(options?: { isInput?: boolean }): {
sidoList: Sido[];
isReady: boolean;
} {
const instSeCd = useUserStore((state) => state.userInfo?.instSeCd ?? "");
const kind: SearchConditionKind = options?.isInput ? "input" : "default";
const { data } = useSWR(
SEARCH_CONDITION_SWR_KEY(instSeCd, kind),
kind === "input" ? fetchSearchConditionInput : fetchSearchCondition,
SIDO_LIST_SWR_OPTIONS,
);
return {
sidoList: data?.data?.sidoList ?? [],
isReady: data != null,
};
}
@@ -6,6 +6,7 @@ import {
type StatisticsInputPendingSearch,
verificationSearchToFormValues,
} from "@/shared/lib/stats-cd-input-navigation";
import { useSearchConditionSidoList } from "@/shared/lib/use-search-condition-sido-list";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { useStatisticsInputFocusStore } from "@/store/statistics-input-focus-store";
@@ -43,9 +44,11 @@ export function useStatisticsInputPendingNavigation({
const activeTabId = useTabStore((s) => s.activeTabId);
const inputFocusNonce = useStatisticsInputFocusStore((s) => s.nonce);
const { sidoList, isReady } = useSearchConditionSidoList({ isInput: true });
useLayoutEffect(() => {
if (activeTabId !== urlPath) return;
if (!isReady) return;
const pending = useStatisticsInputFocusStore
.getState()
.takePendingForUrl(urlPath);
@@ -54,12 +57,15 @@ export function useStatisticsInputPendingNavigation({
handlersRef.current.onPending?.(pending);
if (pending.search) {
const values = verificationSearchToFormValues(pending.search);
const values = verificationSearchToFormValues(
pending.search,
sidoList,
);
setVerificationSearchDefaults(values);
autoSearchRef.current = { values, tabKey: pending.tabKey };
setSearchFormRemountKey((k) => k + 1);
}
}, [activeTabId, inputFocusNonce, urlPath]);
}, [activeTabId, inputFocusNonce, urlPath, isReady, sidoList]);
useEffect(() => {
const job = autoSearchRef.current;