[st] - fix: 권한 및 데이터 상태에 따른 버튼 표출 조건식 오류 수정, 2-6 & 6-2 저장 가능 여부 기능 추가

This commit is contained in:
2026-05-18 16:26:10 +09:00
parent 7a0a8f23b7
commit 91e505f37f
11 changed files with 127 additions and 36 deletions
@@ -334,7 +334,23 @@ const SidoSggFacilityNameSearchForm = ({
}
};
const handleSaveAdminRegions = () => {
const handleSaveAdminRegions = async () => {
const statusCodeResponse = await statisticsApi.CheckStatusCode({
sewerBzmnCd: initialSearchValues?.bsnmCd?.trim() ?? "",
statsCd: 'ST00020002',
statsYr: statsYr?.trim() ?? ''
});
if (statusCodeResponse.status === 'success') {
if (!['0', '1', '4'].includes(statusCodeResponse.data)) {
const codeName = statusCodeResponse.data === '2' ? '제출' : (statusCodeResponse.data === '3' ? '승인' : '검증중');
alert(`시설별 설치 현황 데이터가 ${codeName} 상태일 때는 처리 시설 정보를 변경할 수 없습니다.`);
return;
}
} else {
alert('오류가 발생했습니다.\n관리자에게 문의하세요')
}
if (!statsYr?.trim()) {
alert("상단에서 통계연도로 조회한 뒤 이용해 주세요.");
return;
@@ -240,11 +240,6 @@ export function MainFacilityMappingSaveDialog({
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button type="button" color="gray" disabled={isSubmitting}>
취소
</Button>
</DialogClose>
<Button
type="button"
color="primary"
@@ -253,6 +248,11 @@ export function MainFacilityMappingSaveDialog({
>
저장
</Button>
<DialogClose asChild>
<Button type="button" color="gray" disabled={isSubmitting}>
취소
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -87,13 +87,16 @@ function computeTabToolbarSnapshot(
): TabToolbarSnapshot {
const gugunOk = buildGugunOkForToolbar(searchContext);
const enterable = computeTabEnterable(statusInfo, periodEnterable);
const isSubmitted = statusInfo?.laeSbmsnSttsCd === '2';
const showSaveInToolbar = (enterable || isAdminInfo) && gugunOk;
const showEnterableToolbarButtons = enterable && gugunOk;
const showSubmittedInInputPeriodToolbar = isSubmitted && periodEnterable && gugunOk;
const showActionToolbarCard =
(hasRowData || treatAsSearched) &&
(showSaveInToolbar ||
(statusInfo?.laeSbmsnSttsCd === '2' &&
(isAdminInfo || (enterable && gugunOk))));
showEnterableToolbarButtons ||
showSubmittedInInputPeriodToolbar ||
(isSubmitted && isAdminInfo));
return { showSaveInToolbar, showEnterableToolbarButtons, showActionToolbarCard };
}
@@ -411,6 +414,22 @@ const PublicSewageFacilityInp = () => {
}
if (activeTab === 'ST00060002') {
const statusCodeResponse = await statisticsApi.CheckStatusCode({
sewerBzmnCd: lastSearchedValues.bsnmCd ?? '',
statsCd: 'ST00060001',
statsYr: lastSearchedValues.year ?? ''
});
if (statusCodeResponse.status === 'success') {
if (!['0', '1', '4'].includes(statusCodeResponse.data)) {
const codeName = statusCodeResponse.data === '2' ? '제출' : (statusCodeResponse.data === '3' ? '승인' : '검증중');
alert(`공공 하수처리 시설 데이터가 ${codeName} 상태일 때는 중심 처리 시설 데이터를 변경할 수 없습니다.`);
return;
}
} else {
alert('오류가 발생했습니다.\n관리자에게 문의하세요')
}
if (!(await confirm('저장 하시겠습니까?'))) return;
const leftRows = rowDataMap.get('MainFacility') ?? [];
@@ -980,26 +999,34 @@ const PublicSewageFacilityInp = () => {
};
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const gugunOkOrNotDoList = buildGugunOkForToolbar(searchContextForToolbar);
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabToolbar =
toolbarSnapshotByTab.get(activeTab) ??
computeTabToolbarSnapshot(
activeTabStatus,
tabRowDataExists(activeTab),
hasSearched,
searchContextForToolbar,
inputPeriodEnterable,
isAdminInfo,
);
const activeTabToolbar = computeTabToolbarSnapshot(
activeTabStatus,
tabRowDataExists(activeTab),
hasSearched,
searchContextForToolbar,
inputPeriodEnterable,
isAdminInfo,
);
const {
showSaveInToolbar: baseShowSaveInToolbar,
showEnterableToolbarButtons: baseShowEnterableToolbarButtons,
showActionToolbarCard: baseShowActionToolbarCard,
} = activeTabToolbar;
const showSaveInToolbar = baseShowSaveInToolbar && toolbarGroupAllowed;
const showEnterableToolbarButtons = baseShowEnterableToolbarButtons && toolbarGroupAllowed;
const showActionToolbarCard = baseShowActionToolbarCard && toolbarGroupAllowed;
const isMainFacilityTab = activeTab === 'ST00060002';
const showMainFacilitySaveInToolbar =
isMainFacilityTab &&
tabRowDataExists('ST00060002') &&
gugunOkOrNotDoList &&
(isAdminInfo || inputPeriodEnterable);
const showSaveInToolbar =
toolbarGroupAllowed && (baseShowSaveInToolbar || showMainFacilitySaveInToolbar);
const showEnterableToolbarButtons =
baseShowEnterableToolbarButtons && toolbarGroupAllowed;
const showActionToolbarCard =
toolbarGroupAllowed &&
(baseShowActionToolbarCard || showMainFacilitySaveInToolbar);
const openVerificationHistoryDialog = () => {
const ctx = lastSearchedValues ?? searchValues;
@@ -1037,9 +1064,11 @@ const PublicSewageFacilityInp = () => {
{showActionToolbarCard && (
<div className="card !py-0">
<div className="flex justify-end items-center gap-[0.6rem] mx-[0.6rem]">
<VerificationResultButton
VerificationResultButtonFunc={openVerificationHistoryDialog}
/>
{!isMainFacilityTab && (
<VerificationResultButton
VerificationResultButtonFunc={openVerificationHistoryDialog}
/>
)}
{showEnterableToolbarButtons && !isMainFacilityTab && (
<>
{showIsNotApplicable && <NotApplicableCheckButton isCheck={isNotApplicable} setIsCheck={setIsNotApplicable} />}
@@ -1159,13 +1188,25 @@ const PublicSewageFacilityInp = () => {
}
const gridColumnInfo = getGridColumnInfo(tab);
const tabStatus = tabStatusInfoMap.get(tab.tabKey) ?? null;
const tabEnterableForGrid = computeTabEnterable(
tabStatus,
inputPeriodEnterable,
);
const excelToolbarEnabledForTab =
tabRowDataExists(tab.tabKey) &&
tabEnterableForGrid &&
gugunOkOrNotDoList;
return (
<TabsContent key={tab.tabKey} value={tab.tabKey}>
<DataGrid
key={(tabEnterableForGrid || isAdminInfo) ? 'enterable' : 'readonly'}
columnInfo={gridColumnInfo}
rowStartNum={tab.rowStartNum}
excelExportFileSuffix={tab.tabLabel || tab.tabKey}
fixedColumns={tab.fixedColumns}
statusInfo={activeTabStatus}
statusInfo={tabStatus}
rowData={rowDataMap.get(tab.tabKey) ?? []}
setRowData={(updater) => {
setRowDataMap((prev) => {
@@ -1182,6 +1223,8 @@ const PublicSewageFacilityInp = () => {
getGridDataRef={
tab.tabKey === 'ST00060001' ? st60001GridGetRowsRef : undefined
}
isExcelImport={excelToolbarEnabledForTab}
isExcelExport
showHeaderCheckbox={false}
enableSort={false}
cellStyleCallback={({ row, fieldName }) => {
@@ -332,13 +332,16 @@ function computeTabToolbarSnapshot(
): TabToolbarSnapshot {
const gugunOk = buildGugunOkForToolbar(searchContext);
const enterable = computeTabEnterable(statusInfo, periodEnterable);
const isSubmitted = statusInfo?.laeSbmsnSttsCd === "2";
const showSaveInToolbar = (enterable || isAdminInfo) && gugunOk;
const showEnterableToolbarButtons = enterable && gugunOk;
const showSubmittedInInputPeriodToolbar = isSubmitted && periodEnterable && gugunOk;
const showActionToolbarCard =
(hasRowData || treatAsSearched) &&
(showSaveInToolbar ||
(statusInfo?.laeSbmsnSttsCd === "2" &&
(isAdminInfo || (enterable && gugunOk))));
showEnterableToolbarButtons ||
showSubmittedInInputPeriodToolbar ||
(isSubmitted && isAdminInfo));
return { showSaveInToolbar, showEnterableToolbarButtons, showActionToolbarCard };
}
@@ -1887,9 +1890,11 @@ const SewagePipeInp = () => {
const rightRowData = rowDataMap.get(rightKey) ?? [];
const splitTopButton = info.right.topButton;
const showSplitTopButton =
activeTab === tabKey &&
Boolean(splitTopButton?.text) &&
(splitTopButton?.visible ?? true) &&
excelToolbarEnabledForTab;
tabRowDataExists(rowDataMap, tabKey) &&
gugunOkOrNotDoList;
const leftColumnInfo: ColumnInfo = {
columns: info.left.columns ?? [],
columnLayout: info.left.columnLayout ?? [],
@@ -748,16 +748,21 @@ const SeweragePenetrationRateInp = () => {
? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== 'ALL')
: true;
const inputPeriodCurrent = isInputPeriodCurrent(periodCheckList);
const isSubmitted = statusInfo?.laeSbmsnSttsCd === '2';
const baseShowSaveInToolbar = (isEnterable || isAdminInfo) && gugunOkOrNotDoList;
const baseShowEnterableToolbarButtons = isEnterable && gugunOkOrNotDoList;
const showSubmittedInInputPeriodToolbar =
isSubmitted && inputPeriodCurrent && gugunOkOrNotDoList;
const showSaveInToolbar = baseShowSaveInToolbar && toolbarGroupAllowed;
const showEnterableToolbarButtons = baseShowEnterableToolbarButtons && toolbarGroupAllowed;
/** 카드는 저장/제출줄/제출취소 중 하나라도 보일 때만 (관리자만으로 빈 카드 방지) */
const showActionToolbarCard =
rowData.length > 0 &&
toolbarGroupAllowed &&
(baseShowSaveInToolbar ||
(statusInfo?.laeSbmsnSttsCd === '2' && (isAdminInfo || (isEnterable && gugunOkOrNotDoList))));
baseShowEnterableToolbarButtons ||
showSubmittedInInputPeriodToolbar ||
(isSubmitted && isAdminInfo));
return (
<>
@@ -120,7 +120,7 @@ const PublicSewageFacilityRvw = () => {
const rawList = response.data.publicSewageList;
const convertedData: PublicSewageFacilityReviewItem[] = rawList.map((item) => ({
...item,
impdFcltPstnCn: item.fcltPstnCn ?? "",
fcltPstnCn: item.fcltPstnCn ?? "",
prxyBzentyNmCn: item.prxyBzentyNmCn ?? "",
prxyCtrtCn: item.prxyCtrtCn ?? "",
intgPrcsFcltCn: item.intgPrcsFcltCn ?? "",
@@ -15,7 +15,7 @@ export const PublicSewageFactColumn = {
{ name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '시설명(500m³/일 이상/미만)' }, editable: false, styleName: 'rg-cell-readonly' },
// 소재지
{ name: 'impdFcltPstnCn', fieldName: 'impdFcltPstnCn', header: { text: '소재지' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '소재지' }, editable: false, styleName: 'rg-cell-readonly' },
// 시설용량
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '시설용량(500m³/일 이상/미만)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-total' },
@@ -126,7 +126,7 @@ export const PublicSewageFactColumn = {
items: ['ctpvNm', 'sggNm'],
},
'swtrmtFcltNm',
'impdFcltPstnCn',
'fcltPstnCn',
'prcsFcltCpct',
{
name: 'facilityCapacityDetail',
@@ -49,7 +49,7 @@ export const rawPublicSewageFacilityInfoList = {
],
},
{ name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '시설명(500m³/일 이상/미만)' } },
{ name: 'impdFcltPstnCn', fieldName: 'impdFcltPstnCn', header: { text: '소재지' } },
{ name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '소재지' } },
/* 2. 시설용량 (㎥/일) */
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '시설용량(500m³/일 이상/미만)' }, footer: footerDecimal1ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' },
@@ -132,7 +132,7 @@ export const rawPublicSewageFacilityInfoList = {
header: { text: '행정구역' },
items: [{ column: 'ctpvNm', footerUserSpans: [{ colspan: 3 }, { colspan: 3 }] }, 'sggNm'],
},
'displayYear', 'swtrmtFcltNm', 'impdFcltPstnCn', 'prcsFcltCpct',
'displayYear', 'swtrmtFcltNm', 'fcltPstnCn', 'prcsFcltCpct',
{
name: 'groupB',
header: { text: '시설용량(㎥/일)' },
@@ -5,6 +5,8 @@ import {
type ApiResponse,
type cancelSubmitRequest,
type cancelSubmitResponse,
type CheckStatusCodeRequest,
type CheckStatusCodeResponse,
type getReviewOpinionDetailRequest,
type getReviewOpinionDetailResponse,
type ImrRequest,
@@ -321,6 +323,14 @@ export class StatisticsApiService implements StatisticsService {
);
return response.data;
}
public async CheckStatusCode(param: CheckStatusCodeRequest): Promise<CheckStatusCodeResponse> {
const response = await stClient.get<CheckStatusCodeResponse>(
`${ENDPOINT.ST_SERVICE.INPUTCOMMON}/status-code`,
{ params: param },
);
return response.data;
}
}
export const statisticsApi = new StatisticsApiService();
@@ -37,7 +37,7 @@ export interface PublicSewageFacilityItem {
fcltSn: number;
swtrmtFcltCd: string;
swtrmtFcltNm: string;
impdFcltPstnCn: string | null;
fcltPstnCn: string | null;
prcsFcltCpct: number;
prcsFcltPhysCpct: number;
prcsFcltBoilCpct: number;
@@ -475,6 +475,17 @@ export interface StatiscicsExceptionResetResponse {
message?: string;
}
export interface CheckStatusCodeRequest {
statsCd: string;
statsYr: string;
sewerBzmnCd: string;
}
export interface CheckStatusCodeResponse {
status: string;
data: string;
}
export interface StatisticsService {
getSidoList(params?: { instSeCd?: string }): Promise<ApiResponse<SearchCondition>>;
getSggList(sidoName: string, params?: { instSeCd?: string; gugunSkipYn?: "Y" | "N" }): Promise<ApiResponse<Sgg[]>>;
@@ -504,4 +515,5 @@ export interface StatisticsService {
getReviewerInfo(param: ReviewerInfoRequest): Promise<ReviewerInfoResponse>;
getStatiscicsManagementReviewHistory(param: StatiscicsManagementReviewHistoryRequest): Promise<StatiscicsManagementReviewHistoryResponse>;
StatiscicsExceptionReset(param: StatiscicsExceptionResetRequest): Promise<StatiscicsExceptionResetResponse>;
CheckStatusCode(param: CheckStatusCodeRequest): Promise<CheckStatusCodeResponse>;
}