[st] feat: 입력 저장/제출 로직 수정, 통계입력관리, 검토 반려/제출
This commit is contained in:
@@ -36,6 +36,38 @@
|
||||
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-vrf-prcs-ok {
|
||||
@apply !font-semibold !text-[#3372cf];
|
||||
}
|
||||
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-stats-status-pending {
|
||||
@apply !text-gray-500;
|
||||
}
|
||||
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-stats-status-submitted {
|
||||
@apply !font-semibold
|
||||
}
|
||||
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-stats-status-approved {
|
||||
@apply !font-semibold
|
||||
}
|
||||
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-stats-status-rejected {
|
||||
@apply !font-semibold !text-[#ff6c64];
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-stats-status-rejected > * {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center;
|
||||
width: max-content;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-stats-status-rejected > *::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
margin-left: 0.25rem;
|
||||
vertical-align: middle;
|
||||
width: 0.6125rem;
|
||||
height: 0.6125rem;
|
||||
border-radius: 9999px;
|
||||
background: #ff6c64;
|
||||
box-shadow: 0 0 0 0 rgba(255, 108, 100, 0.75);
|
||||
animation: rg-cell-stats-status-dot-ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
|
||||
}
|
||||
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-total {
|
||||
@apply !bg-[#e3eaf2];
|
||||
}
|
||||
@@ -141,6 +173,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rg-cell-stats-status-dot-ping {
|
||||
75%,
|
||||
100% {
|
||||
box-shadow: 0 0 0 0.5rem rgba(255, 108, 100, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-content {
|
||||
margin-top: 0.6rem !important;
|
||||
}
|
||||
|
||||
@@ -918,9 +918,17 @@ const DataGrid = ({
|
||||
rowIndicator: { visible: false },
|
||||
});
|
||||
|
||||
// footer가 있는 컬럼이 있으면 footer 높이 설정
|
||||
const hasFooter = columns.some((col) => col.footer);
|
||||
if (hasFooter) {
|
||||
const applyFooterSettings = (
|
||||
targetGridView: typeof gridView,
|
||||
) => {
|
||||
const hasFooter = columns.some((col) => col.footer);
|
||||
if (!hasFooter) return;
|
||||
|
||||
targetGridView.setOptions({
|
||||
summaryMode: "aggregate",
|
||||
footer: { visible: true },
|
||||
});
|
||||
|
||||
// footer가 배열이거나 multiRow가 true인 footer가 있으면 멀티푸터 설정 (여러 행)
|
||||
const hasArrayFooter = columns.some(
|
||||
(col) => col.footer && Array.isArray(col.footer),
|
||||
@@ -935,23 +943,24 @@ const DataGrid = ({
|
||||
);
|
||||
});
|
||||
if (hasArrayFooter || hasMultiRowFooter) {
|
||||
// footer 배열의 길이를 확인하여 행 수 결정
|
||||
const arrayFooterLengths = columns
|
||||
.filter((col) => col.footer && Array.isArray(col.footer))
|
||||
.map((col) => (col.footer as unknown[]).length);
|
||||
const maxFooterLength =
|
||||
arrayFooterLengths.length > 0
|
||||
? Math.max(...arrayFooterLengths)
|
||||
: 2; // 기본값 2
|
||||
: 2;
|
||||
const footerHeights = Array.from(
|
||||
{ length: maxFooterLength },
|
||||
() => ({ height: 40 }),
|
||||
);
|
||||
gridView.setFooters?.(footerHeights);
|
||||
targetGridView.setFooters?.(footerHeights);
|
||||
} else {
|
||||
gridView.footer.height = 40;
|
||||
targetGridView.footer.height = 40;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
applyFooterSettings(gridView);
|
||||
|
||||
// 고정 컬럼 설정
|
||||
if (fixedColumns !== undefined && fixedColumns > 0) {
|
||||
@@ -960,10 +969,6 @@ const DataGrid = ({
|
||||
});
|
||||
}
|
||||
|
||||
gridView.setOptions({
|
||||
summaryMode: "aggregate",
|
||||
});
|
||||
|
||||
// footerUserSpans가 있는 경우 setColumnLayout으로 설정
|
||||
const checkFooterUserSpans = (layout: unknown[]): boolean => {
|
||||
return layout.some((item) => {
|
||||
@@ -986,7 +991,9 @@ const DataGrid = ({
|
||||
});
|
||||
};
|
||||
|
||||
if (checkFooterUserSpans(columnLayout)) {
|
||||
const hasFooterUserSpans = checkFooterUserSpans(columnLayout);
|
||||
|
||||
if (hasFooterUserSpans) {
|
||||
// setColumnLayout은 그리드가 완전히 렌더링된 후 호출해야 함
|
||||
setTimeout(() => {
|
||||
if (gridRef.current?.gridView && columnLayout) {
|
||||
@@ -1049,6 +1056,10 @@ const DataGrid = ({
|
||||
(currentGridView as GridViewWithSetColumns).setColumns?.(
|
||||
columnsWithFooter,
|
||||
);
|
||||
applyFooterSettings(currentGridView);
|
||||
if (hasFooterUserSpans) {
|
||||
currentGridView.setColumnLayout?.(columnLayout);
|
||||
}
|
||||
|
||||
// setColumns 호출 후 footer 배열의 valueCallback을 다시 설정
|
||||
setTimeout(() => {
|
||||
@@ -1089,6 +1100,11 @@ const DataGrid = ({
|
||||
);
|
||||
}
|
||||
});
|
||||
applyFooterSettings(currentGridView);
|
||||
if (hasFooterUserSpans) {
|
||||
currentGridView.setColumnLayout?.(columnLayout);
|
||||
}
|
||||
currentGridView.refresh?.();
|
||||
}, 50);
|
||||
} catch (e) {
|
||||
console.warn("Failed to set columns with footer array:", e);
|
||||
|
||||
@@ -109,6 +109,8 @@ export interface VerificationResultDialogProps {
|
||||
queryContext?: VerificationResultDialogQueryContext | null;
|
||||
title?: string;
|
||||
emptyMessage?: string;
|
||||
/** 예외 제출 API 성공 후 부모(입력 화면)에서 메인 데이터를 다시 조회할 때 사용 */
|
||||
onExceptionSubmitted?: () => void | Promise<void>;
|
||||
}
|
||||
|
||||
export function VerificationResultDialog({
|
||||
@@ -117,6 +119,7 @@ export function VerificationResultDialog({
|
||||
queryContext,
|
||||
title = "검증 결과",
|
||||
emptyMessage = "표시할 검증 결과가 없습니다.",
|
||||
onExceptionSubmitted,
|
||||
}: VerificationResultDialogProps) {
|
||||
/** 부모가 매 렌더마다 새 객체를 넘기면 queryContext 참조만 바뀌어 loadInitial이 재실행되므로 필드 값 기준으로 안정화 */
|
||||
const stableQueryContext = useMemo((): VerificationResultDialogQueryContext | null => {
|
||||
@@ -385,6 +388,11 @@ export function VerificationResultDialog({
|
||||
});
|
||||
if (response.status === "success") {
|
||||
alert("예외 제출 처리되었습니다.");
|
||||
try {
|
||||
await onExceptionSubmitted?.();
|
||||
} catch {
|
||||
alert("예외 제출은 완료되었으나 화면 데이터를 다시 불러오지 못했습니다. 조회로 새로고침 해주세요.");
|
||||
}
|
||||
onOpenChange(false);
|
||||
} else {
|
||||
alert(response.message ?? "예외 제출 처리 중 오류가 발생했습니다.");
|
||||
@@ -396,9 +404,10 @@ export function VerificationResultDialog({
|
||||
alert,
|
||||
imrCheckedIndices,
|
||||
imrRows,
|
||||
loadInitial,
|
||||
normalCheckedIndices,
|
||||
normalRows,
|
||||
onExceptionSubmitted,
|
||||
onOpenChange,
|
||||
stableQueryContext,
|
||||
]);
|
||||
|
||||
|
||||
@@ -24,12 +24,12 @@ import { VerificationResultHistoryPagination } from "@/components/module/Verific
|
||||
import type { CellStyleCallbackParams, ColumnInfo } from "@/components/type/DataGrid.type";
|
||||
|
||||
type VerificationResultGridRow = Record<string, string | number>;
|
||||
type VerificationHistoryTabKey = "NV" | "IMR" | "HR";
|
||||
type VerificationHistoryTabKey = "NV" | "IMR" | "HB";
|
||||
|
||||
const verificationHistoryTabs: { value: VerificationHistoryTabKey; label: string }[] = [
|
||||
{ value: "NV", label: "일반검증 내역" },
|
||||
{ value: "IMR", label: "I-MR 검증내역" },
|
||||
{ value: "HR", label: "HR 검증내역" },
|
||||
{ value: "HB", label: "HB 검증내역" },
|
||||
];
|
||||
|
||||
function prcsYnCodeToNumber(raw: string | number | undefined): number | undefined {
|
||||
@@ -90,32 +90,32 @@ export function VerificationResultHistoryDialog({
|
||||
const [activeTab, setActiveTab] = useState<VerificationHistoryTabKey>("NV");
|
||||
const [normalEntries, setNormalEntries] = useState<VerificationResultReportItem[]>([]);
|
||||
const [imrEntries, setImrEntries] = useState<VerificationResultReportItem[]>([]);
|
||||
const [hrEntries, setHrEntries] = useState<VerificationResultReportItem[]>([]);
|
||||
const [hbEntries, setHbEntries] = useState<VerificationResultReportItem[]>([]);
|
||||
const [normalPageIndex, setNormalPageIndex] = useState(0);
|
||||
const [imrPageIndex, setImrPageIndex] = useState(0);
|
||||
const [hrPageIndex, setHrPageIndex] = useState(0);
|
||||
const [hbPageIndex, setHbPageIndex] = useState(0);
|
||||
const [normalPageCount, setNormalPageCount] = useState(1);
|
||||
const [imrPageCount, setImrPageCount] = useState(1);
|
||||
const [hrPageCount, setHrPageCount] = useState(1);
|
||||
const [hbPageCount, setHbPageCount] = useState(1);
|
||||
const [normalLoading, setNormalLoading] = useState(false);
|
||||
const [imrLoading, setImrLoading] = useState(false);
|
||||
const [hrLoading, setHrLoading] = useState(false);
|
||||
const [hbLoading, setHbLoading] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
|
||||
const resetState = useCallback(() => {
|
||||
setActiveTab("NV");
|
||||
setNormalEntries([]);
|
||||
setImrEntries([]);
|
||||
setHrEntries([]);
|
||||
setHbEntries([]);
|
||||
setNormalPageIndex(0);
|
||||
setImrPageIndex(0);
|
||||
setHrPageIndex(0);
|
||||
setHbPageIndex(0);
|
||||
setNormalPageCount(1);
|
||||
setImrPageCount(1);
|
||||
setHrPageCount(1);
|
||||
setHbPageCount(1);
|
||||
setNormalLoading(false);
|
||||
setImrLoading(false);
|
||||
setHrLoading(false);
|
||||
setHbLoading(false);
|
||||
setErrorMessage(null);
|
||||
}, []);
|
||||
|
||||
@@ -147,34 +147,34 @@ export function VerificationResultHistoryDialog({
|
||||
setErrorMessage(null);
|
||||
setNormalLoading(true);
|
||||
setImrLoading(true);
|
||||
setHrLoading(true);
|
||||
setHbLoading(true);
|
||||
setNormalPageIndex(0);
|
||||
setImrPageIndex(0);
|
||||
setHrPageIndex(0);
|
||||
setHbPageIndex(0);
|
||||
try {
|
||||
const [nvRes, imrRes, hrRes] = await Promise.all([
|
||||
const [nvRes, imrRes, hbRes] = await Promise.all([
|
||||
fetchVerificationResultPage("NV", 0),
|
||||
fetchVerificationResultPage("IMR", 0),
|
||||
fetchVerificationResultPage("HR", 0),
|
||||
fetchVerificationResultPage("HB", 0),
|
||||
]);
|
||||
setNormalEntries(nvRes?.data?.verifyResultList ?? []);
|
||||
setImrEntries(imrRes?.data?.verifyResultList ?? []);
|
||||
setHrEntries(hrRes?.data?.verifyResultList ?? []);
|
||||
setHbEntries(hbRes?.data?.verifyResultList ?? []);
|
||||
setNormalPageCount(nvRes?.data?.pageInfo?.totalPages ?? 1);
|
||||
setImrPageCount(imrRes?.data?.pageInfo?.totalPages ?? 1);
|
||||
setHrPageCount(hrRes?.data?.pageInfo?.totalPages ?? 1);
|
||||
setHbPageCount(hbRes?.data?.pageInfo?.totalPages ?? 1);
|
||||
} catch {
|
||||
setNormalEntries([]);
|
||||
setImrEntries([]);
|
||||
setHrEntries([]);
|
||||
setHbEntries([]);
|
||||
setNormalPageCount(1);
|
||||
setImrPageCount(1);
|
||||
setHrPageCount(1);
|
||||
setHbPageCount(1);
|
||||
setErrorMessage("검증 결과 내역 조회 중 오류가 발생했습니다.");
|
||||
} finally {
|
||||
setNormalLoading(false);
|
||||
setImrLoading(false);
|
||||
setHrLoading(false);
|
||||
setHbLoading(false);
|
||||
}
|
||||
}, [fetchVerificationResultPage, queryContext]);
|
||||
|
||||
@@ -216,23 +216,23 @@ export function VerificationResultHistoryDialog({
|
||||
[fetchVerificationResultPage, imrPageIndex, queryContext],
|
||||
);
|
||||
|
||||
const handleHrPageChange = useCallback(
|
||||
const handleHbPageChange = useCallback(
|
||||
async (pageIndex: number) => {
|
||||
if (pageIndex === hrPageIndex || !queryContext) return;
|
||||
if (pageIndex === hbPageIndex || !queryContext) return;
|
||||
setErrorMessage(null);
|
||||
setHrLoading(true);
|
||||
setHbLoading(true);
|
||||
try {
|
||||
const response = await fetchVerificationResultPage("HR", pageIndex);
|
||||
setHrEntries(response?.data?.verifyResultList ?? []);
|
||||
setHrPageCount(response?.data?.pageInfo?.totalPages ?? 1);
|
||||
setHrPageIndex(pageIndex);
|
||||
const response = await fetchVerificationResultPage("HB", pageIndex);
|
||||
setHbEntries(response?.data?.verifyResultList ?? []);
|
||||
setHbPageCount(response?.data?.pageInfo?.totalPages ?? 1);
|
||||
setHbPageIndex(pageIndex);
|
||||
} catch {
|
||||
setErrorMessage("HR 검증내역 조회 중 오류가 발생했습니다.");
|
||||
setErrorMessage("HB 검증내역 조회 중 오류가 발생했습니다.");
|
||||
} finally {
|
||||
setHrLoading(false);
|
||||
setHbLoading(false);
|
||||
}
|
||||
},
|
||||
[fetchVerificationResultPage, hrPageIndex, queryContext],
|
||||
[fetchVerificationResultPage, hbPageIndex, queryContext],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -260,7 +260,7 @@ export function VerificationResultHistoryDialog({
|
||||
|
||||
const normalRows = useMemo(() => mapRows(normalEntries), [normalEntries]);
|
||||
const imrRows = useMemo(() => mapRows(imrEntries), [imrEntries]);
|
||||
const hrRows = useMemo(() => mapRows(hrEntries), [hrEntries]);
|
||||
const hbRows = useMemo(() => mapRows(hbEntries), [hbEntries]);
|
||||
|
||||
const noopSetRows = ((_: SetStateAction<Record<string, string | number>[]>) => {}) as Dispatch<
|
||||
SetStateAction<Record<string, string | number>[]>
|
||||
@@ -332,8 +332,8 @@ export function VerificationResultHistoryDialog({
|
||||
<TabsContent value="IMR" className="rounded-md border border-border">
|
||||
{renderGrid(imrRows, imrPageIndex, imrPageCount, handleImrPageChange, imrLoading)}
|
||||
</TabsContent>
|
||||
<TabsContent value="HR" className="rounded-md border border-border">
|
||||
{renderGrid(hrRows, hrPageIndex, hrPageCount, handleHrPageChange, hrLoading)}
|
||||
<TabsContent value="HB" className="rounded-md border border-border">
|
||||
{renderGrid(hbRows, hbPageIndex, hbPageCount, handleHbPageChange, hbLoading)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
type CheckInputPeriodItem,
|
||||
getVerificationPeriodActionBlockedMessage,
|
||||
isInputPeriodCurrent,
|
||||
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';
|
||||
@@ -46,6 +47,10 @@ import { PublicSewageFactInpColumnList } from '@/components/util/column/input/Pu
|
||||
import { toGridRows } from '@/components/util/grid/gridFunction';
|
||||
import { useAdminStore } from '@/store/admin-store';
|
||||
|
||||
type GridRowWithVerification = Record<string, string | number> & {
|
||||
verificationResult?: SewerageSupplyRateVerificationResult;
|
||||
};
|
||||
|
||||
type TabToolbarSnapshot = {
|
||||
showSaveInToolbar: boolean;
|
||||
showEnterableToolbarButtons: boolean;
|
||||
@@ -1105,6 +1110,32 @@ const PublicSewageFacilityInp = () => {
|
||||
}
|
||||
showHeaderCheckbox={false}
|
||||
enableSort={false}
|
||||
cellStyleCallback={({ row, fieldName }) => {
|
||||
const verification = (row as GridRowWithVerification).verificationResult;
|
||||
if (!verification) return;
|
||||
const errors = verification[fieldName];
|
||||
if (Array.isArray(errors) && errors.length > 0) {
|
||||
const hasImr = errors.some((e) => e.vrfcCd === "IMR");
|
||||
return hasImr ? "rg-cell-verify-error-imr" : "rg-cell-verify-error";
|
||||
}
|
||||
}}
|
||||
cellTooltipCallback={({ row, fieldName }) => {
|
||||
const verification = (row as GridRowWithVerification).verificationResult;
|
||||
if (!verification) return;
|
||||
const errors = verification[fieldName];
|
||||
if (Array.isArray(errors) && errors.length > 0) {
|
||||
return errors
|
||||
.map((e) => {
|
||||
const prefix =
|
||||
e.vrfcCd === "IMR" ? "IMR검증 - " : "일반검증 - ";
|
||||
const comp = e.compVlCn ?? "";
|
||||
const now = e.nowVlCn ?? "";
|
||||
return `${prefix}${e.rsnExpln}(검증 기준값: ${comp} / 현재 입력값: ${now})`;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</TabsContent>
|
||||
);
|
||||
@@ -1128,6 +1159,11 @@ const PublicSewageFacilityInp = () => {
|
||||
open={verificationResultDialogOpen}
|
||||
onOpenChange={setVerificationResultDialogOpen}
|
||||
queryContext={verificationQueryContext}
|
||||
onExceptionSubmitted={async () => {
|
||||
const ctx = lastSearchedValues ?? searchValues;
|
||||
if (!ctx || !searchParamRef.current) return;
|
||||
await loadPublicSewageFacilityActiveTabData(activeTab, searchParamRef.current, ctx);
|
||||
}}
|
||||
/>
|
||||
<MainFacilityMappingSaveDialog
|
||||
key={`main-facility-mapping-save-${mainFacilityMappingSaveDialogKey}`}
|
||||
|
||||
@@ -910,14 +910,14 @@ const SewagePipeInp = () => {
|
||||
return;
|
||||
}
|
||||
if (!isException) {
|
||||
const currentData = getTabGridRowsForVerification(tabKey);
|
||||
const rowsWithVerificationErrors = currentData.filter((d) =>
|
||||
hasRegionalFactVerificationErrors((d as GridRowWithVerification).verificationResult),
|
||||
);
|
||||
if (rowsWithVerificationErrors.length > 0) {
|
||||
alert("데이터에 오류가 있습니다.\n수정 후 제출해주세요.");
|
||||
return;
|
||||
}
|
||||
// const currentData = getTabGridRowsForVerification(tabKey);
|
||||
// const rowsWithVerificationErrors = currentData.filter((d) =>
|
||||
// hasRegionalFactVerificationErrors((d as GridRowWithVerification).verificationResult),
|
||||
// );
|
||||
// if (rowsWithVerificationErrors.length > 0) {
|
||||
// alert("데이터에 오류가 있습니다.\n수정 후 제출해주세요.");
|
||||
// return;
|
||||
// }
|
||||
if (await confirm("제출 하시겠습니까?")) {
|
||||
setIsExceptionSubmitFlow(false);
|
||||
setShowDailog(true);
|
||||
@@ -2052,6 +2052,12 @@ const SewagePipeInp = () => {
|
||||
}
|
||||
: null
|
||||
}
|
||||
onExceptionSubmitted={async () => {
|
||||
const values = lastSearchedValues ?? searchValues;
|
||||
if (!values?.year || !values?.sido || !values?.bsnmCd) return;
|
||||
const refreshedParam = buildSewagePipeSearchParam(values);
|
||||
await loadSewagePipeActiveTabData(activeTab, refreshedParam, values, true, false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -104,6 +104,14 @@ function getSewerageSupplyRateParams(
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeGridString(value: unknown): string | null {
|
||||
if (typeof value !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.trim() === "" ? null : value;
|
||||
}
|
||||
|
||||
// 하수도 통계 관리 > 01. 하수도 보급률
|
||||
const SeweragePenetrationRateInp = () => {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
@@ -378,15 +386,15 @@ const SeweragePenetrationRateInp = () => {
|
||||
|
||||
const submitFunc = async (isException: boolean) => {
|
||||
if (!isException) {
|
||||
const isError = rowData.filter((d) =>
|
||||
hasVerificationErrors((d as GridRowWithVerification).verificationResult)
|
||||
);
|
||||
// const isError = rowData.filter((d) =>
|
||||
// hasVerificationErrors((d as GridRowWithVerification).verificationResult)
|
||||
// );
|
||||
|
||||
if (isError.length > 0) {
|
||||
alert(`데이터에 오류가 있습니다.
|
||||
수정 후 제출해주세요.`);
|
||||
return;
|
||||
}
|
||||
// if (isError.length > 0) {
|
||||
// alert(`데이터에 오류가 있습니다.
|
||||
// 수정 후 제출해주세요.`);
|
||||
// return;
|
||||
// }
|
||||
if (await confirm('제출 하시겠습니까?')) {
|
||||
setIsExceptionSubmitFlow(false);
|
||||
setShowDailog(true);
|
||||
@@ -438,11 +446,12 @@ const SeweragePenetrationRateInp = () => {
|
||||
};
|
||||
|
||||
const convertedData: SewerageSupplyRateSaveItem[] = currentData.map((d) => ({
|
||||
sggNm: (d.sggNm as string) || null,
|
||||
admdstCd: d.admdstCd as string,
|
||||
admdstNm: d.admdstNm as string,
|
||||
trbtrNm: d.trbtrNm as string,
|
||||
spcCtpnRgnCn: d.spcCtpnRgnCn === '해당없음' ? null : (d.spcCtpnRgnCn as string | null),
|
||||
sggNm: normalizeGridString(d.sggNm),
|
||||
admdstCd: normalizeGridString(d.admdstCd),
|
||||
admdstNm: normalizeGridString(d.admdstNm),
|
||||
trbtrNm: normalizeGridString(d.trbtrNm),
|
||||
spcCtpnRgnCn:
|
||||
d.spcCtpnRgnCn === '해당없음' ? null : normalizeGridString(d.spcCtpnRgnCn),
|
||||
ts1SwtrmtPpltnCnt: formatNumber(d.ts1SwtrmtPpltnCnt, 'ts1SwtrmtPpltnCnt'),
|
||||
ts2SwtrmtPpltnCnt: formatNumber(d.ts2SwtrmtPpltnCnt, 'ts2SwtrmtPpltnCnt'),
|
||||
ts3SwtrmtPpltnCnt: formatNumber(d.ts3SwtrmtPpltnCnt, 'ts3SwtrmtPpltnCnt'),
|
||||
@@ -455,7 +464,7 @@ const SeweragePenetrationRateInp = () => {
|
||||
drtwPrcsPpltnCnt: formatNumber(d.drtwPrcsPpltnCnt, 'drtwPrcsPpltnCnt'),
|
||||
spctnkPpltnCnt: formatNumber(d.spctnkPpltnCnt, 'spctnkPpltnCnt'),
|
||||
staaExcpArea: formatNumber(d.staaExcpArea, 'staaExcpArea'),
|
||||
rmrk: (d.rmrk as string) ?? '',
|
||||
rmrk: normalizeGridString(d.rmrk),
|
||||
wholPpltnCnt: formatNumber(d.wholPpltnCnt, 'wholPpltnCnt'),
|
||||
wholArea: formatNumber(d.wholArea, 'wholArea'),
|
||||
wholSwtrmtPpltnCnt: formatNumber(d.wholSwtrmtPpltnCnt, 'wholSwtrmtPpltnCnt'),
|
||||
@@ -584,11 +593,14 @@ const SeweragePenetrationRateInp = () => {
|
||||
|
||||
const convertedData: SewerageSupplyRateSaveItem[] = currentData.map((d) => ({
|
||||
// 행정구역
|
||||
sggNm: (d.sggNm as string) || null, // 시군구
|
||||
admdstCd: d.admdstCd as string, // 행정코드
|
||||
admdstNm: d.admdstNm as string, // 행정구역명
|
||||
trbtrNm: d.trbtrNm as string, // 지류
|
||||
spcCtpnRgnCn: d.spcCtpnRgnCn === '해당없음' ? null : (d.spcCtpnRgnCn as string | null), // 특별대책지역
|
||||
sggNm: normalizeGridString(d.sggNm), // 시군구
|
||||
admdstCd: normalizeGridString(d.admdstCd), // 행정코드
|
||||
admdstNm: normalizeGridString(d.admdstNm), // 행정구역명
|
||||
trbtrNm: normalizeGridString(d.trbtrNm), // 지류
|
||||
spcCtpnRgnCn:
|
||||
d.spcCtpnRgnCn === '해당없음'
|
||||
? null
|
||||
: normalizeGridString(d.spcCtpnRgnCn), // 특별대책지역
|
||||
// 공공하수처리인구(명) - 물리적/생물학적/고도처리
|
||||
ts1SwtrmtPpltnCnt: formatNumber(d.ts1SwtrmtPpltnCnt, 'ts1SwtrmtPpltnCnt'), // 물리적처리
|
||||
ts2SwtrmtPpltnCnt: formatNumber(d.ts2SwtrmtPpltnCnt, 'ts2SwtrmtPpltnCnt'), // 생물학적처리
|
||||
@@ -604,7 +616,7 @@ const SeweragePenetrationRateInp = () => {
|
||||
drtwPrcsPpltnCnt: formatNumber(d.drtwPrcsPpltnCnt, 'drtwPrcsPpltnCnt'), // 오수처리인구
|
||||
spctnkPpltnCnt: formatNumber(d.spctnkPpltnCnt, 'spctnkPpltnCnt'), // 정화조인구
|
||||
staaExcpArea: formatNumber(d.staaExcpArea, 'staaExcpArea'), // 하수처리구역 외 면적(㎢)
|
||||
rmrk: (d.rmrk as string) ?? '', // 비고
|
||||
rmrk: normalizeGridString(d.rmrk), // 비고
|
||||
// 합계·소계(계산필드 저장용)
|
||||
wholPpltnCnt: formatNumber(d.wholPpltnCnt, 'wholPpltnCnt'), // 총인구(명)
|
||||
wholArea: formatNumber(d.wholArea, 'wholArea'), // 총면적(㎢)
|
||||
@@ -819,6 +831,11 @@ const SeweragePenetrationRateInp = () => {
|
||||
}
|
||||
: null
|
||||
}
|
||||
onExceptionSubmitted={async () => {
|
||||
const values = lastSearchedValues ?? searchValues;
|
||||
if (!values) return;
|
||||
await handleSearch(values);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { RealGridReact } from "realgrid-react";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
@@ -13,6 +14,7 @@ import RejectButton from "@/components/button/RejectButton";
|
||||
import DataGrid from "@/components/module/DataGrid";
|
||||
import SearchForm from "@/components/module/SearchForm";
|
||||
import type { SearchFormValues } from "@/components/type/SearchForm.type";
|
||||
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";
|
||||
@@ -32,6 +34,10 @@ const StatsInputStatusInp = () => {
|
||||
);
|
||||
const [searchParams, setSearchParams] =
|
||||
useState<StatsInputStatusListRequestParams | null>(null);
|
||||
const [lastSearchedValues, setLastSearchedValues] =
|
||||
useState<SearchFormValues | null>(null);
|
||||
const [hasCompletedSearch, setHasCompletedSearch] = useState(false);
|
||||
const gridRef = useRef<RealGridReact | null>(null);
|
||||
|
||||
const swrKey = searchParams
|
||||
? (["stats-input-status", searchParams] as const)
|
||||
@@ -45,12 +51,16 @@ const StatsInputStatusInp = () => {
|
||||
onSuccess: (data) => {
|
||||
if (data.status === "success" && data.data) {
|
||||
setRowData(toGridRows(data.data));
|
||||
setHasCompletedSearch(true);
|
||||
} else {
|
||||
setHasCompletedSearch(false);
|
||||
setRowData([]);
|
||||
alert(data.message ?? "조회에 실패했습니다.");
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
alert("데이터 조회 중 오류가 발생했습니다.");
|
||||
setHasCompletedSearch(false);
|
||||
setRowData([]);
|
||||
},
|
||||
},
|
||||
@@ -73,6 +83,8 @@ const StatsInputStatusInp = () => {
|
||||
sggNm: values.gugun === "ALL" ? "" : values.gugun,
|
||||
statsYr: values.year ?? "",
|
||||
};
|
||||
setHasCompletedSearch(false);
|
||||
setLastSearchedValues(values);
|
||||
setSearchParams(params);
|
||||
mutate(["stats-input-status", params]);
|
||||
};
|
||||
@@ -80,6 +92,8 @@ const StatsInputStatusInp = () => {
|
||||
const handleReset = () => {
|
||||
setSearchParams(null);
|
||||
setRowData([]);
|
||||
setLastSearchedValues(null);
|
||||
setHasCompletedSearch(false);
|
||||
};
|
||||
|
||||
const rejectFunc = () => {
|
||||
@@ -87,19 +101,44 @@ const StatsInputStatusInp = () => {
|
||||
};
|
||||
|
||||
const approveFunc = () => {
|
||||
const checkedRows =
|
||||
gridRef.current?.gridView?.getCheckedRows?.(true, false, true) ?? [];
|
||||
|
||||
if (rowData.length === 0 || checkedRows.length !== rowData.length) {
|
||||
alert('모든 통계 항목을 체크해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const hasInvalidStatus = rowData.some((row) =>
|
||||
["0", "1", "4"].includes(String(row.laeSbmsnSttsCd ?? "")),
|
||||
);
|
||||
|
||||
if (hasInvalidStatus) {
|
||||
alert("미작성이나 미제출, 혹은 보완요청(반려) 상태인 항목이 있습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 승인 요청 로직
|
||||
};
|
||||
|
||||
const gugunOkOrNotDoList = doList.includes(lastSearchedValues?.bsnmCd || "")
|
||||
? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== "ALL")
|
||||
: true;
|
||||
const showActionToolbarCard =
|
||||
hasCompletedSearch && rowData.length > 0 && gugunOkOrNotDoList;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative">
|
||||
<SearchForm onSubmit={handleSearch} onReset={handleReset} />
|
||||
<div className="card !py-0">
|
||||
<div className="m-[0.6rem] flex items-center justify-end gap-[0.6rem]">
|
||||
<RejectButton rejectFunc={rejectFunc} />
|
||||
<ApproveButton approveFunc={approveFunc} />
|
||||
{showActionToolbarCard && (
|
||||
<div className="card !py-0">
|
||||
<div className="m-[0.6rem] flex items-center justify-end gap-[0.6rem]">
|
||||
<RejectButton rejectFunc={rejectFunc} />
|
||||
<ApproveButton approveFunc={approveFunc} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isLoading && (
|
||||
<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]">
|
||||
@@ -115,6 +154,27 @@ const StatsInputStatusInp = () => {
|
||||
setRowData={setRowData}
|
||||
isExcelImport={false}
|
||||
showHeaderCheckbox={true}
|
||||
getGridRef={gridRef}
|
||||
cellStyleCallback={({ row, fieldName }) => {
|
||||
if (fieldName !== "laeSbmsnSttsNm") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const statusCd = String(row.laeSbmsnSttsCd ?? "");
|
||||
if (statusCd === "0" || statusCd === "1") {
|
||||
return "rg-cell-stats-status-pending";
|
||||
}
|
||||
if (statusCd === "2") {
|
||||
return "rg-cell-stats-status-submitted";
|
||||
}
|
||||
if (statusCd === "3") {
|
||||
return "rg-cell-stats-status-approved";
|
||||
}
|
||||
if (statusCd === "4") {
|
||||
return "rg-cell-stats-status-rejected";
|
||||
}
|
||||
return undefined;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -33,7 +33,7 @@ function rowSeTypeToCode(row: Record<string, string | number>): "INPT" | "VRFC"
|
||||
return "INPT";
|
||||
}
|
||||
|
||||
function parseYmdToDate(ymd: unknown): Date | undefined {
|
||||
function parseYmdToDate(ymd: string): Date | undefined {
|
||||
const s = String(ymd ?? "").replace(/\D/g, "");
|
||||
if (s.length !== 8) return undefined;
|
||||
const y = Number(s.slice(0, 4));
|
||||
@@ -47,6 +47,12 @@ function parseYmdToDate(ymd: unknown): Date | undefined {
|
||||
return dt;
|
||||
}
|
||||
|
||||
function formatYmdForDisplay(ymd: string): string {
|
||||
const s = String(ymd ?? "").replace(/\D/g, "");
|
||||
if (s.length !== 8) return String(ymd ?? "");
|
||||
return `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}`;
|
||||
}
|
||||
|
||||
const StatisticsInputManagement = () => {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -71,16 +77,17 @@ const StatisticsInputManagement = () => {
|
||||
setIsLoading(true);
|
||||
const params: StatsInputPeriodListRequest = {
|
||||
statsYr: values.year,
|
||||
seType:
|
||||
values.clsfCd && values.clsfCd !== "ALL"
|
||||
? values.clsfCd
|
||||
: "",
|
||||
...(values.clsfCd && values.clsfCd !== "ALL"
|
||||
? { seType: values.clsfCd }
|
||||
: {}),
|
||||
};
|
||||
const res = await statisticsApi.getStatsInputPeriodList(params);
|
||||
if (res.status === "success" && Array.isArray(res.data)) {
|
||||
const rowsForGrid = res.data.map((row) => ({
|
||||
...row,
|
||||
seType: seTypeToDisplayLabel(row.seType),
|
||||
inptBgngYmd: formatYmdForDisplay(row.inptBgngYmd),
|
||||
inptEndYmd: formatYmdForDisplay(row.inptEndYmd),
|
||||
}));
|
||||
setRowData(toGridRows(rowsForGrid));
|
||||
} else {
|
||||
@@ -112,27 +119,47 @@ const StatisticsInputManagement = () => {
|
||||
setPeriodDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleRowClick = (row: Record<string, string | number>) => {
|
||||
const inptYmdSnRaw = row.inptYmdSn;
|
||||
const inptYmdSnNum =
|
||||
typeof inptYmdSnRaw === "number"
|
||||
? inptYmdSnRaw
|
||||
: parseInt(String(inptYmdSnRaw ?? ""), 10);
|
||||
const handleRowClick = async (row: Record<string, string | number>) => {
|
||||
const seCdRaw = row.seCd;
|
||||
const seCdNum =
|
||||
typeof seCdRaw === "number"
|
||||
? seCdRaw
|
||||
: parseInt(String(seCdRaw ?? ""), 10);
|
||||
setPeriodDialogMode("edit");
|
||||
setPeriodDialogInitial({
|
||||
statsYr: String(row.statsYr ?? ""),
|
||||
seType: rowSeTypeToCode(row),
|
||||
inptYmdSn: Number.isFinite(inptYmdSnNum) ? inptYmdSnNum : 0,
|
||||
seCd: Number.isFinite(seCdNum) ? seCdNum : 0,
|
||||
inptBgngYmd: parseYmdToDate(row.inptBgngYmd),
|
||||
inptEndYmd: parseYmdToDate(row.inptEndYmd),
|
||||
});
|
||||
setPeriodDialogOpen(true);
|
||||
const seCd = Number.isFinite(seCdNum) ? seCdNum : 0;
|
||||
const statsYr = String(row.statsYr ?? "");
|
||||
const seType = rowSeTypeToCode(row);
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const res = await statisticsApi.getStatsInputPeriodDetail({
|
||||
statsYr,
|
||||
seType,
|
||||
seCd,
|
||||
});
|
||||
|
||||
if (res.status !== "success" || !res.data) {
|
||||
alert(res.message || "상세 정보를 조회하지 못했습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
const detail = res.data;
|
||||
setPeriodDialogMode("edit");
|
||||
setPeriodDialogInitial({
|
||||
statsYr: String(detail.statsYr ?? statsYr),
|
||||
seType: detail.seType === "VRFC" ? "VRFC" : "INPT",
|
||||
seCd:
|
||||
typeof detail.seCd === "number" && Number.isFinite(detail.seCd)
|
||||
? detail.seCd
|
||||
: seCd,
|
||||
inptBgngYmd: parseYmdToDate(detail.inptBgngYmd),
|
||||
inptEndYmd: parseYmdToDate(detail.inptEndYmd),
|
||||
});
|
||||
setPeriodDialogOpen(true);
|
||||
} catch {
|
||||
alert("상세 정보 조회 중 오류가 발생했습니다.");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const columns: ColumnConfig[] = [
|
||||
@@ -166,13 +193,6 @@ const StatisticsInputManagement = () => {
|
||||
header: { text: "종료일" },
|
||||
editable: false,
|
||||
},
|
||||
{
|
||||
name: "inptYmdSn",
|
||||
fieldName: "inptYmdSn",
|
||||
header: { text: "inptYmdSn" },
|
||||
visible: false,
|
||||
editable: false,
|
||||
},
|
||||
];
|
||||
|
||||
const columnLayout = [
|
||||
|
||||
@@ -33,7 +33,6 @@ import { getUserInfo } from "../../../../../../packages/common/components/src/ap
|
||||
export type StatisticsInputPeriodDialogValues = {
|
||||
statsYr: string;
|
||||
seType: "INPT" | "VRFC";
|
||||
inptYmdSn: number;
|
||||
seCd: number;
|
||||
inptBgngYmd: Date | undefined;
|
||||
inptEndYmd: Date | undefined;
|
||||
@@ -138,22 +137,17 @@ export function StatisticsInputPeriodDialog({
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const userInfo = await getUserInfo();
|
||||
const inptYmdSn =
|
||||
mode === "create"
|
||||
? 0
|
||||
: (initial && Number.isFinite(initial.inptYmdSn) ? initial.inptYmdSn : 0);
|
||||
const param: StatsInputPeriodSaveRequest = {
|
||||
statsYr: yr,
|
||||
seType,
|
||||
inptYmdSn,
|
||||
seCd: seCdParsed,
|
||||
inptBgngYmd: bgng,
|
||||
inptEndYmd: end,
|
||||
rgtrId: userInfo.userUnqId,
|
||||
};
|
||||
const res = isCreate
|
||||
? await statisticsApi.saveStatsInputPeriod(param)
|
||||
: await statisticsApi.updateStatsInputPeriod(param);
|
||||
if (!isCreate) {
|
||||
param.seCd = seCdParsed;
|
||||
}
|
||||
const res = await statisticsApi.saveStatsInputPeriod(param);
|
||||
if (res.status === "success") {
|
||||
alert(`${isCreate ? '등록 되었습니다.' : '수정 되었습니다.'}`);
|
||||
onOpenChange(false);
|
||||
@@ -192,8 +186,6 @@ export function StatisticsInputPeriodDialog({
|
||||
const param: StatsInputPeriodDeleteRequest = {
|
||||
statsYr: yr,
|
||||
seType: initial.seType,
|
||||
inptYmdSn:
|
||||
Number.isFinite(initial.inptYmdSn) ? initial.inptYmdSn : 0,
|
||||
seCd,
|
||||
inptBgngYmd: bgng,
|
||||
inptEndYmd: end,
|
||||
@@ -253,7 +245,7 @@ export function StatisticsInputPeriodDialog({
|
||||
<Select
|
||||
value={seType ?? undefined}
|
||||
onValueChange={(v) => setSeType(v as "INPT" | "VRFC")}
|
||||
disabled={isSubmitting}
|
||||
disabled={isSubmitting || mode === "edit"}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="선택" />
|
||||
|
||||
@@ -91,7 +91,7 @@ const PublicSewageFacilityRvw = () => {
|
||||
sggNm: values.gugun === "ALL" ? "" : values.gugun,
|
||||
sewerBzmnCd: values.bsnmCd,
|
||||
statsYr: values.year,
|
||||
fctName: values.fctName,
|
||||
swtrmtFcltNm: values.fctName,
|
||||
});
|
||||
|
||||
if (response.status === "success" && response.data) {
|
||||
|
||||
@@ -99,12 +99,12 @@ export function ReturnReasonDialog({
|
||||
};
|
||||
|
||||
const response = await statisticsApi.saveReviewReturn(param);
|
||||
if (response.status !== "success") {
|
||||
if (response.resultCode.toLowerCase() !== "success") {
|
||||
alert(response.message || "반려 처리 중 오류가 발생했습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
alert(response.message || "반려 처리 되었습니다.");
|
||||
alert("반려 처리 되었습니다.");
|
||||
await onSave?.();
|
||||
resetForm();
|
||||
onOpenChange(false);
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
|
||||
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
Input,
|
||||
Label,
|
||||
LoadingWithAnimation,
|
||||
Textarea,
|
||||
} from "@common/components";
|
||||
|
||||
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
|
||||
import type {
|
||||
ReviewerInfoRequest,
|
||||
ReviewerInfoResponse,
|
||||
ReviewSubmitRequest,
|
||||
} from "@/entities/statistics/model/statistics-interface";
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
|
||||
export interface ReviewSubmitDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
context: ReviewerInfoRequest | null;
|
||||
onSubmitted?: () => void | Promise<void>;
|
||||
}
|
||||
|
||||
export function ReviewSubmitDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
context,
|
||||
onSubmitted,
|
||||
}: ReviewSubmitDialogProps) {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const [reviewerInfo, setReviewerInfo] =
|
||||
useState<ReviewerInfoResponse["data"] | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setReviewerInfo(null);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (context == null) {
|
||||
setReviewerInfo(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
|
||||
const loadReviewerInfo = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await statisticsApi.getReviewerInfo(context);
|
||||
|
||||
if (cancelled) return;
|
||||
|
||||
if (response.status !== "success" || !response.data) {
|
||||
alert("제출 정보를 불러오지 못했습니다.");
|
||||
setReviewerInfo(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setReviewerInfo(response.data);
|
||||
} catch {
|
||||
if (!cancelled) {
|
||||
alert("제출 정보를 불러오지 못했습니다.");
|
||||
setReviewerInfo(null);
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void loadReviewerInfo();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [open, context, alert]);
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (context == null || reviewerInfo == null) {
|
||||
alert("제출 정보를 확인한 뒤 다시 시도해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
|
||||
const userInfo = await getUserInfo();
|
||||
const submitParam: ReviewSubmitRequest = {
|
||||
statsYr: context.statsYr,
|
||||
sewerBzmnCd: context.sewerBzmnCd,
|
||||
statsCd: context.statsCd,
|
||||
mdfrId: userInfo.userUnqId,
|
||||
splmntOpnnCn: reviewerInfo.splmntOpnnCn ?? "",
|
||||
wrterOpnnCn: reviewerInfo.wrterOpnnCn ?? "",
|
||||
};
|
||||
|
||||
const response = await statisticsApi.submitReview(submitParam);
|
||||
if (response.status !== "success") {
|
||||
alert(response.message || "제출에 실패했습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
alert(response.message || "제출되었습니다.");
|
||||
onOpenChange(false);
|
||||
await onSubmitted?.();
|
||||
} catch {
|
||||
alert("제출 중 오류가 발생했습니다.");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-[64rem]" aria-describedby={undefined}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>제출 정보</DialogTitle>
|
||||
</DialogHeader>
|
||||
{(isLoading || isSubmitting) && (
|
||||
<div className="absolute inset-0 z-50 flex items-center justify-center rounded-lg p-10 [data-theme='black']:bg-transparent">
|
||||
<div className="relative size-[5rem]">
|
||||
<LoadingWithAnimation color="primary" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-cols-2 gap-4 py-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-label">검토자명</Label>
|
||||
<Input value={reviewerInfo?.userNm ?? "-"} readOnly />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-label">연락처</Label>
|
||||
<Input value={reviewerInfo?.telOffrm ?? "-"} readOnly />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-label">부서</Label>
|
||||
<Input value={reviewerInfo?.dpmNm ?? "-"} readOnly />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label className="text-label">직책</Label>
|
||||
<Input value={reviewerInfo?.cps ?? "-"} readOnly />
|
||||
</div>
|
||||
<div className="col-span-2 flex flex-col gap-2">
|
||||
<Label className="text-label">검토의견</Label>
|
||||
<Textarea
|
||||
value={reviewerInfo?.splmntOpnnCn ?? "-"}
|
||||
readOnly
|
||||
className="min-h-[10rem] p-2 resize-none !text-xl"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2 flex flex-col gap-2">
|
||||
<Label className="text-label">처리내역</Label>
|
||||
<Textarea
|
||||
value={reviewerInfo?.wrterOpnnCn ?? "-"}
|
||||
readOnly
|
||||
className="min-h-[10rem] p-2 resize-none !text-xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
onClick={() => void handleConfirm()}
|
||||
disabled={isLoading || isSubmitting || reviewerInfo == null}
|
||||
>
|
||||
제출
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
color="gray"
|
||||
onClick={() => onOpenChange(false)}
|
||||
disabled={isLoading || isSubmitting}
|
||||
>
|
||||
취소
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
|
||||
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import { useAlertStore, useUserStore } from "@/shared/lib";
|
||||
import { statisticsManagementApi } from "@/entities/statistics/api/statistics-management-service";
|
||||
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
|
||||
import type {
|
||||
SewagePipeReviewRequestParams,
|
||||
StatsStatusInfo,
|
||||
} from "@/entities/statistics/model/statistics-sewage-pipe-review-interface";
|
||||
import type { ReviewOpinionSaveRequest } from "@/entities/statistics/model/statistics-interface";
|
||||
import type {
|
||||
ReviewerInfoRequest,
|
||||
ReviewOpinionSaveRequest,
|
||||
} from "@/entities/statistics/model/statistics-interface";
|
||||
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
|
||||
|
||||
import SubmitButton from "@/components/button/SumitButton";
|
||||
@@ -18,17 +19,57 @@ import ReviewOpinionButton from "@/components/button/ReviewOpinionButton";
|
||||
import DataGrid from "@/components/module/DataGrid";
|
||||
import SearchForm from "@/components/module/SearchForm";
|
||||
import { ReturnReasonDialog } from "@/components/statistics/review/ReturnReasonDialog";
|
||||
import { ReviewSubmitDialog } from "@/components/statistics/review/ReviewSubmitDialog";
|
||||
import {
|
||||
ReviewOpinionDialog,
|
||||
type ReviewOpinionDialogContext,
|
||||
} from "@/components/statistics/review/ReviewOpinionDialog";
|
||||
import type { ColumnInfo } from "@/components/type/DataGrid.type";
|
||||
import type {
|
||||
ColumnConfig,
|
||||
ColumnInfo,
|
||||
ColumnInfoList,
|
||||
ColumnLayoutItem,
|
||||
} from "@/components/type/DataGrid.type";
|
||||
import type { SearchFormValues } from "@/components/type/SearchForm.type";
|
||||
import { SewagePipeColumnInfoListRvw } from "@/components/util/column/review/SewagePipeRvwColumn";
|
||||
import { toGridRows } from "@/components/util/grid/gridFunction";
|
||||
|
||||
const firstTabKey = SewagePipeColumnInfoListRvw[0]?.tabKey ?? "ST00020001";
|
||||
|
||||
function cloneColumnLayoutItem(item: ColumnLayoutItem): ColumnLayoutItem {
|
||||
if (typeof item === "string") return item;
|
||||
if ("column" in item) {
|
||||
return {
|
||||
column: item.column,
|
||||
...(item.footerUserSpans && {
|
||||
footerUserSpans: item.footerUserSpans.map((span) => ({ ...span })),
|
||||
}),
|
||||
};
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
header: { ...item.header },
|
||||
items: item.items.map((child) =>
|
||||
typeof child === "string" ? child : cloneColumnLayoutItem(child),
|
||||
),
|
||||
...(item.footerUserSpans && {
|
||||
footerUserSpans: item.footerUserSpans.map((span) => ({ ...span })),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function cloneColumnConfig(col: ColumnConfig): ColumnConfig {
|
||||
return {
|
||||
...col,
|
||||
header: { ...col.header },
|
||||
...(col.footer && {
|
||||
footer: Array.isArray(col.footer)
|
||||
? col.footer.map((item) => ({ ...item }))
|
||||
: { ...col.footer },
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchSewagePipeReviewTab(
|
||||
tabKey: string,
|
||||
searchParam: SewagePipeReviewRequestParams,
|
||||
@@ -45,6 +86,9 @@ async function fetchSewagePipeReviewTab(
|
||||
if (tabKey === "ST00020004") {
|
||||
return statisticsManagementApi.getSewagePipeDiamList(searchParam);
|
||||
}
|
||||
if (tabKey === "ST00020005") {
|
||||
return await statisticsManagementApi.getSewagePipeManholeList(searchParam);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -65,6 +109,8 @@ function extractReviewRowsFromResponseData(
|
||||
return Array.isArray(d.pipeTypeList) ? (d.pipeTypeList as object[]) : [];
|
||||
case "ST00020004":
|
||||
return Array.isArray(d.pipeDiamList) ? (d.pipeDiamList as object[]) : [];
|
||||
case "ST00020005":
|
||||
return Array.isArray(d.manholeList) ? (d.manholeList as object[]) : [];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
@@ -79,12 +125,13 @@ function extractStatsStatusInfo(data: unknown): StatsStatusInfo | null {
|
||||
// 하수도 통계 검토 02.하수관로
|
||||
const SewagePipeRvw = () => {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const { confirm } = useConfirm();
|
||||
const userInfo = useUserStore((state) => state.userInfo);
|
||||
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [activeTab, setActiveTab] = useState<string>(firstTabKey);
|
||||
const [reviewOpinionOpen, setReviewOpinionOpen] = useState(false);
|
||||
const [returnDialogOpen, setReturnDialogOpen] = useState(false);
|
||||
const [submitDialogOpen, setSubmitDialogOpen] = useState(false);
|
||||
const [lastSearchValues, setLastSearchValues] = useState<SearchFormValues | null>(null);
|
||||
/** 탭별 조회 응답의 statsStatusInfo — 활성 탭에 맞춰 버튼 노출 (SewagePipeInp의 tabStatusInfoMap과 동일 패턴) */
|
||||
const [tabStatsStatusInfoMap, setTabStatsStatusInfoMap] = useState<
|
||||
@@ -98,6 +145,17 @@ const SewagePipeRvw = () => {
|
||||
() => tabStatsStatusInfoMap.get(activeTab) ?? {},
|
||||
[tabStatsStatusInfoMap, activeTab],
|
||||
);
|
||||
const columnInfo = useMemo<ColumnInfoList[]>(
|
||||
() =>
|
||||
SewagePipeColumnInfoListRvw.map((tab) => ({
|
||||
...tab,
|
||||
columns: (tab.columns ?? []).map((col) => cloneColumnConfig(col)),
|
||||
columnLayout: (tab.columnLayout ?? []).map((item) =>
|
||||
cloneColumnLayoutItem(item),
|
||||
),
|
||||
})),
|
||||
[],
|
||||
);
|
||||
const reviewOpinionContext = useMemo<ReviewOpinionDialogContext | null>(() => {
|
||||
if (lastSearchValues == null) return null;
|
||||
return {
|
||||
@@ -108,6 +166,25 @@ const SewagePipeRvw = () => {
|
||||
}, [activeStatsStatusInfo.statsCd, lastSearchValues]);
|
||||
const latestReviewOpinion = activeStatsStatusInfo.splmntOpnnCn ?? "";
|
||||
const latestProcessHistory = activeStatsStatusInfo.wrterOpnnCn ?? "";
|
||||
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
|
||||
if (lastSearchValues == null) return null;
|
||||
|
||||
const statsYr = lastSearchValues.year ?? "";
|
||||
const sewerBzmnCd = lastSearchValues.bsnmCd ?? "";
|
||||
const statsCd = activeStatsStatusInfo.statsCd ?? "";
|
||||
const instSeCd = userInfo?.instSeCd ?? "";
|
||||
|
||||
if (!statsYr || !sewerBzmnCd || !statsCd || !instSeCd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
statsYr,
|
||||
sewerBzmnCd,
|
||||
statsCd,
|
||||
instSeCd,
|
||||
};
|
||||
}, [activeStatsStatusInfo.statsCd, lastSearchValues, userInfo?.instSeCd]);
|
||||
|
||||
const { showReviewOpinionButton, showReturnButton, showSubmitButton } = useMemo(() => {
|
||||
const statsStatusInfo = activeStatsStatusInfo;
|
||||
@@ -135,6 +212,7 @@ const SewagePipeRvw = () => {
|
||||
setTabStatsStatusInfoMap(new Map());
|
||||
setReviewOpinionOpen(false);
|
||||
setReturnDialogOpen(false);
|
||||
setSubmitDialogOpen(false);
|
||||
setLastSearchValues(null);
|
||||
searchParamRef.current = null;
|
||||
searchDoneRef.current = false;
|
||||
@@ -235,37 +313,13 @@ const SewagePipeRvw = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const submitFunc = async () => {
|
||||
if (reviewOpinionContext == null) {
|
||||
const submitFunc = () => {
|
||||
if (submitDialogContext == null) {
|
||||
alert("조회 후 제출할 수 있습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await confirm("제출 하시겠습니까?"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const userInfo = await getUserInfo();
|
||||
const response = await statisticsApi.submitReview({
|
||||
statsYr: reviewOpinionContext.statsYr,
|
||||
sewerBzmnCd: reviewOpinionContext.sewerBzmnCd,
|
||||
statsCd: reviewOpinionContext.statsCd,
|
||||
mdfrId: userInfo.userUnqId,
|
||||
});
|
||||
|
||||
if (response.status !== "success") {
|
||||
alert(response.message || "제출에 실패했습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
alert(response.message || "제출되었습니다.");
|
||||
if (lastSearchValues != null) {
|
||||
await handleSearch(lastSearchValues);
|
||||
}
|
||||
} catch {
|
||||
alert("제출 중 오류가 발생했습니다.");
|
||||
}
|
||||
setSubmitDialogOpen(true);
|
||||
};
|
||||
|
||||
const reviewOpinionFunc = async () => {
|
||||
@@ -344,7 +398,7 @@ const SewagePipeRvw = () => {
|
||||
)}
|
||||
{showReturnButton && <ReturnButton returnFunc={returnFunc} />}
|
||||
{showSubmitButton && (
|
||||
<SubmitButton submitFunc={submitFunc} />
|
||||
<SubmitButton submitFunc={() => submitFunc()} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -365,13 +419,13 @@ const SewagePipeRvw = () => {
|
||||
variant="default"
|
||||
>
|
||||
<TabsList>
|
||||
{SewagePipeColumnInfoListRvw.map((info) => (
|
||||
{columnInfo.map((info) => (
|
||||
<TabsTrigger key={info.tabKey} value={info.tabKey}>
|
||||
{info.tabLabel || info.tabKey}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
{SewagePipeColumnInfoListRvw.map((info) => {
|
||||
{columnInfo.map((info) => {
|
||||
const tabKey = info.tabKey;
|
||||
if (!info.columns || !info.columnLayout) {
|
||||
return null;
|
||||
@@ -419,6 +473,16 @@ const SewagePipeRvw = () => {
|
||||
initialProcessHistory={latestProcessHistory}
|
||||
onSave={saveReviewOpinion}
|
||||
/>
|
||||
<ReviewSubmitDialog
|
||||
open={submitDialogOpen}
|
||||
onOpenChange={setSubmitDialogOpen}
|
||||
context={submitDialogContext}
|
||||
onSubmitted={async () => {
|
||||
if (lastSearchValues != null) {
|
||||
await handleSearch(lastSearchValues);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ReturnReasonDialog
|
||||
open={returnDialogOpen}
|
||||
onOpenChange={setReturnDialogOpen}
|
||||
|
||||
@@ -6,13 +6,15 @@ import ReturnButton from "@/components/button/ReturnButton";
|
||||
import SubmitButton from "@/components/button/SumitButton";
|
||||
import { LoadingWithAnimation } from "@common/components";
|
||||
import { ReturnReasonDialog } from "@/components/statistics/review/ReturnReasonDialog";
|
||||
import { ReviewSubmitDialog } from "@/components/statistics/review/ReviewSubmitDialog";
|
||||
|
||||
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { useAlertStore, useUserStore } from "@/shared/lib";
|
||||
import { SeweragePenetrationRateInfoList } from "@/components/util/column/review/SeweragePenetrationRateRvwColumn";
|
||||
import type { SewerageSupplyRateReviewItem, statsStatusInfo } from "@/entities/statistics/model/statistics-sewerage-penetration-review-interface";
|
||||
import type { ReviewOpinionSaveRequest } from "@/entities/statistics/model/statistics-interface";
|
||||
import type {
|
||||
ReviewOpinionSaveRequest,
|
||||
ReviewerInfoRequest,
|
||||
} from "@/entities/statistics/model/statistics-interface";
|
||||
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
|
||||
import { statisticsManagementApi } from "@/entities/statistics/api/statistics-management-service";
|
||||
import type { SearchFormValues } from "@/components/type/SearchForm.type";
|
||||
@@ -25,17 +27,18 @@ import {
|
||||
// 하수도 통계 검토 01.하수도 보급률
|
||||
const SeweragePenetrationRateRvw = () => {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const userInfo = useUserStore((state) => state.userInfo);
|
||||
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [statsStatusInfo, setStatsStatusInfo] = useState<statsStatusInfo>({});
|
||||
const [reviewOpinionOpen, setReviewOpinionOpen] = useState(false);
|
||||
const [returnDialogOpen, setReturnDialogOpen] = useState(false);
|
||||
const [submitDialogOpen, setSubmitDialogOpen] = useState(false);
|
||||
const [reviewOpinionContext, setReviewOpinionContext] =
|
||||
useState<ReviewOpinionDialogContext | null>(null);
|
||||
const [latestReviewOpinion, setLatestReviewOpinion] = useState("");
|
||||
const [latestProcessHistory, setLatestProcessHistory] = useState("");
|
||||
const [lastSearchValues, setLastSearchValues] = useState<SearchFormValues | null>(null);
|
||||
const { confirm } = useConfirm();
|
||||
|
||||
const columnInfo = useMemo(() => {
|
||||
const { columns, columnLayout, fixedColumns, rowStartNum } = SeweragePenetrationRateInfoList;
|
||||
@@ -66,6 +69,24 @@ const SeweragePenetrationRateRvw = () => {
|
||||
const showActionBar =
|
||||
showReviewOpinionButton || showReturnButton || showSubmitButton;
|
||||
|
||||
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
|
||||
if (reviewOpinionContext == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const instSeCd = userInfo?.instSeCd ?? "";
|
||||
if (!instSeCd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
statsYr: reviewOpinionContext.statsYr,
|
||||
sewerBzmnCd: reviewOpinionContext.sewerBzmnCd,
|
||||
statsCd: reviewOpinionContext.statsCd,
|
||||
instSeCd,
|
||||
};
|
||||
}, [reviewOpinionContext, userInfo?.instSeCd]);
|
||||
|
||||
const handleReset = () => {
|
||||
setRowData([]);
|
||||
setStatsStatusInfo({});
|
||||
@@ -129,37 +150,13 @@ const SeweragePenetrationRateRvw = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const submitFunc = async () => {
|
||||
if (reviewOpinionContext == null) {
|
||||
const submitFunc = () => {
|
||||
if (submitDialogContext == null) {
|
||||
alert("조회 후 제출할 수 있습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await confirm("제출 하시겠습니까?"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const userInfo = await getUserInfo();
|
||||
const response = await statisticsApi.submitReview({
|
||||
statsYr: reviewOpinionContext.statsYr,
|
||||
sewerBzmnCd: reviewOpinionContext.sewerBzmnCd,
|
||||
statsCd: reviewOpinionContext.statsCd,
|
||||
mdfrId: userInfo.userUnqId,
|
||||
});
|
||||
|
||||
if (response.status !== "success") {
|
||||
alert(response.message || "제출에 실패했습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
alert(response.message || "제출되었습니다.");
|
||||
if (lastSearchValues != null) {
|
||||
await handleSearch(lastSearchValues);
|
||||
}
|
||||
} catch {
|
||||
alert("제출 중 오류가 발생했습니다.");
|
||||
}
|
||||
setSubmitDialogOpen(true);
|
||||
};
|
||||
|
||||
const reviewOpinionFunc = async () => {
|
||||
@@ -258,6 +255,16 @@ const SeweragePenetrationRateRvw = () => {
|
||||
initialProcessHistory={latestProcessHistory}
|
||||
onSave={saveReviewOpinion}
|
||||
/>
|
||||
<ReviewSubmitDialog
|
||||
open={submitDialogOpen}
|
||||
onOpenChange={setSubmitDialogOpen}
|
||||
context={submitDialogContext}
|
||||
onSubmitted={async () => {
|
||||
if (lastSearchValues != null) {
|
||||
await handleSearch(lastSearchValues);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ReturnReasonDialog
|
||||
open={returnDialogOpen}
|
||||
onOpenChange={setReturnDialogOpen}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/re
|
||||
import DataGrid from "@/components/module/DataGrid";
|
||||
import SearchForm from "@/components/module/SearchForm";
|
||||
import ApproveButton from "@/components/button/ApproveButton";
|
||||
import { doList } from "@/components/util/column/input/DoList";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
@@ -39,6 +40,8 @@ const StatsInputStatusRvw = () => {
|
||||
const [historyRowData, setHistoryRowData] = useState<ReviewHistoryRow[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [searchedYear, setSearchedYear] = useState<string>("");
|
||||
const [lastSearchedValues, setLastSearchedValues] = useState<SearchFormValues | null>(null);
|
||||
const [hasCompletedSearch, setHasCompletedSearch] = useState(false);
|
||||
const [reviewHistoryOpen, setReviewHistoryOpen] = useState<boolean>(false);
|
||||
const [historySearchInfo, setHistorySearchInfo] = useState<{
|
||||
ctpvNm: string;
|
||||
@@ -85,6 +88,8 @@ const StatsInputStatusRvw = () => {
|
||||
const handleReset = () => {
|
||||
setRowData([]);
|
||||
setSearchedYear("");
|
||||
setLastSearchedValues(null);
|
||||
setHasCompletedSearch(false);
|
||||
};
|
||||
|
||||
const handleSearch = async (values: SearchFormValues) => {
|
||||
@@ -96,6 +101,8 @@ const StatsInputStatusRvw = () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setSearchedYear(values.year ?? "");
|
||||
setLastSearchedValues(values);
|
||||
setHasCompletedSearch(false);
|
||||
|
||||
const response = await statisticsManagementApi.getStatsInputStatusList({
|
||||
ctpvNm: values.sido,
|
||||
@@ -115,11 +122,15 @@ const StatsInputStatusRvw = () => {
|
||||
setRowData(toGridRows(convertedData));
|
||||
} else {
|
||||
setRowData([]);
|
||||
setHasCompletedSearch(false);
|
||||
alert(response.message || "조회된 데이터가 없습니다.");
|
||||
return;
|
||||
}
|
||||
setHasCompletedSearch(true);
|
||||
} catch {
|
||||
alert('데이터 조회 중 오류가 발생했습니다.');
|
||||
setRowData([]);
|
||||
setHasCompletedSearch(false);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -127,6 +138,12 @@ const StatsInputStatusRvw = () => {
|
||||
|
||||
const approveFunc = () => console.log('approveFunc');
|
||||
|
||||
const gugunOkOrNotDoList = doList.includes(lastSearchedValues?.bsnmCd || "")
|
||||
? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== "ALL")
|
||||
: true;
|
||||
const showActionToolbarCard =
|
||||
hasCompletedSearch && rowData.length > 0 && gugunOkOrNotDoList;
|
||||
|
||||
const handleRowClick = async (row: Record<string, string | number>) => {
|
||||
setHistorySearchInfo({
|
||||
ctpvNm: String(row.ctpvNm ?? ""),
|
||||
@@ -157,11 +174,13 @@ const StatsInputStatusRvw = () => {
|
||||
<div className="relative">
|
||||
<SearchForm onSubmit={handleSearch} onReset={handleReset} />
|
||||
|
||||
<div className="card !py-0">
|
||||
<div className="flex justify-end items-center gap-[0.6rem] m-[0.6rem]">
|
||||
<ApproveButton approveFunc={approveFunc} />
|
||||
{showActionToolbarCard && (
|
||||
<div className="card !py-0">
|
||||
<div className="flex justify-end items-center gap-[0.6rem] m-[0.6rem]">
|
||||
<ApproveButton approveFunc={approveFunc} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
|
||||
@@ -180,6 +199,43 @@ const StatsInputStatusRvw = () => {
|
||||
rowData={rowData}
|
||||
setRowData={setRowData}
|
||||
onRowClick={handleRowClick}
|
||||
cellStyleCallback={({ row, fieldName }) => {
|
||||
if (fieldName === "submitStatusNm") {
|
||||
const statusCd = String(row.submitStatusCd ?? "");
|
||||
|
||||
if (statusCd === "0" || statusCd === "1") {
|
||||
return "rg-cell-stats-status-pending";
|
||||
}
|
||||
if (statusCd === "2") {
|
||||
return "rg-cell-stats-status-submitted";
|
||||
}
|
||||
if (statusCd === "3") {
|
||||
return "rg-cell-stats-status-approved";
|
||||
}
|
||||
if (statusCd === "4") {
|
||||
return "rg-cell-stats-status-rejected";
|
||||
}
|
||||
return undefined;
|
||||
} else if (fieldName === "prvnStatusNm") {
|
||||
const statusCd = String(row.prvnStatusCd ?? "");
|
||||
|
||||
if (statusCd === "0" || statusCd === "1") {
|
||||
return "rg-cell-stats-status-pending";
|
||||
}
|
||||
if (statusCd === "2") {
|
||||
return "rg-cell-stats-status-submitted";
|
||||
}
|
||||
if (statusCd === "3") {
|
||||
return "rg-cell-stats-status-approved";
|
||||
}
|
||||
if (statusCd === "4") {
|
||||
return "rg-cell-stats-status-rejected";
|
||||
}
|
||||
return undefined;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Dialog open={reviewHistoryOpen} onOpenChange={setReviewHistoryOpen}>
|
||||
<DialogContent
|
||||
|
||||
@@ -10,8 +10,7 @@ export const StatsInputStatusInpColumn = {
|
||||
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false },
|
||||
{ name: 'statsCd', fieldName: 'statsCd', header: { text: '메뉴코드' }, visible: false, editable: false },
|
||||
{ name: 'menuTabName', fieldName: 'menuTabName', header: { text: '구분' }, editable: false, width: 220 },
|
||||
{ name: 'locgStatusNm', fieldName: 'locgStatusNm', header: { text: '진행상황' }, editable: false },
|
||||
// { name: 'inputRate', fieldName: 'inputRate', header: { text: '작성률' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
|
||||
{ name: 'laeSbmsnSttsNm', fieldName: 'laeSbmsnSttsNm', header: { text: '진행상황' }, editable: false },
|
||||
{ name: 'laePicNm', fieldName: 'laePicNm', header: { text: '작성자' }, editable: false },
|
||||
{ name: 'laePicTelNo', fieldName: 'laePicTelNo', header: { text: '작성자연락처' }, editable: false },
|
||||
{ name: 'laeKbrdrDeptNm', fieldName: 'laeKbrdrDeptNm', header: { text: '부서' }, editable: false },
|
||||
@@ -20,6 +19,7 @@ export const StatsInputStatusInpColumn = {
|
||||
{ name: 'laeIdfrDeptNm', fieldName: 'laeIdfrDeptNm', header: { text: '부서' }, editable: false },
|
||||
{ name: 'laeIdfrJbgdNm', fieldName: 'laeIdfrJbgdNm', header: { text: '직제' }, editable: false },
|
||||
{ name: 'rjctOpnnCn', fieldName: 'rjctOpnnCn', header: { text: '반려사유' }, editable: false },
|
||||
{ name: 'laeSbmsnSttsCd', fieldName: 'laeSbmsnSttsCd', header: { text: '진행상황' }, editable: false, visible: false },
|
||||
],
|
||||
|
||||
columnLayout: [
|
||||
@@ -32,8 +32,7 @@ export const StatsInputStatusInpColumn = {
|
||||
hideChildHeaders: false,
|
||||
header: { text: '하수도사업자' },
|
||||
items: [
|
||||
'locgStatusNm',
|
||||
// 'inputRate',
|
||||
'laeSbmsnSttsNm',
|
||||
'laePicNm',
|
||||
'laePicTelNo',
|
||||
'laeKbrdrDeptNm',
|
||||
@@ -41,7 +40,8 @@ export const StatsInputStatusInpColumn = {
|
||||
'laeIdfrNm',
|
||||
'laeIdfrDeptNm',
|
||||
'laeIdfrJbgdNm',
|
||||
'rjctOpnnCn'
|
||||
'rjctOpnnCn',
|
||||
'laeSbmsnSttsCd'
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
|
||||
const currentYear = dayjs().year() - 1;
|
||||
const startYear = 1996;
|
||||
const totalCount = currentYear - startYear + 1 + 1;
|
||||
const totalCount = currentYear - startYear + 1;
|
||||
|
||||
// 하수도 통계 관리 02. 하수관로
|
||||
const rawList = [
|
||||
@@ -398,7 +398,7 @@ const rawList = [
|
||||
|
||||
|
||||
...Array.from({ length: totalCount }, (_, i) => {
|
||||
const dtoNum = totalCount - i;
|
||||
const dtoNum = i + 1;
|
||||
|
||||
const wholField = `yy${dtoNum}PrlgWhol`;
|
||||
const lyngField = `yy${dtoNum}LyngPrlg`;
|
||||
@@ -426,7 +426,8 @@ const rawList = [
|
||||
{ name: "summaryGroup", header: { text: "총계" }, items: ["pipeByPrstWhol", "lyngPrlg", "dmltnPrlg"] },
|
||||
|
||||
...Array.from({ length: totalCount }, (_, i) => {
|
||||
const dtoNum = totalCount - i;
|
||||
// const dtoNum = totalCount - i;
|
||||
const dtoNum = i + 1;
|
||||
const yearLabel = i === 0 ? "1996년이전" : `${startYear + (i - 1)}년`;
|
||||
|
||||
return {
|
||||
@@ -550,7 +551,311 @@ const rawList = [
|
||||
'etcPdmtPrlg',
|
||||
'rmrk'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
tabKey: "ST00020005",
|
||||
tabLabel: "맨홀 현황",
|
||||
rowStartNum: 3,
|
||||
fixedColumns: 3,
|
||||
columns: [
|
||||
{
|
||||
name: "ctpvNm",
|
||||
fieldName: "ctpvNm",
|
||||
header: { text: "시도" },
|
||||
mergeCells: true,
|
||||
sortdisable: true,
|
||||
footer: [
|
||||
{
|
||||
valueCallback: (grid: GridView) => {
|
||||
const divisionIndex = 0;
|
||||
const dataSource = grid.getDataSource();
|
||||
const uniqueValues = new Set<string>();
|
||||
for (let i = 0; i < dataSource.getRowCount(); i++) {
|
||||
const val = dataSource.getValue(i, "displayYear");
|
||||
if (val) uniqueValues.add(String(val));
|
||||
}
|
||||
const divisionValues = Array.from(uniqueValues);
|
||||
return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + "합계" : "합계";
|
||||
}
|
||||
},
|
||||
{
|
||||
valueCallback: (grid: GridView) => {
|
||||
const divisionIndex = 1;
|
||||
const dataSource = grid.getDataSource();
|
||||
const uniqueValues = new Set<string>();
|
||||
for (let i = 0; i < dataSource.getRowCount(); i++) {
|
||||
const val = dataSource.getValue(i, "displayYear");
|
||||
if (val) uniqueValues.add(String(val));
|
||||
}
|
||||
const divisionValues = Array.from(uniqueValues);
|
||||
return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + "합계" : "합계";
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, mergeCells: true, sortdisable: true },
|
||||
{ name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, mergeCells: true, sortdisable: true },
|
||||
{ name: "emphsMngSeNm", fieldName: "emphsMngSeNm", header: { text: "집중강우\n중점관리\n구역구분" }, mergeCells: true },
|
||||
{
|
||||
name: 'displayYear',
|
||||
fieldName: 'displayYear',
|
||||
header: { text: '구분' },
|
||||
sortdisable: true,
|
||||
footer: [
|
||||
{
|
||||
valueCallback: (grid: GridView) => {
|
||||
const divisionIndex = 0;
|
||||
const dataSource = grid.getDataSource();
|
||||
const uniqueValues = new Set<string>();
|
||||
for (let i = 0; i < dataSource.getRowCount(); i++) {
|
||||
const val = dataSource.getValue(i, 'displayYear');
|
||||
if (val) uniqueValues.add(String(val));
|
||||
}
|
||||
const divisionValues = Array.from(uniqueValues);
|
||||
return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + '합계' : '';
|
||||
}
|
||||
},
|
||||
{
|
||||
valueCallback: (grid: GridView) => {
|
||||
const divisionIndex = 1;
|
||||
const dataSource = grid.getDataSource();
|
||||
const uniqueValues = new Set<string>();
|
||||
for (let i = 0; i < dataSource.getRowCount(); i++) {
|
||||
const val = dataSource.getValue(i, 'displayYear');
|
||||
if (val) uniqueValues.add(String(val));
|
||||
}
|
||||
const divisionValues = Array.from(uniqueValues);
|
||||
return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + '합계' : '';
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{ name: "totalWholMnhlCnt", fieldName: "totalWholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "totalStep1MnhlCnt", fieldName: "totalStep1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "totalStep2MnhlCnt", fieldName: "totalStep2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "totalStep3MnhlCnt", fieldName: "totalStep3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "totalStep4MnhlCnt", fieldName: "totalStep4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "totalStep5MnhlCnt", fieldName: "totalStep5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2015년 이전 (yy14) ──────────────────────────────────
|
||||
{ name: "yy14WholMnhlCnt", fieldName: "yy14WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy14Step1MnhlCnt", fieldName: "yy14Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy14Step2MnhlCnt", fieldName: "yy14Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy14Step3MnhlCnt", fieldName: "yy14Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy14Step4MnhlCnt", fieldName: "yy14Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy14Step5MnhlCnt", fieldName: "yy14Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2015년 (yy15) ───────────────────────────────────────
|
||||
{ name: "yy15WholMnhlCnt", fieldName: "yy15WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy15Step1MnhlCnt", fieldName: "yy15Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy15Step2MnhlCnt", fieldName: "yy15Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy15Step3MnhlCnt", fieldName: "yy15Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy15Step4MnhlCnt", fieldName: "yy15Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy15Step5MnhlCnt", fieldName: "yy15Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2016년 (yy16) ───────────────────────────────────────
|
||||
{ name: "yy16WholMnhlCnt", fieldName: "yy16WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy16Step1MnhlCnt", fieldName: "yy16Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy16Step2MnhlCnt", fieldName: "yy16Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy16Step3MnhlCnt", fieldName: "yy16Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy16Step4MnhlCnt", fieldName: "yy16Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy16Step5MnhlCnt", fieldName: "yy16Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2017년 (yy17) ───────────────────────────────────────
|
||||
{ name: "yy17WholMnhlCnt", fieldName: "yy17WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy17Step1MnhlCnt", fieldName: "yy17Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy17Step2MnhlCnt", fieldName: "yy17Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy17Step3MnhlCnt", fieldName: "yy17Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy17Step4MnhlCnt", fieldName: "yy17Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy17Step5MnhlCnt", fieldName: "yy17Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2018년 (yy18) ───────────────────────────────────────
|
||||
{ name: "yy18WholMnhlCnt", fieldName: "yy18WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy18Step1MnhlCnt", fieldName: "yy18Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy18Step2MnhlCnt", fieldName: "yy18Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy18Step3MnhlCnt", fieldName: "yy18Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy18Step4MnhlCnt", fieldName: "yy18Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy18Step5MnhlCnt", fieldName: "yy18Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2019년 (yy19) ───────────────────────────────────────
|
||||
{ name: "yy19WholMnhlCnt", fieldName: "yy19WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy19Step1MnhlCnt", fieldName: "yy19Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy19Step2MnhlCnt", fieldName: "yy19Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy19Step3MnhlCnt", fieldName: "yy19Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy19Step4MnhlCnt", fieldName: "yy19Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy19Step5MnhlCnt", fieldName: "yy19Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2020년 (yy20) ───────────────────────────────────────
|
||||
{ name: "yy20WholMnhlCnt", fieldName: "yy20WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy20Step1MnhlCnt", fieldName: "yy20Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy20Step2MnhlCnt", fieldName: "yy20Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy20Step3MnhlCnt", fieldName: "yy20Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy20Step4MnhlCnt", fieldName: "yy20Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy20Step5MnhlCnt", fieldName: "yy20Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2021년 (yy21) ───────────────────────────────────────
|
||||
{ name: "yy21WholMnhlCnt", fieldName: "yy21WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy21Step1MnhlCnt", fieldName: "yy21Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy21Step2MnhlCnt", fieldName: "yy21Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy21Step3MnhlCnt", fieldName: "yy21Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy21Step4MnhlCnt", fieldName: "yy21Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy21Step5MnhlCnt", fieldName: "yy21Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2022년 (yy22) ───────────────────────────────────────
|
||||
{ name: "yy22WholMnhlCnt", fieldName: "yy22WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy22Step1MnhlCnt", fieldName: "yy22Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy22Step2MnhlCnt", fieldName: "yy22Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy22Step3MnhlCnt", fieldName: "yy22Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy22Step4MnhlCnt", fieldName: "yy22Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy22Step5MnhlCnt", fieldName: "yy22Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2023년 (yy23) ───────────────────────────────────────
|
||||
{ name: "yy23WholMnhlCnt", fieldName: "yy23WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy23Step1MnhlCnt", fieldName: "yy23Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy23Step2MnhlCnt", fieldName: "yy23Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy23Step3MnhlCnt", fieldName: "yy23Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy23Step4MnhlCnt", fieldName: "yy23Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy23Step5MnhlCnt", fieldName: "yy23Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2024년 (yy24) ───────────────────────────────────────
|
||||
{ name: "yy24WholMnhlCnt", fieldName: "yy24WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy24Step1MnhlCnt", fieldName: "yy24Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy24Step2MnhlCnt", fieldName: "yy24Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy24Step3MnhlCnt", fieldName: "yy24Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy24Step4MnhlCnt", fieldName: "yy24Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy24Step5MnhlCnt", fieldName: "yy24Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
// ── 2025년 (yy25) ───────────────────────────────────────
|
||||
{ name: "yy25WholMnhlCnt", fieldName: "yy25WholMnhlCnt", header: { text: "계" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy25Step1MnhlCnt", fieldName: "yy25Step1MnhlCnt", header: { text: "주철" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy25Step2MnhlCnt", fieldName: "yy25Step2MnhlCnt", header: { text: "주철(조화)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy25Step3MnhlCnt", fieldName: "yy25Step3MnhlCnt", header: { text: "콘크리트" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy25Step4MnhlCnt", fieldName: "yy25Step4MnhlCnt", header: { text: "기타" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
{ name: "yy25Step5MnhlCnt", fieldName: "yy25Step5MnhlCnt", header: { text: "추락방지시설\n설치맨홀(개)" }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
|
||||
|
||||
{ name: "rmrk", fieldName: "rmrk", header: { text: "비고" }, editable: false },
|
||||
],
|
||||
columnLayout: [
|
||||
{
|
||||
name: "addrGroup",
|
||||
header: { text: "행정구역" },
|
||||
items: [
|
||||
{ column: "ctpvNm", footerUserSpans: [{ colspan: 5 }, { colspan: 5 }] },
|
||||
"sggNm",
|
||||
"admdstNm",
|
||||
],
|
||||
},
|
||||
"emphsMngSeNm",
|
||||
"displayYear",
|
||||
{
|
||||
name: "totGroup",
|
||||
header: { text: "총계" },
|
||||
items: [
|
||||
{ name: "totMtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["totalWholMnhlCnt", "totalStep1MnhlCnt", "totalStep2MnhlCnt", "totalStep3MnhlCnt", "totalStep4MnhlCnt"] },
|
||||
"totalStep5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy14Group",
|
||||
header: { text: "2015년 이전" }, // ← _14Y
|
||||
items: [
|
||||
{ name: "yy14MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy14WholMnhlCnt", "yy14Step1MnhlCnt", "yy14Step2MnhlCnt", "yy14Step3MnhlCnt", "yy14Step4MnhlCnt"] },
|
||||
"yy14Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy15Group",
|
||||
header: { text: "2015년" }, // ← _15Y
|
||||
items: [
|
||||
{ name: "yy15MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy15WholMnhlCnt", "yy15Step1MnhlCnt", "yy15Step2MnhlCnt", "yy15Step3MnhlCnt", "yy15Step4MnhlCnt"] },
|
||||
"yy15Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy16Group",
|
||||
header: { text: "2016년" }, // ← _16Y
|
||||
items: [
|
||||
{ name: "yy16MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy16WholMnhlCnt", "yy16Step1MnhlCnt", "yy16Step2MnhlCnt", "yy16Step3MnhlCnt", "yy16Step4MnhlCnt"] },
|
||||
"yy16Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy17Group",
|
||||
header: { text: "2017년" },
|
||||
items: [
|
||||
{ name: "yy17MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy17WholMnhlCnt", "yy17Step1MnhlCnt", "yy17Step2MnhlCnt", "yy17Step3MnhlCnt", "yy17Step4MnhlCnt"] },
|
||||
"yy17Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy18Group",
|
||||
header: { text: "2018년" },
|
||||
items: [
|
||||
{ name: "yy18MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy18WholMnhlCnt", "yy18Step1MnhlCnt", "yy18Step2MnhlCnt", "yy18Step3MnhlCnt", "yy18Step4MnhlCnt"] },
|
||||
"yy18Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy19Group",
|
||||
header: { text: "2019년" },
|
||||
items: [
|
||||
{ name: "yy19MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy19WholMnhlCnt", "yy19Step1MnhlCnt", "yy19Step2MnhlCnt", "yy19Step3MnhlCnt", "yy19Step4MnhlCnt"] },
|
||||
"yy19Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy20Group",
|
||||
header: { text: "2020년" },
|
||||
items: [
|
||||
{ name: "yy20MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy20WholMnhlCnt", "yy20Step1MnhlCnt", "yy20Step2MnhlCnt", "yy20Step3MnhlCnt", "yy20Step4MnhlCnt"] },
|
||||
"yy20Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy21Group",
|
||||
header: { text: "2021년" },
|
||||
items: [
|
||||
{ name: "yy21MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy21WholMnhlCnt", "yy21Step1MnhlCnt", "yy21Step2MnhlCnt", "yy21Step3MnhlCnt", "yy21Step4MnhlCnt"] },
|
||||
"yy21Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy22Group",
|
||||
header: { text: "2022년" },
|
||||
items: [
|
||||
{ name: "yy22MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy22WholMnhlCnt", "yy22Step1MnhlCnt", "yy22Step2MnhlCnt", "yy22Step3MnhlCnt", "yy22Step4MnhlCnt"] },
|
||||
"yy22Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy23Group",
|
||||
header: { text: "2023년" },
|
||||
items: [
|
||||
{ name: "yy23MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy23WholMnhlCnt", "yy23Step1MnhlCnt", "yy23Step2MnhlCnt", "yy23Step3MnhlCnt", "yy23Step4MnhlCnt"] },
|
||||
"yy23Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy24Group",
|
||||
header: { text: "2024년" },
|
||||
items: [
|
||||
{ name: "yy24MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy24WholMnhlCnt", "yy24Step1MnhlCnt", "yy24Step2MnhlCnt", "yy24Step3MnhlCnt", "yy24Step4MnhlCnt"] },
|
||||
"yy24Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "yy25Group",
|
||||
header: { text: "2025년" },
|
||||
items: [
|
||||
{ name: "yy25MtrlGroup", header: { text: "맨홀뚜껑 재질(개)" }, items: ["yy25WholMnhlCnt", "yy25Step1MnhlCnt", "yy25Step2MnhlCnt", "yy25Step3MnhlCnt", "yy25Step4MnhlCnt"] },
|
||||
"yy25Step5MnhlCnt",
|
||||
],
|
||||
},
|
||||
"rmrk",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const SewagePipeColumnInfoListRvw = rawList.map(tab => ({
|
||||
|
||||
@@ -38,6 +38,5 @@ export const StatsInputStatusInfoList = {
|
||||
columns: StatsInputStatusInfoColumnRaw.columns.map(col => ({
|
||||
...col,
|
||||
editable: false,
|
||||
styleName: "rg-cell-readonly"
|
||||
}))
|
||||
};
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
type SewagePipeFacilityApiResponse,
|
||||
type SewerPipeTypeApiResponse,
|
||||
type SewerPipeDiamApiResponse,
|
||||
type SewagePipeManholeApiResponse,
|
||||
} from "../model/statistics-sewage-pipe-review-interface";
|
||||
import {
|
||||
type SewerPipeMtcApiResponse,
|
||||
@@ -135,6 +136,20 @@ export class StatisticsManagementApiService {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 02. 하수관로 맨홀 현황 조회 (Tab 5)
|
||||
* @param params (sido, gugun, yr, bsnmCd)
|
||||
*/
|
||||
public async getSewagePipeManholeList(
|
||||
params: SewagePipeReviewRequestParams
|
||||
): Promise<SewagePipeManholeApiResponse> {
|
||||
const response = await stClient.get<SewagePipeManholeApiResponse>(
|
||||
`${ENDPOINT.ST_SERVICE.REVIEW}/sewer-pipe/manhole`,
|
||||
{ params }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 03. 하수관로 유지관리 개보수 현황 조회
|
||||
* @param params (yr, bsnmCd, sido, gugun)
|
||||
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
type ImrRequest,
|
||||
type ImrResponse,
|
||||
type ImrStatusResponse,
|
||||
type ReviewerInfoRequest,
|
||||
type ReviewerInfoResponse,
|
||||
type ReviewOpinionSaveRequest,
|
||||
type ReviewOpinionSaveResponse,
|
||||
type ReviewReturnSaveRequest,
|
||||
@@ -25,6 +27,8 @@ import {
|
||||
type StatisticsService,
|
||||
type StatsInputPeriodDeleteApiResponse,
|
||||
type StatsInputPeriodDeleteRequest,
|
||||
type StatsInputPeriodDetailRequest,
|
||||
type StatsInputPeriodDetailResponse,
|
||||
type StatsInputPeriodListApiResponse,
|
||||
type StatsInputPeriodListRequest,
|
||||
type StatsInputPeriodSaveApiResponse,
|
||||
@@ -81,6 +85,14 @@ export class StatisticsApiService implements StatisticsService {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getStatsInputPeriodDetail(params: StatsInputPeriodDetailRequest): Promise<StatsInputPeriodDetailResponse> {
|
||||
const response = await stClient.get<StatsInputPeriodDetailResponse>(
|
||||
`${ENDPOINT.ST_SERVICE.DASHBOARD}/schedule-info/detail`,
|
||||
{ params },
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/** 통계 입력 기간 저장 — 경로는 백엔드 스펙에 맞게 수정 */
|
||||
public async saveStatsInputPeriod(
|
||||
param: StatsInputPeriodSaveRequest,
|
||||
@@ -92,17 +104,6 @@ export class StatisticsApiService implements StatisticsService {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/** 통계 입력 기간 수정 — 경로는 저장과 동일, 메서드는 PUT */
|
||||
public async updateStatsInputPeriod(
|
||||
param: StatsInputPeriodSaveRequest,
|
||||
): Promise<StatsInputPeriodSaveApiResponse> {
|
||||
const response = await stClient.put<StatsInputPeriodSaveApiResponse>(
|
||||
`${ENDPOINT.ST_SERVICE.DASHBOARD}/schedule-info`,
|
||||
param,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/** 통계 입력 기간 삭제 — JSON body. HTTP 메서드·URL은 백엔드 스펙에 맞게 수정 */
|
||||
public async deleteStatsInputPeriod(
|
||||
param: StatsInputPeriodDeleteRequest,
|
||||
@@ -238,6 +239,14 @@ export class StatisticsApiService implements StatisticsService {
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getReviewerInfo(param: ReviewerInfoRequest): Promise<ReviewerInfoResponse> {
|
||||
const response = await stClient.get<ReviewerInfoResponse>(
|
||||
`${ENDPOINT.ST_SERVICE.REVIEW}/reviewer-info`,
|
||||
{ params: param },
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const statisticsApi = new StatisticsApiService();
|
||||
|
||||
@@ -15,8 +15,8 @@ export interface StatsInputStatusList {
|
||||
statsCd?: string;
|
||||
sewerBzmnCd?: string;
|
||||
menuTabName?: string;
|
||||
locgStatusNm?: string;
|
||||
inputRate?: number;
|
||||
laeSbmsnSttsNm?: string;
|
||||
laeSbmsnSttsCd?: string;
|
||||
laePicNm?: string;
|
||||
laePicTelNo?: string;
|
||||
laeKbrdrDeptNm?: string;
|
||||
|
||||
+4
-4
@@ -87,9 +87,9 @@ export interface SewerageSupplyRateRequestParams {
|
||||
/** 하수도 보급률 저장 API 요청 body (한 행) */
|
||||
export interface SewerageSupplyRateSaveItem {
|
||||
sggNm: string | null;
|
||||
admdstCd: string;
|
||||
admdstNm: string;
|
||||
trbtrNm: string;
|
||||
admdstCd: string | null;
|
||||
admdstNm: string | null;
|
||||
trbtrNm: string | null;
|
||||
spcCtpnRgnCn: string | null;
|
||||
ts1SwtrmtPpltnCnt: number;
|
||||
ts2SwtrmtPpltnCnt: number;
|
||||
@@ -103,7 +103,7 @@ export interface SewerageSupplyRateSaveItem {
|
||||
drtwPrcsPpltnCnt: number;
|
||||
spctnkPpltnCnt: number;
|
||||
staaExcpArea: number;
|
||||
rmrk: string;
|
||||
rmrk: string | null;
|
||||
wholPpltnCnt: number;
|
||||
wholArea: number;
|
||||
wholSwtrmtPpltnCnt: number;
|
||||
|
||||
@@ -51,10 +51,9 @@ export type StatisticsColumnGuideListResponse = ApiResponse<StatisticsColumnGuid
|
||||
export interface StatsInputPeriodItem {
|
||||
statsYr: string;
|
||||
seCd: number;
|
||||
seType: "INPT" | "VRFC";
|
||||
seType: string;
|
||||
inptBgngYmd: string;
|
||||
inptEndYmd: string;
|
||||
inptYmdSn: number | null;
|
||||
}
|
||||
|
||||
/** `data`가 `StatsInputPeriodItem[]` 인 조회 응답 */
|
||||
@@ -63,16 +62,26 @@ export type StatsInputPeriodListApiResponse = ApiResponse<StatsInputPeriodItem[]
|
||||
/** 통계 입력 기간 목록 조회 요청 쿼리 */
|
||||
export interface StatsInputPeriodListRequest {
|
||||
statsYr?: string;
|
||||
/** 전체 / 입력 / 검증 (SearchForm `clsfCd`와 동일) */
|
||||
seType?: "" | "ALL" | "INPT" | "VRFC";
|
||||
seType?: string;
|
||||
}
|
||||
|
||||
export interface StatsInputPeriodDetailRequest {
|
||||
statsYr: string;
|
||||
seType: string;
|
||||
seCd: number;
|
||||
}
|
||||
|
||||
export interface StatsInputPeriodDetailResponse {
|
||||
status: string;
|
||||
message?: string;
|
||||
data: StatsInputPeriodItem;
|
||||
}
|
||||
|
||||
/** 통계 입력 기간 등록·수정 등 요청 본문 */
|
||||
export interface StatsInputPeriodSaveRequest {
|
||||
statsYr: string;
|
||||
seType: string;
|
||||
inptYmdSn: number;
|
||||
seCd: number;
|
||||
seCd?: number;
|
||||
/** 시작일 (백엔드 형식 문자열, 예: YYYYMMDD) */
|
||||
inptBgngYmd: string;
|
||||
/** 종료일 (백엔드 형식 문자열, 예: YYYYMMDD) */
|
||||
@@ -83,8 +92,15 @@ export interface StatsInputPeriodSaveRequest {
|
||||
/** 통계 입력 기간 저장 응답 */
|
||||
export type StatsInputPeriodSaveApiResponse = ApiResponse<unknown>;
|
||||
|
||||
/** 통계 입력 기간 삭제 요청 (필드 구성은 저장과 동일) */
|
||||
export type StatsInputPeriodDeleteRequest = StatsInputPeriodSaveRequest;
|
||||
/** 통계 입력 기간 삭제 요청 */
|
||||
export interface StatsInputPeriodDeleteRequest {
|
||||
statsYr: string;
|
||||
seType: string;
|
||||
seCd: number;
|
||||
inptBgngYmd: string;
|
||||
inptEndYmd: string;
|
||||
rgtrId: string;
|
||||
}
|
||||
|
||||
/** 통계 입력 기간 삭제 응답 */
|
||||
export type StatsInputPeriodDeleteApiResponse = ApiResponse<unknown>;
|
||||
@@ -327,8 +343,10 @@ export interface ReviewReturnSaveRequest {
|
||||
}
|
||||
|
||||
export interface ReviewReturnSaveResponse {
|
||||
status: string;
|
||||
message?: string;
|
||||
exceptionCnt: number;
|
||||
message: string;
|
||||
resultCode: string;
|
||||
updatedStts: string;
|
||||
}
|
||||
|
||||
export interface ReviewSubmitRequest {
|
||||
@@ -336,6 +354,8 @@ export interface ReviewSubmitRequest {
|
||||
sewerBzmnCd: string;
|
||||
statsCd: string;
|
||||
mdfrId: string;
|
||||
splmntOpnnCn: string;
|
||||
wrterOpnnCn: string;
|
||||
}
|
||||
|
||||
export interface ReviewSubmitResponse {
|
||||
@@ -361,17 +381,32 @@ export interface SubmitVerifyExceptionResponse {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface ReviewerInfoRequest {
|
||||
sewerBzmnCd: string;
|
||||
statsYr: string;
|
||||
statsCd: string;
|
||||
instSeCd : string;
|
||||
}
|
||||
|
||||
export interface ReviewerInfoResponse {
|
||||
status: string;
|
||||
data: {
|
||||
userNm: string;
|
||||
telOffrm: string;
|
||||
dpmNm: string;
|
||||
cps: string;
|
||||
splmntOpnnCn: string;
|
||||
wrterOpnnCn: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface StatisticsService {
|
||||
getSidoList(params?: { instSeCd?: string }): Promise<ApiResponse<SearchCondition>>;
|
||||
getSggList(sidoName: string, params?: { instSeCd?: string; gugunSkipYn?: "Y" | "N" }): Promise<ApiResponse<Sgg[]>>;
|
||||
getSggAllList(sidoName: string, params?: { instSeCd?: string; gugunSkipYn?: "Y" | "N" }): Promise<ApiResponse<Sgg[]>>;
|
||||
getStatsInputPeriodList(
|
||||
params?: StatsInputPeriodListRequest,
|
||||
): Promise<StatsInputPeriodListApiResponse>;
|
||||
/** 입력 기간 등록 (POST) */
|
||||
getStatsInputPeriodList(params?: StatsInputPeriodListRequest): Promise<StatsInputPeriodListApiResponse>;
|
||||
getStatsInputPeriodDetail(param: StatsInputPeriodDetailRequest): Promise<StatsInputPeriodDetailResponse>;
|
||||
saveStatsInputPeriod(param: StatsInputPeriodSaveRequest): Promise<StatsInputPeriodSaveApiResponse>;
|
||||
/** 입력 기간 수정 (PUT) */
|
||||
updateStatsInputPeriod(param: StatsInputPeriodSaveRequest): Promise<StatsInputPeriodSaveApiResponse>;
|
||||
deleteStatsInputPeriod(param: StatsInputPeriodDeleteRequest): Promise<StatsInputPeriodDeleteApiResponse>;
|
||||
cancelSubmit(param: cancelSubmitRequest): Promise<cancelSubmitResponse>;
|
||||
callImrService(param: ImrRequest): Promise<ImrResponse>;
|
||||
@@ -385,6 +420,6 @@ export interface StatisticsService {
|
||||
getVerificationResultList(param: VerificationResultListRequest): Promise<VerificationResultListResponse>;
|
||||
getImrStatus(param: string): Promise<ImrStatusResponse>;
|
||||
SubmitVerifyException(param: SubmitVerifyExceptionRequest): Promise<SubmitVerifyExceptionResponse>;
|
||||
/** `INPUTCOMMON/column-guides` — 통계코드 등 단일 쿼리 문자열 */
|
||||
getColumnGuides(param: string): Promise<StatisticsColumnGuideListResponse>;
|
||||
getReviewerInfo(param: ReviewerInfoRequest): Promise<ReviewerInfoResponse>;
|
||||
}
|
||||
+1
-1
@@ -79,7 +79,7 @@ export interface PublicSewageFacilityReviewRequestParams {
|
||||
sggNm?: string;
|
||||
statsYr?: string;
|
||||
sewerBzmnCd?: string;
|
||||
fctName?: string;
|
||||
swtrmtFcltNm?: string;
|
||||
}
|
||||
|
||||
export type PublicSewageFacilityReviewApiResponse =
|
||||
|
||||
@@ -244,6 +244,118 @@ export interface SewagePipeDiamService {
|
||||
getSewagePipeDiamList(params: SewagePipeReviewRequestParams): Promise<SewerPipeDiamApiResponse>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 02. 하수관로 맨홀 현황 아이템 (Tab 5)
|
||||
*/
|
||||
export interface SewagePipeReviewManholeItem {
|
||||
ctpvNm: string;
|
||||
sggNm: string;
|
||||
admdstNm: string;
|
||||
admdstCd: string;
|
||||
statsYr: string;
|
||||
displayYear: string;
|
||||
rowType: 'OLD' | 'CURR';
|
||||
emphsMngSeCd: string;
|
||||
emphsMngSeNm?: string;
|
||||
|
||||
yy14WholMnhlCnt: number;
|
||||
yy14Step1MnhlCnt: number;
|
||||
yy14Step2MnhlCnt: number;
|
||||
yy14Step3MnhlCnt: number;
|
||||
yy14Step4MnhlCnt: number;
|
||||
yy14Step5MnhlCnt: number;
|
||||
|
||||
yy15WholMnhlCnt: number;
|
||||
yy15Step1MnhlCnt: number;
|
||||
yy15Step2MnhlCnt: number;
|
||||
yy15Step3MnhlCnt: number;
|
||||
yy15Step4MnhlCnt: number;
|
||||
yy15Step5MnhlCnt: number;
|
||||
|
||||
yy16WholMnhlCnt: number;
|
||||
yy16Step1MnhlCnt: number;
|
||||
yy16Step2MnhlCnt: number;
|
||||
yy16Step3MnhlCnt: number;
|
||||
yy16Step4MnhlCnt: number;
|
||||
yy16Step5MnhlCnt: number;
|
||||
|
||||
yy17WholMnhlCnt: number;
|
||||
yy17Step1MnhlCnt: number;
|
||||
yy17Step2MnhlCnt: number;
|
||||
yy17Step3MnhlCnt: number;
|
||||
yy17Step4MnhlCnt: number;
|
||||
yy17Step5MnhlCnt: number;
|
||||
|
||||
yy18WholMnhlCnt: number;
|
||||
yy18Step1MnhlCnt: number;
|
||||
yy18Step2MnhlCnt: number;
|
||||
yy18Step3MnhlCnt: number;
|
||||
yy18Step4MnhlCnt: number;
|
||||
yy18Step5MnhlCnt: number;
|
||||
|
||||
yy19WholMnhlCnt: number;
|
||||
yy19Step1MnhlCnt: number;
|
||||
yy19Step2MnhlCnt: number;
|
||||
yy19Step3MnhlCnt: number;
|
||||
yy19Step4MnhlCnt: number;
|
||||
yy19Step5MnhlCnt: number;
|
||||
|
||||
yy20WholMnhlCnt: number;
|
||||
yy20Step1MnhlCnt: number;
|
||||
yy20Step2MnhlCnt: number;
|
||||
yy20Step3MnhlCnt: number;
|
||||
yy20Step4MnhlCnt: number;
|
||||
yy20Step5MnhlCnt: number;
|
||||
|
||||
yy21WholMnhlCnt: number;
|
||||
yy21Step1MnhlCnt: number;
|
||||
yy21Step2MnhlCnt: number;
|
||||
yy21Step3MnhlCnt: number;
|
||||
yy21Step4MnhlCnt: number;
|
||||
yy21Step5MnhlCnt: number;
|
||||
|
||||
yy22WholMnhlCnt: number;
|
||||
yy22Step1MnhlCnt: number;
|
||||
yy22Step2MnhlCnt: number;
|
||||
yy22Step3MnhlCnt: number;
|
||||
yy22Step4MnhlCnt: number;
|
||||
yy22Step5MnhlCnt: number;
|
||||
|
||||
yy23WholMnhlCnt: number;
|
||||
yy23Step1MnhlCnt: number;
|
||||
yy23Step2MnhlCnt: number;
|
||||
yy23Step3MnhlCnt: number;
|
||||
yy23Step4MnhlCnt: number;
|
||||
yy23Step5MnhlCnt: number;
|
||||
|
||||
yy24WholMnhlCnt: number;
|
||||
yy24Step1MnhlCnt: number;
|
||||
yy24Step2MnhlCnt: number;
|
||||
yy24Step3MnhlCnt: number;
|
||||
yy24Step4MnhlCnt: number;
|
||||
yy24Step5MnhlCnt: number;
|
||||
|
||||
yy25WholMnhlCnt: number;
|
||||
yy25Step1MnhlCnt: number;
|
||||
yy25Step2MnhlCnt: number;
|
||||
yy25Step3MnhlCnt: number;
|
||||
yy25Step4MnhlCnt: number;
|
||||
yy25Step5MnhlCnt: number;
|
||||
|
||||
rmrk: string | null;
|
||||
}
|
||||
|
||||
export interface SewagePipeManholeResponseData {
|
||||
manholeList: SewagePipeReviewManholeItem[];
|
||||
statsStatusInfo: StatsStatusInfo;
|
||||
}
|
||||
|
||||
export type SewagePipeManholeApiResponse = ApiResponse<SewagePipeManholeResponseData>;
|
||||
|
||||
export interface SewagePipeManholeService {
|
||||
getSewagePipeManholeList(params: SewagePipeReviewRequestParams): Promise<SewagePipeManholeApiResponse>;
|
||||
}
|
||||
|
||||
export interface StatsStatusInfo {
|
||||
statsCd?: string;
|
||||
submitStatusCd?: string;
|
||||
|
||||
Reference in New Issue
Block a user