Compare commits

...
4 Commits
19 changed files with 342 additions and 155 deletions
@@ -185,7 +185,9 @@ export default function DemandStatusPage() {
const userData = JSON.parse(user) as SessionUser;
const dmdInfoId = userData.dmdInfoId?.trim();
if (dmdInfoId) {
getSdmInfoStatusList(dmdInfoId);
void getSdmInfoStatusList(dmdInfoId).then((active) => {
if (!active) window.location.href = "/";
});
}
} catch (error) {
console.error("Error parsing user data:", error);
@@ -341,7 +343,9 @@ export default function DemandStatusPage() {
if (res) {
const user = JSON.parse(sessionStorage.getItem("user") || "{}");
if (user?.dmdInfoId) {
getSdmInfoStatusList(user.dmdInfoId);
void getSdmInfoStatusList(user.dmdInfoId).then((active) => {
if (!active) window.location.href = "/";
});
}
setIsEditMode(false);
}
+48 -38
View File
@@ -211,18 +211,32 @@ const selectMiddleRegionData: SelectDataType[] = [
* STORE - API
*/
/** API 선택 목록의 빈 값과 중복 값을 제거하고 전체 항목을 한 번만 추가한다. */
function normalizeSelectData<T extends SelectDataType>(items: T[], allItem: T): T[] {
const uniqueItems = new Map<string, T>();
items.forEach((item) => {
const val = String(item.val ?? '').trim();
if (!val || val === '0' || uniqueItems.has(val)) return;
uniqueItems.set(val, { ...item, val });
});
return [allItem, ...uniqueItems.values()];
}
// 시도 API 호출 함수 추가
const fetchSidoData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/city');
const data = res.data;
selectSidoData = [
{ text: "전체", val: '0'},
...data.map((item: sidoDataType) => ({
text: item.sido || '',
val: String(item.adstDstrCd) || ''
}))
];
selectSidoData = normalizeSelectData(
data.map((item: sidoDataType) => ({
text: item.sido || '',
val: String(item.adstDstrCd) || ''
})),
{ text: "전체", val: '0' }
);
return selectSidoData;
} catch (error) {
console.error('시도 데이터를 가져오는데 실패했습니다:', error);
@@ -235,14 +249,13 @@ const fetchSigunData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/sgg');
const data = res.data;
selectSigunData = [
{ text: "전체", val: '0' },
...data
.map((item: sigunDataType) => ({
text: item.gugun || '',
val: String(item.adstDstrCd) || ''
}))
];
selectSigunData = normalizeSelectData(
data.map((item: sigunDataType) => ({
text: item.gugun || '',
val: String(item.adstDstrCd) || ''
})),
{ text: "전체", val: '0' }
);
return selectSigunData;
} catch (error) {
console.error('시군구 데이터를 가져오는데 실패했습니다:', error);
@@ -255,13 +268,13 @@ const fetchRegionData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/sarea');
const data = res.data;
return [
{ text: "전체", val: '0'},
...data.map((item: regionDataType) => ({
return normalizeSelectData(
data.map((item: regionDataType) => ({
text: item.rvbsn || '',
val: String(item.rvbsnCd) || ''
}))
];
})),
{ text: "전체", val: '0' }
);
} catch (error) {
console.error('권역 데이터를 가져오는데 실패했습니다:', error);
return selectRegionData;
@@ -273,14 +286,13 @@ const fetchMiddleRegionData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/midArea');
const data = res.data;
return [
{ text: "전체", val: '0' },
...data
.map((item: middleRegionDataType) => ({
return normalizeSelectData(
data.map((item: middleRegionDataType) => ({
text: item.mdmbsn || '',
val: String(item.rvbsnCd) || ''
}))
];
})),
{ text: "전체", val: '0' }
);
} catch (error) {
console.error('중권역 데이터를 가져오는데 실패했습니다:', error);
return selectMiddleRegionData;
@@ -292,14 +304,13 @@ const fetchUnitWaterShedData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/largeArea');
const data = res.data;
return [
{ text: "전체", val: '0' },
...data
.map((item: unitWaterShedDataType) => ({
return normalizeSelectData(
data.map((item: unitWaterShedDataType) => ({
text: item.lgbsn || '',
val: String(item.rvbsnCd) || ''
}))
];
})),
{ text: "전체", val: '0' }
);
} catch (error) {
console.error('단위유역 데이터를 가져오는데 실패했습니다:', error);
return selectUnitWaterShedData;
@@ -311,15 +322,14 @@ const fetchEnvPartData = async (): Promise<envPartSelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/govArea');
const data = res.data;
selectEnvPartData = [
{ text: "전체", val: '0', adstDliStrCd: '' },
...data
.map((item: envPartDataType) => ({
selectEnvPartData = normalizeSelectData(
data.map((item: envPartDataType) => ({
text: item.lclgvrnmntNm || '',
val: String(item.lclgvrnmntCd) || '',
adstDliStrCd: item.adstDliStrCd || ''
}))
];
})),
{ text: "전체", val: '0', adstDliStrCd: '' }
);
return selectEnvPartData;
} catch (error) {
console.error('환경청 데이터를 가져오는데 실패했습니다:', error);
+53 -24
View File
@@ -134,22 +134,23 @@ export type SupplyCandidate = {
export type DemandMatchings = {
splyFcltFk: string | '';
splyFcltFlg: string | '';
pbnstRuseFcltBizFk: number | '';
splyBgngDay: string | '';
splyEndDay: string | '';
splyCpct: number | '';
splyFmYn: string | '';
useUsgCd: string | '';
dmdInfoSn: number | '';
splyCndtList: string | '';
mtchnCmptnYmd: string | '';
rvwCmptnYmd: string | '';
regDt: string | '';
rgtr: string | '';
mdfcnDt: string | '';
mdfr: string | '';
splyFcltFk: string | ''; // 공급시설 외래키
splyFcltFlg: string | ''; // 공급시설 진행상태
pbnstRuseFcltBizFk: number | ''; // 공공하수 재이용시설 사업 외래키
splyBgngDay: string | ''; // 공급시작일
splyEndDay: string | ''; // 공급종료일
splyCpct: number | ''; // 계약 공급량
splyFmYn: string | ''; // 공급 유량계 유무
useUsgCd: string | ''; // 사용용도 코드
dmdInfoSn: number | ''; // 수요정보 일련번호
ruseUtztnFcltNm?: string | ''; // 재이용시설명
splyCndtList: string | ''; // 공급 후보지 목록
mtchnCmptnYmd: string | ''; // 매칭 완료일자
rvwCmptnYmd: string | ''; // 검토 완료일자
regDt: string | ''; // 등록일시
rgtr: string | ''; // 등록자
mdfcnDt: string | ''; // 수정일시
mdfr: string | ''; // 수정자
}
// 유사사례
@@ -503,6 +504,14 @@ const postSdmData = async (data: DemandCompType) => {
// 수요정보 등록
const postSdmInfoData = async (data: SdmInfoFormData): Promise<boolean> => {
try {
const activeDemand = data.dmdInfoId
? await getSdmInfoStatusList(data.dmdInfoId)
: null;
if (!activeDemand?.dmdInfoId) {
sessionStorage.removeItem("user");
alert('사용 중지되었거나 등록되지 않은 수요처입니다. 다시 로그인해 주세요.');
return false;
}
await postFetch("/reuseFc/request/demandInfo", data);
alert('수요 정보 등록에 성공하였습니다.');
return true;
@@ -516,6 +525,14 @@ const postSdmInfoData = async (data: SdmInfoFormData): Promise<boolean> => {
// 수요정보 수정
const fetchUpdateSdmInfoData = async (data: SdmInfoFormData): Promise<boolean> => {
try {
const activeDemand = data.dmdInfoId
? await getSdmInfoStatusList(data.dmdInfoId)
: null;
if (!activeDemand?.dmdInfoId) {
sessionStorage.removeItem("user");
alert('사용 중지되었거나 등록되지 않은 수요처입니다. 다시 로그인해 주세요.');
return false;
}
await postFetch("/reuseFc/request/updateDemandInfo", data);
alert('수요 정보 수정에 성공하였습니다.');
return true;
@@ -584,14 +601,19 @@ const getDemandCompList = async (searchText: string, pageCnt: number) => {
}
}
const getSdmInfoStatusList = async (dmdInfoId: string) => {
type DemandStatusResponse = DemandCompType & {
demandInfo?: DemandCompBizType[];
demandMatchings?: DemandMatchings[];
};
const getSdmInfoStatusList = async (dmdInfoId: string): Promise<DemandStatusResponse | null> => {
try {
const url = '/reuseFc/request/' + dmdInfoId + '?infoListYn=Y';
const res = await getFetch(fetchType.WR, url);
return res?.data || [];
return res?.data ?? null;
} catch (error) {
console.error(error + ' 수요정보 신청현황을 불러오지 못했습니다.');
return [];
return null;
}
}
@@ -620,7 +642,7 @@ export type SdmActions = {
updateSdmInfoData: (data: SdmInfoFormData) => Promise<boolean>;
getDemandCompList: (searchText: string, pageCnt: number) => Promise<void>;
actionLogin: (data: DemandCompType) => Promise<boolean>;
getSdmInfoStatusList: (dmdInfoId: string) => Promise<void>;
getSdmInfoStatusList: (dmdInfoId: string) => Promise<boolean>;
getSimilarCaseList: (part: string, value: number | string) => Promise<void>;
actionLogout: () => Promise<void>;
@@ -736,19 +758,25 @@ export const SdmReducer = (set: any): SdmActions => {
getSdmInfoStatusList: async (dmdInfoId: string) => {
const res = await getSdmInfoStatusList(dmdInfoId);
if (res.length === 0) {
if (!res?.dmdInfoId) {
sessionStorage.removeItem("user");
set((state: SdmType) => ({
Sdm: {
...state.Sdm,
sdmInfoStatusList: [],
successLogin: false,
loginUser: null,
}
}))
return;
toast.error("사용 중지되었거나 등록되지 않은 수요처입니다.");
return false;
}
//const sdmInfoStatusList : DemandCompBizType[] = [];
const sdmInfoStatusList: DemandCompBizType[] = res.demandInfo.map((el: DemandCompBizType, index: number) => {
const matchingInfo = res.demandMatchings.find((matching: DemandMatchings) => matching.dmdInfoSn === el.dmdInfoSn);
const demandInfo = Array.isArray(res.demandInfo) ? res.demandInfo : [];
const demandMatchings = Array.isArray(res.demandMatchings) ? res.demandMatchings : [];
const sdmInfoStatusList: DemandCompBizType[] = demandInfo.map((el: DemandCompBizType, index: number) => {
const matchingInfo = demandMatchings.find((matching: DemandMatchings) => matching.dmdInfoSn === el.dmdInfoSn);
const status = matchingInfo?.splyFcltFlg?.trim() === 'Sub' ? '검토' : matchingInfo?.splyFcltFlg?.trim() === 'matched' ? '매칭완료' : '신청';
const ruseUtztnFcltNm = matchingInfo?.ruseUtztnFcltNm ? matchingInfo.ruseUtztnFcltNm : '';
@@ -781,6 +809,7 @@ export const SdmReducer = (set: any): SdmActions => {
sdmInfoStatusList: sdmInfoStatusList,
}
}))
return true;
},
actionLogin: async (data: DemandCompType): Promise<boolean> => {
+4 -2
View File
@@ -8,8 +8,10 @@ NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
NEXT_PUBLIC_SUB_DOMAIN = http://localhost:13000
#NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://localhost:3001/auth/login
SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service/api/v1
FO_SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service/api/v1
WR_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1
# FO_SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service/api/v1
FO_SERVER_API_URL = http://localhost:3000/operation-service/api/v1
# WR_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1
WR_SERVER_API_URL = http://localhost:3000/reuse-service/api/v1
CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
+34 -18
View File
@@ -237,6 +237,26 @@ export const COD_YEAR = 2021;
* STORE - API
*/
/** API 선택 목록을 값 기준으로 정규화하고 프런트 표준 전체 항목을 한 번만 추가한다. */
const normalizeSelectData = (items: SelectDataType[]): SelectDataType[] => {
const uniqueItems = new Map<string, SelectDataType>();
items.forEach((item) => {
const val = String(item.val ?? '').trim();
if (!val || val === '0' || uniqueItems.has(val)) return;
uniqueItems.set(val, {
...item,
val,
});
});
return [
{ text: '전체', val: '0' },
...uniqueItems.values(),
];
};
// 처리시설 계획정보 호출 (알에스원)
export const planInfoUrl = '/dc-service/api/v1/nowDmgMtncGnsvy';
@@ -263,13 +283,12 @@ const fetchSidoData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/city');
const data = res.data;
selectSidoData = [
{ text: "전체", val: '0' },
...data.map((item: sidoDataType) => ({
selectSidoData = normalizeSelectData(
data.map((item: sidoDataType) => ({
text: item.sido || '',
val: String(item.adstDstrCd) || ''
}))
];
})),
);
return selectSidoData;
} catch (error) {
console.error('시도 데이터를 가져오는데 실패했습니다:', error);
@@ -282,14 +301,12 @@ const fetchSigunData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/sgg');
const data = res.data;
selectSigunData = [
{ text: "전체", val: '0' },
...data
.map((item: sigunDataType) => ({
text: item.gugun || '',
val: String(item.adstDstrCd) || ''
}))
];
selectSigunData = normalizeSelectData(
data.map((item: sigunDataType) => ({
text: item.gugun || '',
val: String(item.adstDstrCd) || ''
})),
);
return selectSigunData;
} catch (error) {
console.error('시군구 데이터를 가져오는데 실패했습니다:', error);
@@ -302,13 +319,12 @@ const fetchRegionData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/sarea');
const data = res.data;
return [
{ text: "전체", val: '0' },
...data.map((item: regionDataType) => ({
return normalizeSelectData(
data.map((item: regionDataType) => ({
text: item.rvbsn || '',
val: String(item.rvbsnCd) || ''
}))
];
})),
);
} catch (error) {
console.error('권역 데이터를 가져오는데 실패했습니다:', error);
return selectRegionData;
+6 -6
View File
@@ -7,16 +7,16 @@ NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_PROXY_HOST = http://localhost:3001
NEXT_PUBLIC_SUB_DOMAIN = http://localhost:13000
SERVER_API_URL = http://dev.onasys.co.kr:8000
FO_SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service/api/v1
# FO_SERVER_API_URL = http://localhost:8883/operation-service/api/v1
# FO_SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service/api/v1
FO_SERVER_API_URL = http://localhost:8883/operation-service/api/v1
CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
WR_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1
# WR_SERVER_API_URL = http://localhost:8883/reuse-service/api/v1
SEWERAGE_API_URL = http://con.mind-one.co.kr:11083/sewerage-service # 개발 서버 API
# SEWERAGE_API_URL=http://localhost:8883/sewerage-service
# WR_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1
WR_SERVER_API_URL = http://localhost:8883/reuse-service/api/v1
# SEWERAGE_API_URL = http://con.mind-one.co.kr:11083/sewerage-service # 개발 서버 API
SEWERAGE_API_URL=http://localhost:8883/sewerage-service
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr
NEXT_PUBLIC_NGII_PARAM = 04trYP9_xwLAfALjwZ-B8g&service=WMTS&request=GetTile&version=1.0.0&format=image/png&layer=korean_map&style=korean&tilematrixset=korean&tilematrix={z}&tilecol={x}&tilerow={y}
# NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login
@@ -48,9 +48,10 @@ export default function CoSttn() {
coSttnTxtSearch,
demandCompList,
coSttnPwInit,
coSttnDelete,
getMyUUID
} = useWrStore((state) => state);
const { regAuthrtYn, mdfcnAuthrtYn } = auth;
const { regAuthrtYn, mdfcnAuthrtYn, delAuthrtYn } = auth;
const alert = useAlertStore((state) => state.setMessage);
const { confirm } = useConfirm();
const [bdstTable, setBdstTable] = useState<DemandCompType[]>();
@@ -69,6 +70,7 @@ export default function CoSttn() {
const [seartchTxt, setSearchTxt] = useState<string>();
const [updateData, setUpdateData] = useState<DemandCompType>();
const [isResetSelection, setIsResetSelection] = useState<boolean>(false);
const [isDeleting, setIsDeleting] = useState(false);
const sanitizeDemandForPopup = useCallback((data?: DemandCompType) => {
if (!data) {
@@ -242,6 +244,52 @@ export default function CoSttn() {
});
};
/**
* 선택한 수요처를 논리삭제하고 현재 검색조건으로 목록을 다시 조회한다.
* @param data 삭제 대상 수요처
*/
const handleDeleteDemand = async (data: DemandCompType) => {
if (!data.dmdCoSn) {
alert("수요처 식별값이 없어 삭제할 수 없습니다.");
return;
}
const confirmed = await confirm(
`${data.dmdCoNm || "선택한 수요처"}를 삭제하시겠습니까? 삭제 후 목록과 로그인, 지도에서 조회되지 않습니다.`,
);
if (!confirmed) return;
const modifier = data.mdfr || await getMyUUID();
if (typeof modifier !== "string" || !modifier) {
alert("수정자 정보를 확인할 수 없어 삭제할 수 없습니다.");
return;
}
setIsDeleting(true);
try {
const deleted = await coSttnDelete(Number(data.dmdCoSn), modifier);
if (!deleted) {
alert("수요처 삭제에 실패하였습니다.");
return;
}
alert("수요처가 삭제되었습니다.");
setIsDmdModal({ isMoal: false, data: {} as DemandCompType });
setUpdateData(undefined);
setIsResetSelection(true);
const con =
objSearch.sgg && objSearch.sgg !== "0" ? objSearch.sgg : objSearch.city;
if (seartchTxt && seartchTxt.trim().length > 0) {
await coSttnTxtSearch(String(pageNation), "13", String(con), seartchTxt);
} else {
await coSttnSearch(String(pageNation), "13", String(con));
}
} finally {
setIsDeleting(false);
}
};
return (
<>
<ContentsDiv>
@@ -388,6 +436,8 @@ export default function CoSttn() {
getDemandCompList(searchText, pageCnt);
}}
rtnInputData={handleRtnInputData}
onDelete={handleDeleteDemand}
deleteDisabled={delAuthrtYn === "N" || isDeleting}
bodyClassNm={"w-full mx-auto bg-background rounded-lg shadow-md p-6"}
updateList={updateData}
openNetwork={true}
@@ -60,7 +60,7 @@ export default function Page() {
isRainSttn, // 빗물이용시설 상세/선택 상태 초기화
rswtChkgdnInit, // 중수도시설 지도점검 목록 초기 조회
rswtChkNmSearch, // 중수도시설명으로 목록 조회
rswtChkPolInit, // 중수도 시설구분 변경 시 조회조건 초기화
rswtChkPolInit, // 중수도 시설구분 조회 시 상세 조회조건 초기화
rswtChkDetilsUpdate, // 중수도시설 지도점검 상세 수정 저장
rswtChkDelete, // 중수도시설 지도점검 선택 건 삭제
rswtrChkExcelDownload, // 중수도시설 지도점검 엑셀 양식/데이터 조회
@@ -80,6 +80,8 @@ export default function Page() {
const [rswtPageNation, setRswtPageNation] = useState(1);
const [fcltNm, setFcltNm] = useState<string>("");
const [selFcltDiv, setSelFcltDiv] = useState("RNB");
// 검색 조건에서 선택한 시설구분과 실제 조회·표시 중인 시설구분을 분리한다.
const [appliedFcltDiv, setAppliedFcltDiv] = useState("RNB");
const [isModal, setIsModal] = useState({
open: false,
data: {},
@@ -107,7 +109,9 @@ export default function Page() {
const cellClick = () => {
if (searchYear)
if (searchYear) {
if (selFcltDiv === "RST") rswtChkPolInit();
setAppliedFcltDiv(selFcltDiv);
if (selFcltDiv === "RNB") { // 빗물
if (fcltNm) {
rainChkNmSearch( rainPageNation, rainPageSize, searchYear, laeSeCd, fcltNm );
@@ -123,6 +127,7 @@ export default function Page() {
rswtChkgdnInit(rswtPageNation, rswtPageSize, searchYear, laeSeCd);
}
}
}
};
const rainPageChange = (pageNo: number) => {
@@ -176,13 +181,13 @@ export default function Page() {
}, [rainChkgdn.excelList]);
const excelClick = useCallback(() => {
if (selFcltDiv === "RNB") {
if (appliedFcltDiv === "RNB") {
rainChkExcelDownload(code.cityCd, code.sggCd,laeSeCd);
} else {
rswtrChkExcelDownload(code.cityCd, code.sggCd,laeSeCd);
}
},
[ rainChkExcelDownload, rswtrChkExcelDownload, code.cityCd, code.sggCd, selFcltDiv, laeSeCd ]
[ rainChkExcelDownload, rswtrChkExcelDownload, code.cityCd, code.sggCd, appliedFcltDiv, laeSeCd ]
);
const handleClick = useCallback(
@@ -274,7 +279,6 @@ export default function Page() {
selectClass={""}
selectArray={rainChkgdn.fcltDiv}
onSelectValue={(val: string | number) => {
if (val === "RST") rswtChkPolInit();
setSelFcltDiv(String(val));
}}
value={selFcltDiv ?? rainChkgdn.fcltDiv[0]?.val}
@@ -306,7 +310,7 @@ export default function Page() {
<Button
className={btnClass}
onClick={() => {
void clickDelete(deleteIdx, selFcltDiv);
void clickDelete(deleteIdx, appliedFcltDiv);
}}
disabled={delAuthrtYn === 'N'}
>
@@ -319,7 +323,7 @@ export default function Page() {
<Button
className={btnClass}
onClick={() => {
void chkUpdateTable(selFcltDiv);
void chkUpdateTable(appliedFcltDiv);
}}
disabled={mdfcnAuthrtYn === 'N'}
>
@@ -355,7 +359,7 @@ export default function Page() {
<div className="grid grid-cols-6 gap-4 ">
<div className="col-span-6">
<ListDiv>
{selFcltDiv === "RNB" ? (
{appliedFcltDiv === "RNB" ? (
<RainChkgdTable
deleteData={setDeleteIdx}
detailUp={setUpdateRainDetail}
@@ -392,7 +396,7 @@ export default function Page() {
bodyClass={"w-[80vw] max-w-none"}
body={
<ChkgdModal
type={selFcltDiv}
type={appliedFcltDiv}
obj={isModal.data}
setIsModal={(open) => setIsModal((prev) => ({ ...prev, open }))}
readonly={mdfcnAuthrtYn === 'N' ? false : true}
@@ -405,7 +409,7 @@ export default function Page() {
bodyClass={"w-[30vw] max-w-none"}
body={
<ChkgdExcel
type={selFcltDiv}
type={appliedFcltDiv}
setIsRegExcel={setIsRegExcel}
setYearChange={(year) => {
setSearchYear(year);
@@ -57,6 +57,8 @@ export default function Page() {
const fixedSigunValue =
!isAllAreaUser && laeSeCd?.length === 5 ? laeSeCd : "0";
const [selFcltDiv, setSelFcltDiv] = useState("RNB");
// 검색 조건에서 선택한 시설구분과 실제 조회·표시 중인 시설구분을 분리한다.
const [appliedFcltDiv, setAppliedFcltDiv] = useState("RNB");
const [rainPageNation, setRainPageNation] = useState(1);
const [rswtPageNation, setRswtPageNation] = useState(1);
const [fcltNm, setFcltNm] = useState<string>("");
@@ -118,6 +120,8 @@ export default function Page() {
const conValue = getFixedConValue();
if (searchYear) {
if (selFcltDiv === "RST") rswtChkPolInit();
setAppliedFcltDiv(selFcltDiv);
if (selFcltDiv === "RNB") {
if (fcltNm) {
rainChkSttnNmSearch(
@@ -248,7 +252,7 @@ export default function Page() {
const { t } = useTranslation("base");
const alert = useAlertStore((state) => state.setMessage);
const excelDown = () => {
if (rainChkSttn.sttnExcelList && selFcltDiv === "RNB") {
if (rainChkSttn.sttnExcelList && appliedFcltDiv === "RNB") {
if (
!rainChkSttn.sttnExcelList ||
rainChkSttn.sttnExcelList.length === 0
@@ -270,7 +274,7 @@ export default function Page() {
"sample_rainChkSttn.xlsx",
sheetArray
);
} else if (rswtChkSttn.sttnExcelList && selFcltDiv === "RST") {
} else if (rswtChkSttn.sttnExcelList && appliedFcltDiv === "RST") {
if (
!rswtChkSttn.sttnExcelList ||
rswtChkSttn.sttnExcelList.length === 0
@@ -361,7 +365,6 @@ export default function Page() {
selectClass={""}
selectArray={rainChkSttn.fcltDiv}
onSelectValue={(val: string | number) => {
if (val === "RST") rswtChkPolInit();
setSelFcltDiv(String(val));
}}
value={selFcltDiv ?? rainChkSttn.fcltDiv[0]?.val}
@@ -410,7 +413,7 @@ export default function Page() {
<ListDiv>
<div className="h-full w-full overflow-x-scroll min-[1900px]:overflow-x-hidden">
<div className="w-[160%] min-[1900px]:w-[100%]">
{selFcltDiv === "RNB" ? (
{appliedFcltDiv === "RNB" ? (
<RainChkgdTable
deleteData={() => {}}
detailUp={() => {}}
@@ -447,7 +450,7 @@ export default function Page() {
body={
<ChkgdModal
readonly={true}
type={selFcltDiv}
type={appliedFcltDiv}
obj={isModal.data}
setIsModal={() => {}}
/>
+5 -5
View File
@@ -1,5 +1,6 @@
export const dynamic = "force-dynamic";
import { SITE_ID } from "@/src/shared/config";
import { normalizeSelectData } from "@/app/store/common/code/normalizeSelectData";
import { NextResponse } from "next/server";
export const revalidate = 86400; //60 * 60 * 24;
@@ -143,13 +144,12 @@ export async function GET(request: Request) {
val: String(item.adstDstrCd) || "",
})) as SelectDataType[]),
],
cityCd: [
{ val: "0", text: "전체" },
...((city ?? []).map((item: any) => ({
cityCd: normalizeSelectData(
(city ?? []).map((item: any) => ({
val: item.adstDstrCd,
text: item.sido,
})) as SelectDataType[]),
],
})),
),
rvbsnCd: [
{ val: "0", text: "전체" },
...((rvbsn ?? []).map((item: any) => ({
+7 -5
View File
@@ -11,6 +11,7 @@ import {
ruseSeCd,
yn,
} from "./basicCode";
import { normalizeSelectData } from "./normalizeSelectData";
export const btnClass = "p-4 m-2 rounded-sm text-lg font-medium text-primary-foreground ";
const laeSecd = "41";
@@ -133,11 +134,12 @@ export const getSggCd = async (set: any) => {
};
export const getCityCd = async (set: any) => {
const city = await getFetch("common", "/addr/city");
const cityCd = city.map((item: any) => ({
val: item.adstDstrCd,
text: item.sido,
}));
cityCd.unshift({ val: "0", text: "전체" });
const cityCd = normalizeSelectData(
city.map((item: any) => ({
val: item.adstDstrCd,
text: item.sido,
})),
);
set((state: comCodeState) => ({
code: {
...state.code,
@@ -0,0 +1,28 @@
type SelectDataInput = {
text?: string | null;
val?: string | number | null;
};
export type NormalizedSelectData = {
text: string;
val: string;
};
/** 선택 목록을 값 기준으로 중복 제거하고 프런트 표준 전체 항목을 한 번만 추가한다. */
export const normalizeSelectData = (
items: readonly SelectDataInput[],
): NormalizedSelectData[] => {
const uniqueItems = new Map<string, NormalizedSelectData>();
items.forEach((item) => {
const val = String(item.val ?? "").trim();
if (!val || val === "0" || uniqueItems.has(val)) return;
uniqueItems.set(val, {
text: String(item.text ?? ""),
val,
});
});
return [{ text: "전체", val: "0" }, ...uniqueItems.values()];
};
+20 -2
View File
@@ -1,5 +1,5 @@
import type { CommonTableProps } from "@/app/store/common/type/header";
import { fetchType, getFetchWithCount, putFectch } from "@/components/lib/api";
import { deleteFectch, fetchType, getFetchWithCount, putFectch } from "@/components/lib/api";
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
import { sha512 } from "js-sha512";
import { coSttHeadListExport, coSttNodeListExport } from "./headList";
@@ -26,6 +26,7 @@ type coSttnActions = {
coSttnSearch: (pageNo: string, pageCnt: string, con: string) => void;
coSttnTxtSearch: (pageNo: string, pageCnt: string, con: string, txt: string) => void;
coSttnPwInit: (dmdCoSn: number, dmdInfoId: string) => Promise<string>;
coSttnDelete: (dmdCoSn: number, mdfr: string) => Promise<boolean>;
};
export type coSttnType = coSttnState & coSttnActions;
@@ -148,6 +149,23 @@ export const coSttnActionsExport: (set: any) => coSttnActions = (
}
});
}
}
},
// 수요처를 물리삭제하지 않고 미사용 상태로 변경한다.
coSttnDelete: async (dmdCoSn: number, mdfr: string) => {
try {
const responseText = await deleteFectch(
`/reuseFc/request/${dmdCoSn}`,
{ mdfr },
);
const response = JSON.parse(responseText) as {
status?: number;
data?: number;
};
return response.status === 200 && Number(response.data) > 0;
} catch (error) {
console.error("수요처 삭제 중 오류가 발생했습니다.", error);
return false;
}
},
}
}
@@ -150,7 +150,7 @@ export const rswtChkHeadListExport: TableUpperProps[] = [
{ id: "splyUswtrUtztnQnt", title: "연간대체수이용량(m³)" ,type: "number", widthPercent: 5 },
{ id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)",type: "number", widthPercent: 5 },
{ id: "grssClfgUswtr", title: "총대장균군수(개/100㎖)", upName: "useQuilty", upSequnce: 1 ,type: "number", widthPercent: 6 },
{ id: "combResdch", title: "결합잔류염소(mh/L)", upName: "useQuilty", upSequnce: 2 ,type: "number", widthPercent: 6 },
{ id: "combResdch", title: "결합잔류염소(mg/L)", upName: "useQuilty", upSequnce: 2 ,type: "number", widthPercent: 6 },
{ id: "trbt", title: "탁도(NTU)", upName: "useQuilty", upSequnce: 3 ,type: "number", widthPercent: 4 },
{ id: "bod", title: "BOD\n(mg/L)", upName: "useQuilty", upSequnce: 4 ,type: "number", widthPercent: 4 },
{ id: "smll", title: "냄새", upName: "useQuilty", upSequnce: 5 ,type: "text", widthPercent: 4 },
@@ -15,7 +15,7 @@ export const rswtChkSttnHeadListExport: TableUpperProps[] = [
{ id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)", type: "number", widthPercent: 5 },
{ id: "grssClfgUswtr", title: "총대장균군수(개/100㎖)", upName: "useQuilty", upSequnce: 1, type: "number", widthPercent: 6 },
{ id: "combResdch", title: "결합잔류염소(mh/L)", upName: "useQuilty", upSequnce: 2, type: "number", widthPercent: 6 },
{ id: "combResdch", title: "결합잔류염소(mg/L)", upName: "useQuilty", upSequnce: 2, type: "number", widthPercent: 6 },
{ id: "trbt", title: "탁도(NTU)", upName: "useQuilty", upSequnce: 3, type: "number", widthPercent: 4 },
{ id: "bod", title: "BOD (mg/L)", upName: "useQuilty", upSequnce: 4, type: "number", widthPercent: 4 },
{ id: "smll", title: "냄새", upName: "useQuilty", upSequnce: 5, type: "text", widthPercent: 4 },
@@ -297,25 +297,6 @@ export default function ChkgdModal({
</div>
<ScrollArea className="h-[60vh]">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">
{`연간대체수이용량`}
</h3>
</div>
<GuideDetailsTable
chgRes={guidData || []}
headList={headList.splHeadList}
upheadList={
type === "RNB"
? rainChkgdn.rainSplHeadNm
: rswtChkgdn.rswtSplHeadNm
}
type={""}
setChGuidData={(data) => {
setChGuidData(data);
}}
readonly={readonly}
/>
<div className="card-header">
<h3 className="text-2xl font-medium text-label">
{type === "RNB" ? `연간빗물 이용량` : "연간중수도 이용량"}
@@ -335,6 +316,25 @@ export default function ChkgdModal({
}}
readonly={readonly}
/>
<div className="card-header">
<h3 className="text-2xl font-medium text-label">
{`연간대체수이용량`}
</h3>
</div>
<GuideDetailsTable
chgRes={guidData || []}
headList={headList.splHeadList}
upheadList={
type === "RNB"
? rainChkgdn.rainSplHeadNm
: rswtChkgdn.rswtSplHeadNm
}
type={""}
setChGuidData={(data) => {
setChGuidData(data);
}}
readonly={readonly}
/>
{type !== "RNB" && polutionData !== undefined ? (
<div key={"polutionData"}>
Binary file not shown.
@@ -188,8 +188,9 @@ export const sigunguCenter: Record<string, [number, number]> = {
"11530": [943255.85156361, 1944224.7496726601], // 구로구
"41310": [967836.346287095, 1955821.725581055], // 구리시
"47190": [1076297.5737083051, 1801900.2253747801], // 구미시
"45130": [904674.42544751, 1767715.183037305], // 군산시
"27720": [1103849.611897755, 1797204.1944948], // 군위군
"45130": [931167.80, 1774809.14], // 군산시 (육지(시청·군청) 기준, 도서지역 제외)
"27720": [1096402.36, 1805597.71], // 군위군 (군청 기준)
"47720": [1096402.36, 1805597.71], // 군위군 경북 구코드 (군청 기준)
"41410": [948411.5776788951, 1927135.757437655], // 군포시
"44710": [998110.87580872, 1792026.97835628], // 금산군
"26410": [1144696.85330015, 1697028.92074261], // 금정구
@@ -214,7 +215,8 @@ export const sigunguCenter: Record<string, [number, number]> = {
"27290": [1092874.923985485, 1758802.075636735], // 달서구
"27710": [1092556.27778919, 1753550.63671916], // 달성군
"46710": [953252.8048743049, 1699620.87304459], // 담양군
"44270": [922626.193186825, 1879100.4990195702], // 당진시
"44270": [923895.22, 1877173.14], // 당진시 (육지(시청·군청) 기준, 도서지역 제외)
"44830": [923895.22, 1877173.14], // 당진시 신코드 (육지(시청·군청) 기준, 도서지역 제외)
"30230": [995239.980794815, 1823508.474804295], // 대덕구
"11320": [958705.07469343, 1963040.273132075], // 도봉구
"26170": [1140792.941703185, 1682445.908553165], // 동구
@@ -235,7 +237,7 @@ export const sigunguCenter: Record<string, [number, number]> = {
"47280": [1055620.11152262, 1855147.380883595], // 문경시
"28170": [926226.3594373399, 1940093.5607894901], // 미추홀구
"48270": [1117632.749546145, 1722736.830743895], // 밀양시
"44180": [896412.984839845, 1817364.412693855], // 보령시
"44180": [920366.76, 1815478.75], // 보령시 (육지(시청·군청) 기준, 도서지역 제외)
"46780": [974939.082074685, 1646364.3864357], // 보성군
"43720": [1018843.696119875, 1832681.50774474], // 보은군
"47920": [1125627.975921425, 1881676.019098885], // 봉화군
@@ -284,7 +286,8 @@ export const sigunguCenter: Record<string, [number, number]> = {
"45770": [962532.77503817, 1714757.02288094], // 순창군
"46150": [989441.17839757, 1668074.09581569], // 순천시
"41390": [934025.6249972349, 1932609.450570995], // 시흥시
"46910": [837068.838454425, 1628081.7615658198], // 신안군
"46910": [894983.16, 1649365.33], // 신안군 (군청 압해도 기준, 목포 인접)
"12870": [894983.16, 1649365.33], // 신안군 전남광주통합특별시 코드 (군청 압해도 기준, 목포 인접)
"44200": [953095.1457245699, 1866467.6723041101], // 아산시
"47170": [1108716.740335165, 1841036.9856003649], // 안동시
"41273": [921146.8917017151, 1914174.551465955], // 안산시 단원구
@@ -298,7 +301,8 @@ export const sigunguCenter: Record<string, [number, number]> = {
"41710": [957059.65493005, 1978556.8707086649], // 양주시
"11470": [943115.85871501, 1947726.50098438], // 양천구
"41830": [1006799.81121338, 1946640.3454016], // 양평군
"46130": [1005710.6789662449, 1606473.622557845], // 여수시
"46130": [1014844.04, 1640669.82], // 여수시 (육지(시청·군청) 기준, 도서지역 제외)
"12130": [1014844.04, 1640669.82], // 여수시 전남광주통합특별시 코드 (육지(시청·군청) 기준, 도서지역 제외)
"41730": [1007389.488139985, 1920986.5590416798], // 여주시
"28185": [924301.1973107599, 1932716.5874987599], // 연수구
"26470": [1143967.4104618952, 1688206.29479672], // 연제구
@@ -318,13 +322,14 @@ export const sigunguCenter: Record<string, [number, number]> = {
"41370": [959957.253397675, 1907245.6871241601], // 오산시
"43730": [1016925.1630849349, 1812306.15335931], // 옥천군
"28720": [831017.06725137, 1941763.7671776651], // 옹진군
"46890": [934681.072970785, 1580260.96434381], // 완도군
"46890": [931461.10, 1591076.05], // 완도군 (육지(시청·군청) 기준, 도서지역 제외)
"12850": [931461.10, 1591076.05], // 완도군 전남광주통합특별시 코드 (육지(시청·군청) 기준, 도서지역 제외)
"45710": [971714.974980355, 1764297.860421155], // 완주군
"11170": [954152.265892775, 1948105.11473188], // 용산구
"41463": [966362.167328945, 1919086.059435395], // 용인시 기흥구
"41465": [962751.309054115, 1926067.760215145], // 용인시 수지구
"41461": [979413.39966679, 1913728.657159615], // 용인시 처인구
"47940": [1339459.86503077, 1939972.251018405], // 울릉군
"47940": [1297396.76, 1949622.47], // 울릉군 (울릉도 본섬 중심, 독도 제외)
"31710": [1152103.383022245, 1727062.6953479801], // 울주군
"47930": [1158508.6557213152, 1879083.142978875], // 울진군
"42130": [1042675.604280725, 1925192.470561375], // 원주시
@@ -382,7 +387,7 @@ export const sigunguCenter: Record<string, [number, number]> = {
"43130": [1034982.8699803599, 1889790.67989893], // 충주시
"47850": [1086736.697665845, 1779277.075473485], // 칠곡군
"42190": [1131634.14282853, 1912631.8063723398], // 태백시
"44825": [864678.84145598, 1854929.12571281], // 태안군
"44825": [892666.48, 1861510.33], // 태안군 (육지(시청·군청) 기준, 도서지역 제외)
"48220": [1085316.421521755, 1640596.827168765], // 통영시
"41480": [941812.744809255, 1983528.9263305948], // 파주시
"42760": [1088652.567431635, 1949959.4658569149], // 평창군
@@ -15,6 +15,8 @@ type thisProps = {
dataList: DemandCompType[];
rtnValue: (searchText: string, pageCnt: number) => void;
rtnInputData: (data: DemandCompType) => void;
onDelete?: (data: DemandCompType) => void | Promise<void>;
deleteDisabled?: boolean;
bodyClassNm?: string;
updateList?: DemandCompType;
readOnly?: boolean;
@@ -122,6 +124,8 @@ export default function SdmInsertForm({
dataList,
rtnValue,
rtnInputData,
onDelete,
deleteDisabled,
bodyClassNm,
updateList,
readOnly,
@@ -227,16 +231,28 @@ export default function SdmInsertForm({
readOnly={readOnly}
openNetwork={openNetwork}
/>
<div className="flex justify-center pt-4">
<div className="flex justify-center gap-3 pt-4">
{readOnly == true ? (
<></>
) : (
<Button
type="submit"
className="hover:bg-primary/90 w-32 rounded-md bg-primary px-4 py-2 font-semibold text-primary-foreground transition-colors duration-200"
>
{updateList ? "수정" : "등록"}
</Button>
<>
{updateList && onDelete ? (
<Button
type="button"
className="w-32 rounded-md bg-red-600 px-4 py-2 font-semibold text-white transition-colors duration-200 hover:bg-red-700"
disabled={deleteDisabled}
onClick={() => onDelete(updateList)}
>
삭제
</Button>
) : null}
<Button
type="submit"
className="hover:bg-primary/90 w-32 rounded-md bg-primary px-4 py-2 font-semibold text-primary-foreground transition-colors duration-200"
>
{updateList ? "수정" : "등록"}
</Button>
</>
)}
</div>
</form>