Merge branch 'iipbs/sn/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/sn/dev

This commit is contained in:
yschoi
2025-07-08 13:52:20 +09:00
214 changed files with 11333 additions and 3647 deletions
@@ -0,0 +1,2 @@
import { LoginlogPage } from "@/pages/loginlog";
export default LoginlogPage;
+1
View File
@@ -0,0 +1 @@
export { LoginlogPage } from "./ui/loginlog-page";
@@ -71,8 +71,8 @@ export const getDcTree = async () => {
return result;
};
const getOptionsAplcnYm = (yyyymms: string[]): { text: string; val: string }[] => {
let yyyymms1 = yyyymms.map(dateStr => {
const getOptionsAplcnYm = (yyyymms: string[]): { text: string; val: string, hidden?: boolean }[] => {
const yyyymms1 = yyyymms.map(dateStr => {
const year = dateStr.slice(0, 4);
const month = dateStr.slice(4, 6);
return {
@@ -80,7 +80,7 @@ const getOptionsAplcnYm = (yyyymms: string[]): { text: string; val: string }[] =
val: dateStr
};
});
yyyymms1.unshift({ val: '0', text: '선택' });
yyyymms1.unshift({ val: '0', text: '선택', hidden: true });
return yyyymms1;
};
const selectAplcnYms = async(gubun:string) => { // 상수사용월
@@ -109,12 +109,12 @@ export const useAplcnYms = (gubun:string) => {
return data;
};
export const getOptionsInftwtAnlsInfo = (anlsArr: TInftwtAnls[]): { text: string; val: number }[] => {
export const getOptionsInftwtAnlsInfo = (anlsArr: TInftwtAnls[]): { text: string; val: number, hidden?: boolean }[] => {
const options = anlsArr.map(anls => ({
text: anls.anlsInfo,
val: anls.inftwtAnlsSn,
}));
options.unshift({ val: 0, text: '선택' });
options.unshift({ val: 0, text: '선택', hidden: true });
return options;
};
@@ -208,12 +208,12 @@ export const selectPplRdiiQty = async(rainInfwtAnlsSn:number, pplMngNo:string) =
}
};
export const getOptionsRdiiAnlsInfo = (anlsArr: TRdiiAnls[]): { text: string; val: number }[] => {
export const getOptionsRdiiAnlsInfo = (anlsArr: TRdiiAnls[]): { text: string; val: number, hidden?: boolean }[] => {
const options = anlsArr.map(anls => ({
text: anls.anlsInfo,
val: anls.rainInfwtAnlsSn,
}));
options.unshift({ val: 0, text: '선택' });
options.unshift({ val: 0, text: '선택', hidden: true });
return options;
};
@@ -11,25 +11,25 @@ import { useInfilChartModalStore } from '../chartModalStore'; // 모달 상태
type ModalOpenProp = {
pplMngNo?: string, // 관로관리번호
}
type DataType = {label: string; val: number};
type DataType = { label: string; val: number };
const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
const { isOpen: isModalOpen, setIsOpen: setIsModalOpen } = useInfilChartModalStore(); // 모달 상태 관리 스토어
const [popupOpen, setPopupOpen] = React.useState(false); // 조회조건팝업
const buttonRef = React.useRef<HTMLButtonElement>(null); // 버튼 참조
const [popupPosition, setPopupPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 }); // 팝업 위치
const [aplcnYm, setAplcnYm] = useState("");
const [selectedAplcnYm, setSelectedAplcnYm] = useState("");
const [inftwtAnlss, setInftwtAnlss] = useState<TInftwtAnls[]>([]);
const [inftwtAnlsInfos, setInftwtAnlsInfos] = useState<any[]>([{ val: 0, text: '선택' }]); // 침입수분석정보 options
const [inftwtAnlsInfos, setInftwtAnlsInfos] = useState<any[]>([{ val: 0, text: '선택', hidden: true }]); // 침입수분석정보 options
const [inftwtAnls, setInftwtAnls] = useState<TInftwtAnls | null>(null); // 침입수분석정보
const [inftwtAnlsSn, setInftwtAnlsSn] = useState<number>(0); // 침입수분석 정보 선택시 사용
const [selectedInftwtAnls, setSelectedInftwtAnls] = useState<number>(0); // 침입수분석 정보 선택시 사용
const [chartData, setChartData] = useState<{ chartData1: DataType[]; chartData2: DataType[] }>({
chartData1: [], chartData2: [],
});
const [inftwtQty, setInftwtQty] = useState<number>(0); // 침입수량
const aplcnYms = fn.useAplcnYms('inftwtAnlsRslt'); // 상수사용월 데이터 가져오기
pplMngNo = "L"+pplMngNo; // inp파일에 하수관로와 맨홀의 번호가 같으면 분석 오류가 발행하여
pplMngNo = "L" + pplMngNo; // inp파일에 하수관로와 맨홀의 번호가 같으면 분석 오류가 발행하여
const openPopup = () => {
if (buttonRef.current) {
const rect = buttonRef.current.getBoundingClientRect(); // 버튼 위치 계산
@@ -50,60 +50,73 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
useEffect(() => {
if (isModalOpen === false) {
setPopupOpen(true); // 모달이 닫힐 때 조회조건팝업은 열어두기
setAplcnYm(""); // 상수사용월 초기화
setSelectedAplcnYm(""); // 상수사용월 초기화
setInftwtAnlss([]); // 침입수분석 초기화
setInftwtAnlsInfos([{ val: 0, text: '선택' }]); // 침입수분석정보 초기화
setInftwtAnlsInfos([{ val: 0, text: '선택', hidden: true }]); // 침입수분석정보 초기화
setInftwtAnls(null); // 침입수분석정보 초기화
setInftwtAnlsSn(0); // 침입수분석 일련번호 초기화
setChartData({chartData1: [], chartData2: [],}); // 차트 데이터 초기화
setSelectedInftwtAnls(0); // 침입수분석 일련번호 초기화
setChartData({ chartData1: [], chartData2: [], }); // 차트 데이터 초기화
setInftwtQty(0); // 침입수량 초기화
}
}, [isModalOpen]);
// 상수사용월 변경시 분석 정보 리스트 가져오기
useEffect(() => {
// aplcnYm이 비어있거나 "0"이면 API 호출하지 않음
if (!aplcnYm || aplcnYm === "0" || aplcnYm === "") {
setInftwtAnlsSn(0); // 초기화
setInftwtAnlsInfos([{ val: 0, text: '선택' }]);
// 상수사용월 변경 핸들러
const handleAplcnYmChange = async (val: string | number) => {
const selectedValue = String(val);
console.log("선택된 상수사용월:", selectedValue);
setSelectedAplcnYm(selectedValue);
// 즉시 관련 상태 초기화
setInftwtAnls(null);
// 빈 값이면 초기화만 하고 종료
if (!selectedValue || selectedValue === "0" || selectedValue === "") {
setInftwtAnlsInfos([{ val: 0, text: '선택', hidden: true }]);
return;
}
const dcData = fn.getDcTree(); // 자료관 데이터 가져오기
fn.selectInftwtAnls(aplcnYm)
.then((data) => {
setInftwtAnlss(data ?? []);
setInftwtAnlsInfos(fn.getOptionsInftwtAnlsInfo(data));
setInftwtAnls(null); // 오수전환율 이후 초기화
setInftwtAnlsSn(0); // 초기화
})
.catch((error) => {
console.error("selectInftwtAnls 에러:", error);
setInftwtAnlss([]);
setInftwtAnlsInfos([{ val: 0, text: '선택' }]);
});
}, [aplcnYm]);
// 값 표시
useEffect(() => {
// API 호출
try {
const dcData = fn.getDcTree();
const data = await fn.selectInftwtAnls(selectedValue);
setInftwtAnlss(data ?? []);
setInftwtAnlsInfos(fn.getOptionsInftwtAnlsInfo(data));
} catch (error) {
console.error("selectInftwtAnls 에러:", error);
setInftwtAnlss([]);
setInftwtAnlsInfos([{ val: 0, text: '선택', hidden: true }]);
}
setSelectedInftwtAnls(0);
};
// 분석정보 변경 핸들러
const handleInftwtAnlsSnChange = (val: string | number) => {
const selectedValue = Number(val);
setSelectedInftwtAnls(selectedValue);
// 선택된 침입수분석 정보에 대한 처리 로직
if (inftwtAnlsSn === 0) return;
const selectedAnls = inftwtAnlss.find(anls => anls.inftwtAnlsSn === inftwtAnlsSn);
if (selectedValue === 0) {
setInftwtAnls(null);
return;
}
const selectedAnls = inftwtAnlss.find(anls => anls.inftwtAnlsSn === selectedValue);
if (selectedAnls) {
setInftwtAnls(selectedAnls);
}
}, [inftwtAnlsSn]);
};
const closePopup = () => { // 팝업 닫기
fn.selectInftwtAnlsRslt(inftwtAnlsSn, pplMngNo).then((data) => {// 침입수분석 결과 조회
fn.selectInftwtAnlsRslt(selectedInftwtAnls, pplMngNo).then((data) => {// 침입수분석 결과 조회
const rsltData = fn.getChartDataInftwtAnlsRslt(data ?? []);
setChartData(prev => ({
...prev,
chartData1: rsltData // 침입수분석결과
}));
});
fn.selectOilq(inftwtAnlsSn, pplMngNo).then((data) => {// 유량 조회
fn.selectOilq(selectedInftwtAnls, pplMngNo).then((data) => {// 유량 조회
const rsltData = fn.getChartDataOilq(data ?? []);
setChartData(prev => ({
...prev,
@@ -116,7 +129,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
chartData2: [] // 유량 데이터 초기화
}));
});
fn.selectPplInftwtQty(inftwtAnlsSn, pplMngNo).then((data) => {// 침입수량 조회
fn.selectPplInftwtQty(selectedInftwtAnls, pplMngNo).then((data) => {// 침입수량 조회
if (data > 0) {
setInftwtQty(data);
}
@@ -138,7 +151,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
<span className="text-2xl text-red-600 font-bold">상수사용량 적용연월</span>
<div className="flex items-center mt-2">
<span className="text-black text-xl font-bold">
{(aplcnYm)? moment(aplcnYm, "YYYYMM").format("YYYY-MM"):""}
{(selectedAplcnYm) ? moment(selectedAplcnYm, "YYYYMM").format("YYYY-MM") : ""}
</span>
</div>
</div>
@@ -175,10 +188,8 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
selectClass="rounded-lg bg-white"
label=''
selectArray={aplcnYms}
value={aplcnYm}
onSelectValue={(val) => {console.log("선택된 상수사용월:", val);
setAplcnYm(String(val));
}}
value={selectedAplcnYm}
onSelectValue={handleAplcnYmChange}
/>
</div>
<div className='flex items-center justify-between'>
@@ -188,10 +199,8 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
selectClass="rounded-lg bg-white"
label=''
selectArray={inftwtAnlsInfos}
value={inftwtAnlsSn}
onSelectValue={(val) => {
setInftwtAnlsSn(Number(val));
}}
value={selectedInftwtAnls}
onSelectValue={handleInftwtAnlsSnChange}
/>
</div>
<div className='flex items-center justify-between'>
@@ -256,7 +265,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
onOpenChange={setIsModalOpen}
headerTitle=""
bodyClass={`
h-[50vh]
min-h-[50vh]
w-[50vw]
max-w-none
max-h-none
@@ -264,7 +273,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
z-[50]
`}
body={renderBody()}
noFooterYn = 'Y'
noFooterYn='Y'
/>
)
}
@@ -106,7 +106,9 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
});
fn.selectPplRdiiQty(rainInfwtAnlsSn, pplMngNo).then((data) => {// 강우유입수량 조회
setRdiiQty(Math.round(data.rdiih*24)); // cmd: m³/d 단위로 변환
setTotalVolume(Math.round(data.rdiih * data.sustaintime)); // 총체적 m³/mm
if (data.raintotal && data.rainTotal !== 0) {
setTotalVolume(Math.round(data.rdiih * data.sustaintime / data.raintotal)); // 총체적 m³/mm
}
});
setPopupOpen(false);
};
@@ -248,7 +250,7 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
onOpenChange={setIsModalOpen}
headerTitle=""
bodyClass={`
h-[50vh]
min-h-[50vh]
w-[50vw]
max-w-none
max-h-none
+1
View File
@@ -129,6 +129,7 @@ const Map = () => {
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={undefined}
/>
</div>
);
@@ -0,0 +1,48 @@
/**
* 파일 확장자 검증 유틸리티
*/
// 파일 확장자 검증 함수
export const validateFileExtension = (file: File, allowedExtensions: string[]): boolean => {
const fileName = file.name.toLowerCase();
return allowedExtensions.some(ext => fileName.endsWith(ext.toLowerCase()));
};
// 파일 확장자 검증 결과 타입
export interface FileValidationResult {
isValid: boolean;
errorMessage?: string;
}
// 파일 검증 설정 타입
export interface FileValidationConfig {
file: File;
allowedExtensions: string[];
fileDescription: string;
}
/**
* 여러 파일의 확장자를 한 번에 검증하는 함수
* @param validationConfigs 검증할 파일들의 설정 배열
* @returns 검증 결과. 실패 시 첫 번째 에러 메시지 반환
*/
export const validateMultipleFiles = (validationConfigs: FileValidationConfig[]): FileValidationResult => {
for (const config of validationConfigs) {
if (!validateFileExtension(config.file, config.allowedExtensions)) {
const allowedExtStr = config.allowedExtensions.join(', ');
return {
isValid: false,
errorMessage: `${config.fileDescription} 파일은 ${allowedExtStr} 확장자만 업로드 가능합니다.`
};
}
}
return { isValid: true };
};
// 자주 사용되는 확장자 상수
export const FILE_EXTENSIONS = {
INP: ['.inp'],
EXCEL: ['.xls', '.xlsx', '.xlsm', '.xltx'],
ALL_OFFICE: ['.xls', '.xlsx', '.xlsm', '.xltx', '.doc', '.docx', '.ppt', '.pptx']
};
@@ -6,11 +6,13 @@ import { DateMonthPicker, filePicked } from '@common/business_components/ui'
import ListDiv from '@common/business_components/ui/list_div'
import { Input } from '@common/components/ui'
import { zodResolver } from '@hookform/resolvers/zod'
import { useState, useEffect } from 'react'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { excelToFlowRateData, excelToMatchingResultData, excelToTimePatternData } from '@/components/lib/utils'
import BaseLayout from '../BaseLayout'
import { validateMultipleFiles, FILE_EXTENSIONS, type FileValidationConfig } from '../../fileValidation';
import { validateBasicSelectionsWithAlert } from '../../selectionValidation';
const formSchema = z.object({
managenm: z.string(),
@@ -148,6 +150,17 @@ const IrSetting = () => {
const handleAnalysisClick = async () => {
setIsLoadingResult(true); // 로딩 상태 시작
// 기본 선택 항목들 검증 (환경청, 시도, 시군구, 하수처리장)
if (!validateBasicSelectionsWithAlert(select1, select2, select3, select4, {
requireEnv: true,
requireSido: true,
requireSigungu: true,
requireTreatmentPlant: true
})) {
setIsLoadingResult(false);
return;
}
console.log(uploadedFilesData);
const file1 = uploadedFilesData[0]; // 오수관망도
const file2 = uploadedFilesData[1]; // 수용가-맨홀 매칭결과
@@ -167,6 +180,42 @@ const IrSetting = () => {
return;
}
// 파일 확장자 검증 (분리된 유틸리티 사용)
const validationConfigs: FileValidationConfig[] = [
{
file: file1.file,
allowedExtensions: FILE_EXTENSIONS.INP,
fileDescription: '오수관망도'
},
{
file: file2.file,
allowedExtensions: FILE_EXTENSIONS.EXCEL,
fileDescription: '수용가-맨홀 매칭결과'
},
{
file: file3.file,
allowedExtensions: FILE_EXTENSIONS.EXCEL,
fileDescription: '오수량 시간패턴'
}
];
// 유량자료 파일이 있는 경우 검증 목록에 추가
if (file4?.file) {
validationConfigs.push({
file: file4.file,
allowedExtensions: FILE_EXTENSIONS.EXCEL,
fileDescription: '유량자료'
});
}
// 파일 확장자 검증 실행
const validationResult = validateMultipleFiles(validationConfigs);
if (!validationResult.isValid) {
setIsLoadingResult(false);
alert(validationResult.errorMessage);
return;
}
// 오수전환율 값 0 ~ 1 사이인지 검증
if (input1 === '' || input1 === null || input1 === undefined) {
setIsLoadingResult(false);
@@ -394,21 +443,6 @@ const IrSetting = () => {
</div>
</ListDiv>
</div>]}
{/* <div className="flex items-center gap-4">
<FormSection
name='region' form={form} titleNm='▷ 유량자료 등록'>
<FileUploadForm
key={`file-upload-3-${formResetKey}`} // 폼 초기화를 위한 key
onFileSelect={(file, cd, id) => handleFileSelection(3, file, cd, id)}
showButton={false}
showPreview={true}
accept={".xls, .xlsx, .xlsm, .xltx"}
fileIdx={3} />
</FormSection>
<div className="flex justify-center mt-1">
<Button size="sm" type="submit" onClick={handleAnalysisClick}>침입수분석 실행</Button>
</div>
</div> */}
</BaseLayout>
</>
)
@@ -13,6 +13,8 @@ import { useFieldArray, useForm } from "react-hook-form"
import { z } from "zod"
import { excelToMatchingResultData, excelToRainData } from '@/components/lib/utils'
import { EditableFormTable, type EditableTableHeadItem } from '../../../components/ui/EditableFormTable'
import { validateMultipleFiles, FILE_EXTENSIONS, type FileValidationConfig } from '../../fileValidation';
import { validateBasicSelectionsWithAlert } from '../../selectionValidation';
// zod 스키마 정의
@@ -167,7 +169,18 @@ export default function RTKForm() {
// 침입수 분석 실행 버튼 클릭 핸들러
const handleAnalysisClick = async () => {
setIsLoadingResult(true); // 로딩 상태 시작
// 기본 선택 항목들 검증 (환경청, 시도, 시군구, 하수처리장)
if (!validateBasicSelectionsWithAlert(select1, select2, select3, select4, {
requireEnv: true,
requireSido: true,
requireSigungu: true,
requireTreatmentPlant: true
})) {
setIsLoadingResult(false);
return;
}
const formData = form.getValues();
console.log(uploadedFilesData);
const file1 = uploadedFilesData[0]; // 오수관망도
@@ -185,6 +198,33 @@ export default function RTKForm() {
return;
}
// 파일 확장자 검증 (분리된 유틸리티 사용)
const validationConfigs: FileValidationConfig[] = [
{
file: file1.file,
allowedExtensions: FILE_EXTENSIONS.INP,
fileDescription: '오수관망도'
},
{
file: file2.file,
allowedExtensions: FILE_EXTENSIONS.EXCEL,
fileDescription: '수용가-맨홀 매칭결과'
},
{
file: file3.file,
allowedExtensions: FILE_EXTENSIONS.EXCEL,
fileDescription: '오수량 시간패턴'
}
];
// 파일 확장자 검증 실행
const validationResult = validateMultipleFiles(validationConfigs);
if (!validationResult.isValid) {
setIsLoadingResult(false);
alert(validationResult.errorMessage);
return;
}
// 오수전환율 값 0 ~ 1 사이인지 검증
const rateValue = form.getValues().rate;
if (rateValue === '' || rateValue === null || rateValue === undefined) {
@@ -193,7 +233,7 @@ export default function RTKForm() {
alert('오수전환율을 입력해주세요.');
return;
}
const numericInput1 = parseFloat(rateValue);
if (isNaN(numericInput1) || numericInput1 < 0 || numericInput1 > 1) {
setIsLoadingResult(false);
@@ -359,7 +399,7 @@ export default function RTKForm() {
selectedSwageTreatmentPlant={select4}
onSwageTreatmentPlantChange={setSelect4}
selectedSwageProcessingZone={select5}
onSwageProcessingZoneChange={setSelect4}
onSwageProcessingZoneChange={setSelect5}
isLoading={isLoadingResult}
onAnalysis={handleAnalysisClick}
>
@@ -0,0 +1,161 @@
/**
* 선택 항목 검증 유틸리티
*/
export interface ApiSelectOption {
text: string;
val: string;
}
// 선택 항목 검증 설정 타입
export interface SelectionValidationConfig {
value: string | number | ApiSelectOption;
description: string;
required: boolean;
}
// 선택 항목 검증 결과 타입
export interface SelectionValidationResult {
isValid: boolean;
errorMessage?: string;
}
/**
* 단일 선택 항목이 유효한지 검증하는 함수
* @param value 검증할 값
* @param isRequired 필수 선택 여부
* @returns 유효한지 여부
*/
export const isValidSelection = (
value: string | number | ApiSelectOption,
isRequired: boolean = true
): boolean => {
if (!isRequired) return true;
if (typeof value === 'object' && value !== null) {
// ApiSelectOption 객체인 경우
return value.val !== '0' && value.val !== '' && value.val !== null && value.val !== undefined;
}
// 문자열 또는 숫자인 경우
return value !== '0' && value !== 0 && value !== '' && value !== null && value !== undefined;
};
/**
* 여러 선택 항목을 한 번에 검증하는 함수
* @param validationConfigs 검증할 선택 항목들의 설정 배열
* @returns 검증 결과. 실패 시 첫 번째 에러 메시지 반환
*/
export const validateMultipleSelections = (
validationConfigs: SelectionValidationConfig[]
): SelectionValidationResult => {
for (const config of validationConfigs) {
if (!isValidSelection(config.value, config.required)) {
return {
isValid: false,
errorMessage: `${config.description}을(를) 선택해주세요.`
};
}
}
return { isValid: true };
};
/**
* 여러 선택 항목을 한 번에 검증하고 alert 표시하는 함수
* @param validationConfigs 검증할 선택 항목들의 설정 배열
* @returns 검증 결과
*/
export const validateMultipleSelectionsWithAlert = (
validationConfigs: SelectionValidationConfig[]
): boolean => {
const result = validateMultipleSelections(validationConfigs);
if (!result.isValid && result.errorMessage) {
alert(result.errorMessage);
return false;
}
return true;
};
/**
* 기본 선택 항목들 검증 (환경청, 시도, 시군구, 하수처리장)
* @param select1 환경청코드
* @param select2 시도코드
* @param select3 시군구 객체
* @param select4 하수처리장코드
* @param options 각 항목의 필수 여부 설정
* @returns 검증 결과
*/
export const validateBasicSelections = (
select1: string,
select2: string,
select3: ApiSelectOption,
select4: string | number,
options: {
requireEnv?: boolean;
requireSido?: boolean;
requireSigungu?: boolean;
requireTreatmentPlant?: boolean;
} = {}
): SelectionValidationResult => {
const {
requireEnv = true,
requireSido = true,
requireSigungu = true,
requireTreatmentPlant = true
} = options;
const validationConfigs: SelectionValidationConfig[] = [
{
value: select1,
description: '환경청',
required: requireEnv
},
{
value: select2,
description: '시도',
required: requireSido
},
{
value: select3,
description: '시군구',
required: requireSigungu
},
{
value: select4,
description: '하수처리장',
required: requireTreatmentPlant
}
];
return validateMultipleSelections(validationConfigs);
};
/**
* 기본 선택 항목들 검증 및 alert 표시
* @param select1 환경청코드
* @param select2 시도코드
* @param select3 시군구 객체
* @param select4 하수처리장코드
* @param options 각 항목의 필수 여부 설정
* @returns 검증 결과
*/
export const validateBasicSelectionsWithAlert = (
select1: string,
select2: string,
select3: ApiSelectOption,
select4: string | number,
options: {
requireEnv?: boolean;
requireSido?: boolean;
requireSigungu?: boolean;
requireTreatmentPlant?: boolean;
} = {}
): boolean => {
const result = validateBasicSelections(select1, select2, select3, select4, options);
if (!result.isValid && result.errorMessage) {
alert(result.errorMessage);
return false;
}
return true;
};
+1 -1
View File
@@ -1,4 +1,4 @@
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || '';
const BASE_URL = 'http://con.mind-one.co.kr:11083/sewerage-service/api';
export const postData = async (url: string, data: object): Promise<any> => {
const options: RequestInit = {
+32
View File
@@ -12,6 +12,38 @@ export const yearOptions = Array.from({ length: 10 }, (_, i) => {
return { text: `${year}년`, val: year.toString() };
});
// 현재 날짜를 기준으로 현재 분기까지의 분기 옵션 생성
export const getQuarterOptions = (selectedYear?: string): { text: string; val: string }[] => {
const currentDate = new Date();
const currentYear = currentDate.getFullYear().toString();
const currentMonth = currentDate.getMonth() + 1; // 0부터 시작하므로 +1
// 현재 년도인 경우에만 현재 날짜까지의 분기 표시
if (selectedYear === currentYear) {
// 현재 월을 기준으로 현재 분기 계산
const currentQuarter = Math.ceil(currentMonth / 3);
// 현재 분기까지의 분기 옵션 생성
const quarterOptions = [];
for (let i = 1; i <= currentQuarter; i++) {
quarterOptions.push({
text: `${i}분기`,
val: i.toString()
});
}
return quarterOptions;
} else {
// 이전 년도인 경우 모든 분기 표시
return [
{ text: "1분기", val: "1" },
{ text: "2분기", val: "2" },
{ text: "3분기", val: "3" },
{ text: "4분기", val: "4" }
];
}
};
// ["202506", "202505", "202502", "202405"] 을 SelectBox의 옵션으로 변환
export const convertToTextValArray = (yyyymms: string[]): { text: string; val: string }[] => {
return yyyymms.map(dateStr => {
@@ -1,17 +1,30 @@
"use client"
import { SelectBox } from '@common/business_components/ui';
import React, { useState } from 'react'
import React, { useState, useEffect } from 'react'
import FileUploadForm from './fileUploadForm';
import { yearOptions } from '@/components/lib/utils';
import { yearOptions, getQuarterOptions } from '@/components/lib/utils';
const operFileUploadForm = ({ title, onSubmit }: { title: string, onSubmit: (files: string, year: string, quarter: string, code: string, id: string) => void }) => {
const [atflCd, setAtflCd] = useState<string | null>(null);
const [atflId, setAtflId] = useState<string | null>(null);
const quater = [{ text: '1분기', val: '1' }, { text: '2분기', val: '2' }, { text: '3분기', val: '3' }, { text: '4분기', val: '4' }]
const [selectedYear, setSelectedYear] = useState<string>(yearOptions[0]?.val || '');
const [selectedQuarter, setSelectedQuarter] = useState<string>(quater[0]?.val || '');
const [quaterOptions, setQuaterOptions] = useState(() => getQuarterOptions(selectedYear));
const [selectedQuarter, setSelectedQuarter] = useState<string>(quaterOptions[0]?.val || '');
// 년도가 변경될 때 분기 옵션 업데이트
useEffect(() => {
const updatedQuarterOptions = getQuarterOptions(selectedYear);
setQuaterOptions(updatedQuarterOptions);
// 분기 옵션이 변경되면 첫 번째 옵션으로 자동 선택
setSelectedQuarter(updatedQuarterOptions[0]?.val || '');
}, [selectedYear]);
// 년도 변경 핸들러
const handleYearChange = (value: string | number) => {
const yearValue = String(value);
setSelectedYear(yearValue);
};
return (
<div>
@@ -26,13 +39,16 @@ const operFileUploadForm = ({ title, onSubmit }: { title: string, onSubmit: (fil
selectClass="rounded-lg bg-white"
label="년도"
selectArray={yearOptions}
onSelectValue={(value: string | number) => setSelectedYear(String(value))}
/><SelectBox
onSelectValue={handleYearChange}
value={selectedYear}
/>
<SelectBox
className="w-50"
selectClass="rounded-lg bg-white"
label="분기"
selectArray={quater}
selectArray={quaterOptions}
onSelectValue={(value: string | number) => setSelectedQuarter(String(value))}
value={selectedQuarter}
/>
<FileUploadForm
onSubmit={(files, year, quarter, code, id) => {
@@ -0,0 +1,87 @@
import { VerticalTable } from '../../../components/ui/verticalTable';
import React, { useState } from 'react'
import InputSection from '../InputSection';
import { handleInputChange } from '../common';
import { Button } from '@common/components/ui';
import InfilChart from './chart/infilChart';
// 하수관로
// TODO: RDII 버튼인가 그거 팝업 두개 구현 필요
const inputColumn = [
{ id: "title", title: "설치주제", type: "select", options: ["BTL", "재정사업"] },
{ id: "swtrmt_zone_cd", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
{ id: "basu", title: "배수구역", type: "input" },
{ id: "mltv_dtdv_nm", title: "배수분구", type: "input" },
{ id: "wltjs", title: "지선/간선", type: "select", options: ["지선", "간선"] },
{ id: "flow", title: "흐름방식", type: "select", options: ["자연유하", "압송"] },
{ id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] },
];
const SwtrmtFclt = ({ featureData }: { featureData: any }) => {
console.log("featureData", featureData);
const [isModalOpen, setIsModalOpen] = React.useState(false);
const [btnTxt, setBtnTxt] = useState<string>("");
const openModal = (e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.textContent && setBtnTxt(e.currentTarget.textContent)
setIsModalOpen(true);
};
const closeModal = () => {
setIsModalOpen(false);
};
const [repairData, setRepairData] = useState([
{ year: "2025", type: "개대체" },
{ year: "2025", type: "보수" },
]);
const [cleaningData, setCleaningData] = useState([
{ year: "2025", type: "청소" },
{ year: "2025", type: "준설" },
]);
const [inspectionData, setInspectionData] = useState([
{ year: "2025", type: "관로기술진단" },
{ year: "2025", type: "관로기술진단" },
]);
const dataMap = {
repair: { data: repairData, setData: setRepairData },
cleaning: { data: cleaningData, setData: setCleaningData },
inspection: { data: inspectionData, setData: setInspectionData },
};
const keysToExtract = [
"swtrmt_zone_cd",
"mltv_dtdv_nm",
"fid",
"lyrTypeNm",
"reg_dt",
"mdfcn_dt",
"rgtr",
"mdfr",
];
// const filteredData = Object.entries(featureData)
// .filter(([key]) => keysToExtract.includes(key)) // 필요한 키만 필터링
// .map(([key, value]) => ({ key, value: value ?? "N/A" })); // 테이블 데이터 형식으로 변환
return (
<div>
<VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={20} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}}></VerticalTable>
<InputSection title="보수 및 갱신" data={repairData} sectionKey="repair" onInputChange={(section, index, field, value) => handleInputChange(section, index, field, value, dataMap)} />
<InputSection title="청소 및 준설" data={cleaningData} sectionKey="cleaning" onInputChange={(section, index, field, value) => handleInputChange(section, index, field, value, dataMap)} />
<InputSection title="점검 및 진단" data={inspectionData} sectionKey="inspection" onInputChange={(section, index, field, value) => handleInputChange(section, index, field, value, dataMap)} />
<div className="absolute bottom-0 left-0 w-full flex justify-start space-x-2 p-4">
<Button
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
onClick={openModal}
>
Infil
</Button>
<Button
className="px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700"
onClick={openModal}
>
RDII
</Button>
</div>
<InfilChart btnTxt={btnTxt} isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal} />
</div>
)
}
export default SwtrmtFclt
@@ -0,0 +1,49 @@
import Modal from '../../../components/ui/modal';
import { VerticalTable } from '../../../components/ui/verticalTable';
import { UiTable } from '@common/business_components/ui';
import { Button, TableCell } from '@common/components/ui';
import React, { useState } from 'react'
// 하수처리시설
const headList = [
{ id: "title", title: "하수도정비 기본계획보고서" },
{ id: "cad", title: "하수도대장 평면도" },]
const modalHeadList = [
{ id: "no", title: "번호" },
{ id: "year", title: "승인연도" },
{ id: "cad", title: '평면도'},
{ id: "fileNm", title: "파일명" },
{ id: "date", title: "입력시간" },
]
const SwtrmtFclt = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalTitle, setModalTitle] = useState<string>('');
const openModal = (title:string) => {
setIsModalOpen(true);
setModalTitle(title)
};
const closeModal = () => {
setIsModalOpen(false);
};
return (
<div>
<VerticalTable children={{
title: <Button onClick={() => openModal('하수도정비 기본계획보고서')}>보고서</Button>,
cad: <Button onClick={() => openModal('하수도대장 평면도')}>평면도</Button>,
}} headName='디지털 자료관 연계정보' tableData={[]} headlist={headList} pageSize={20} total={0} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}}></VerticalTable>
<Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal}>
<div className='grid grid-col-2'>
<UiTable headName={modalTitle} tableData={[]} headlist={modalHeadList} pageSize={20} total={0} cellClick={undefined} children={undefined} onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}
}></UiTable>
</div>
</Modal>
</div>
)
}
export default SwtrmtFclt
@@ -0,0 +1,140 @@
import React, { useState } from 'react'
import { type ChartConfig, MainChart, CHART_FORM } from "@common/business_components";
import Modal from '../../../../components/ui/modal';
import { Button } from '@common/components/ui';
type ModalOpenProp = {
isModalOpen: boolean,
setIsModalOpen: (open: boolean) => void,
closeModal: () => void,
btnTxt?: string,
}
const InfilChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal }: ModalOpenProp) => {
const [popupOpen, setPopupOpen] = React.useState(false);
const buttonRef = React.useRef<HTMLButtonElement>(null); // 버튼 참조
const [popupPosition, setPopupPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 }); // 팝업 위치
const openPopup = () => {
if (buttonRef.current) {
const rect = buttonRef.current.getBoundingClientRect(); // 버튼 위치 계산
setPopupPosition({
top: rect.bottom + window.scrollY, // 버튼 아래쪽
left: rect.left + window.scrollX, // 버튼 왼쪽
});
}
setPopupOpen(true); // 팝업 열기
};
console.log("btnTxt", btnTxt);
const closePopup = () => {setPopupOpen(false);}; // 팝업 닫기
const chartForm = CHART_FORM.LINE;
const chartData = {
chartData1: [
{ label: "1", val: 20 },
{ label: "3", val: 40 },
{ label: "5", val: 60 },
],
chartData2: [
{ label: "1", val: 15 },
{ label: "3", val: 35 },
{ label: "5", val: 55 },
],
}
const chartConfig = Object.values(chartData).flat().reduce((config, element) => {
config[element.label] = {
label: element.label,
color: "hsl(var(--chart-1))",
};
return config;
}, {} as Record<string, { label: string; color: string }>) satisfies ChartConfig;
const getCurrentDate = () => {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, "0"); // 월은 0부터 시작하므로 +1
return `${year}-${month}`;
};
return (
<Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal}>
{/* 분석 입력현황 조회 */}
<div className='grid grid-cols-4 gap-4'>
<div className="grid grid-rows-4 gap-4 mb-4">
<Button className="bg-white text-white w-[130px] font-bold px-4 mt-2 py-2" onClick={openPopup}>
분석 입력현황 조회
</Button>
{/* 침입수량 분석결과 */}
<div className="bg-white text-red-600 font-bold px-4 py-2 ">
침입수량 분석결과
<div className="flex items-center mt-2">
<span className="text-black text-xl font-bold">120</span>
<span className="ml-2 text-black">m³/d</span>
</div>
</div>
</div>
{/* 차트 */}
<div className="w-[500px] h-[300px] bg-white p-4">
<MainChart chartConfig={chartConfig} chartForm={chartForm} chartData={chartData} chartTitle={'관로유량'} />
</div>
</div>
{popupOpen && (
<div
className="absolute bg-gray-100 border border-gray-300 w-[320px] shadow-lg p-4 rounded"
style={{
top: popupPosition.top,
left: popupPosition.left,
zIndex: 1000,
}}
>
<div className="text-lg font-bold mb-2">분석 입력현황</div>
<div className="grid grid-cols-2 gap-2">
<div>상수사용량 정보:</div>
<div>
<input type='month' defaultValue={getCurrentDate()} className="border border-gray-300 rounded px-2 py-1"/>
</div>
<div>오수 전환율:</div>
<div>
<select className="border border-gray-300 rounded px-2 py-1">
<option>0.85</option>
<option>0.90</option>
</select>
</div>
<div>수용가-맨홀 매칭:</div>
<div>
<select className="border border-gray-300 rounded px-2 py-1">
<option>매칭(양평).shp</option>
<option>매칭(서울).shp</option>
</select>
</div>
<div>관망도 자료:</div>
<div>
<select className="border border-gray-300 rounded px-2 py-1">
<option>양평.inp</option>
<option>서울.inp</option>
</select>
</div>
{btnTxt === 'RDII' ? <><div>강우 자료:</div>
<div>
<select className="border border-gray-300 rounded px-2 py-1">
<option>R20240815.xlsx</option>
</select>
</div>
<div>RTX 계수 출처:</div>
<div>
<select className="border border-gray-300 rounded px-2 py-1">
<option>2015년 하수도정비기본계획</option>
</select>
</div>
</> : null}
</div>
<div className="flex justify-end mt-4 space-x-2">
<button className="px-4 py-2 bg-gray-600 text-white rounded" onClick={closePopup}>
저장
</button>
<button className="px-4 py-2 bg-gray-600 text-white rounded" onClick={closePopup}>
종료
</button>
</div>
</div>
)}
</Modal>
)
}
export default InfilChart
@@ -0,0 +1,18 @@
import { VerticalTable } from '../../../components/ui/verticalTable';
import React from 'react'
const inputColumn = [
{ id: "swtrmt_zone_cd", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
{ id: "mltv_dtdv_nm", title: "배수분구", type: "input" },
{ id: "freq", title: "설계빈도", type: "input" },
]
// 오수펌프
const sewagePump = ({ featureData }: { featureData: any }) => {
return (
<div><VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={0} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
throw new Error('Function not implemented.');
}} />
</div>
)
}
export default sewagePump
@@ -0,0 +1,21 @@
// 하수맨홀
import { VerticalTable } from '../../../components/ui/verticalTable';
import React from 'react'
const inputColumn = [
{ id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
{ id: "basu", title: "배수구역", type: "input" },
{ id: "bungu", title: "배수분구", type: "input" },
{ id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] },
{id:'spec', title:'규격', type:'input'},
];
const Smh = () => {
return (
<div>
<VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={20} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}}></VerticalTable>
</div>
)
}
export default Smh;
@@ -0,0 +1,19 @@
import { VerticalTable } from '../../../components/ui/verticalTable';
import React from 'react'
const inputColumn = [
{ id: "swtrmt_zone_cd", title: "처리구역", type: "input" },
{ id: "mltv_dtdv_nm", title: "배수분구", type: "input" },
]
// 빗물펌프
const StormWaterPump = ({ featureData }: { featureData: Record<string, any> }) => {
const validKey = inputColumn.map((item) => item.id);
const filteredData = featureData && Object.entries(featureData).filter(([key]) => validKey.includes(key)).reduce((obj: Record<string, any>, [key, value]) => {obj[key] = value; return obj;}, {});
return (
<div><VerticalTable headName='자체 입력정보' tableData={[filteredData]} headlist={inputColumn} pageSize={0} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
throw new Error('Function not implemented.');
}} />
</div>
)
}
export default StormWaterPump
+12
View File
@@ -0,0 +1,12 @@
NEXT_PUBLIC_SITE_ID = WR
NEXT_PUBLIC_PORT = 11301
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11301
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_TEMP_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver #TODO 운영에서 바꿔야 함
NEXT_PUBLIC_CLAIM_NAME = Authorization
NEXT_PUBLIC_AUTH_USER_ID = token-id
NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
+14
View File
@@ -0,0 +1,14 @@
NEXT_PUBLIC_SITE_ID = WR
NEXT_PUBLIC_PORT = 3001
NEXT_PUBLIC_PROXY_HOST = http://localhost:3001
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_TEMP_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver #TODO 운영에서 바꿔야 함
NEXT_PUBLIC_CLAIM_NAME = Authorization
NEXT_PUBLIC_AUTH_USER_ID = token-id
NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
+1 -1
View File
@@ -31,7 +31,7 @@ yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
#.env*
# vercel
.vercel
+201
View File
@@ -0,0 +1,201 @@
'use client'
import { getData, putData } from "../../../sn/app/components/lib/api";
import Modal from "../../../sn/app/components/ui/modal";
import { VerticalTable } from "../../../sn/app/components/ui/verticalTable";
import { useGisContext } from "@common/business_components/ui/map/Gis";
import { Button } from "@common/components/ui";
import { useEffect, useRef, useState } from "react";
import { getLayerContentComponent } from "../../../sn/app/map/components/common";
// TODO:
const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData: any }) => {
const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject<number[]>; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; };
// const featureId = featureData.getId(); // 피처의 id 가져오기
const featureId = typeof featureData.getId === 'function'
? featureData.getId()
: featureData.fid;
const [layerNm, setLayerNm] = useState<string>(""); // 레이어 이름 상태
const [tableData, setTableData] = useState<any>(null); // 피처 데이터
const [mntrngsData, setMntrngsData] = useState(false); // 수량/수질 측정 정보
const [headlist, setHeadlist] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태
const [headlist2, setHeadlist2] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태
const [buttonChk, setButtonChk] = useState(true)
const [fetchData, setfetchData] = useState<any>(); // api 호출여부
const contentRef = useRef<HTMLDivElement>(null);
const [isModalOpen, setIsModalOpen] = useState(false); // 모달 열기 상태
const [chkMainSsc, setChkMainSsc] = useState(false); // 중점우수토실 여부
// 피쳐데이터의 현재 레이어 이름 가져오는 함수
const getSelectedLayers = () => {
let result = null;
const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기
let featureLayer: { get: (key: string) => any } = { get: (key: string) => null }; // 피처가 속한 레이어를 찾기 위한 변수
layers.forEach((layer: any) => {
const source = layer.getSource();
if (source && source.hasFeature && featureData
&& typeof featureData.getId === 'function' && source.hasFeature(featureData)) {
featureLayer = layer; // 피처가 속한 레이어 찾기
}
});
// SearchModal 클릭 이벤트와 Gis 클릭 이벤트 구분하기
if (featureLayer.get('lyrNm') === null) {
if (featureData.lyrTypeNm === 'tb_il_swage_prcs_fclt') result = '하수처리시설';
else if (featureData.lyrTypeNm === 'tb_ms_swage_prcs_zone') result = '하수처리구역';
else if (featureData.lyrTypeNm === 'om_dt_untrt_swg_rain') result = '중점우수토실';
}
else result = featureLayer.get('lyrNm');
return result;
// return featureLayer.get('lyrNm'); // 레이어 이름 출력
};
useEffect(() => {
const fetchData = async () => {
if (featureId) {
const selectedLayerName = getSelectedLayers(); // 레이어 이름 가져오기
setLayerNm(selectedLayerName); // 상태 업데이트
const layerId = featureId.split('.')[0]
let data = await getData(`v1/fclts/${featureId}?lyrTypeNm=${layerId}`);
setfetchData(data)
switch (selectedLayerName) {
case '하수처리시설':
setButtonChk(false)
break;
case '하수처리구역':
setButtonChk(false)
break;
case '중점우수토실':
setButtonChk(false)
setChkMainSsc(true)
// 데이터 전처리: 합쳐진 필드 추가
data = data ? {
...data,
discharge_pipe_spec: `직경: ${data.pipe_diameter || '-'} / 높이: ${data.pipe_hg || '-'} / 길이: ${data.pipe_lt || '-'}`
} : {};
// 수량/수질측정 정보 데이터 호출
const additionalResponse = await getData(`v1/onsemiro/swgRain/${data.adst_dstr_cd}/${data.manage_no}/mntrngs`);
setMntrngsData(additionalResponse.content);
break;
case '우수토실':
setButtonChk(true)
break;
case '하수관로':
setButtonChk(true)
break;
case '오수펌프':
setButtonChk(true)
break;
case '빗물펌프':
setButtonChk(true)
break;
case '하수맨홀':
setButtonChk(true)
break;
default:
break;
}
setTableData(data);
};
}
fetchData();
}, [featureId]);
// 저장 시 모달 띄우기
const handleSaveClick = () => {
setIsModalOpen(true); // 모달 열기
};
const handleModalSave = () => {
setIsModalOpen(false); // 모달 닫기
btnClickEvent(); // 저장 이벤트 실행
};
const handleModalClose = () => {
setIsModalOpen(false); // 모달 닫기
};
const btnClickEvent = async () => {
const bodyData: Record<string, any> = {}
if (buttonChk && contentRef.current) {
const inputElements = contentRef.current.querySelectorAll('input');
const selectElements = contentRef.current.querySelectorAll('select');
// 카멜케이스 변환 함수
const toCamelCase = (str: string) =>
str.replace(/_([a-z])/g, (_, char) => char.toUpperCase());
// select 태그의 값 가져오기
selectElements.forEach((select) => {
const camelCaseId = toCamelCase(select.id);
bodyData[camelCaseId] = select.value;
});
// input 태그의 값 가져오기
inputElements.forEach((input) => {
const camelCaseId = toCamelCase(input.id);
bodyData[camelCaseId] = input.value;
});
if (layerNm === '하수관로') bodyData['lyrTypeNm'] = 'tb_ms_swage_ppl'
else if (layerNm === '우수토실') bodyData['lyrTypeNm'] = 'tb_ms_stofcbr'
else if (layerNm === '오수펌프') bodyData['lyrTypeNm'] = 'tb_ms_swage_pmstn'
else if (layerNm === '빗물펌프') bodyData['lyrTypeNm'] = 'tb_ms_swage_pmstn'
else if (layerNm === '하수맨홀') bodyData['lyrTypeNm'] = 'tb_ms_swage_mnhl'
bodyData['fid'] = featureId
const data = await putData(`v1/fclts/${featureId}`, bodyData);
if (data.status == 200) {
alert('저장되었습니다')
setTableData(data)
setfetchData(data)
}
}
}
type ContentComponentProps = {
featureData: any;
chkMainSsc: boolean;
additionalData: any;
};
const ContentComponent = layerNm ? getLayerContentComponent(layerNm) as React.ComponentType<ContentComponentProps> : null;
return (
<div id="popup" className="absolute top-0 right-0 w-[350px] h-[calc(100vh-10rem)] bg-white border-r border-gray-300 flex flex-col overflow-hidden transition-all duration-300 z-[102]">
<div className="card-header bg-primary flex items-center justify-between p-4">
<div className="flex mx-3 flex-col">
<h3 className="text-2xl font-medium text-white text-label">{layerNm} 정보</h3>
<div className="font-medium text-white text-label">(00구 00리 하수관로)</div>
</div>
<button
onClick={onClose}
className="text-white hover:text-gray-700 focus:outline-none"
aria-label="닫기"
>
✕
</button>
</div>
<div className="overflow-y-auto overflow-x-hidden flex-1" ref={contentRef}>
<VerticalTable
headName='LX 연계정보'
tableData={[tableData]}
headlist={headlist}
pageSize={20}
total={0}
children={undefined}
cellClick={undefined}
onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}}
/>
<div className="w-full mx-5 flex items-center justify-end space-x-2">
{buttonChk ? <Button onClick={handleSaveClick} className="px-4 mr-5 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700">
저장
</Button> : null}
</div>
<Modal isModalOpen={isModalOpen} title="자체입력정보 저장" setIsModalOpen={setIsModalOpen} closeModal={handleModalClose} saveModal={handleModalSave}><div className="text-lg">정보를 저장하시겠습니까?</div></Modal>
{ContentComponent &&
<ContentComponent
featureData={fetchData}
chkMainSsc={chkMainSsc}
additionalData={mntrngsData}
/>
}
</div>
</div>
)
}
export default FeatureClickEventPopup;
+621
View File
@@ -0,0 +1,621 @@
"use client";
import {
Form,
FormField,
FormUi,
SelectBox,
} from "@common/business_components/ui";
import { Button, Input } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { useWrStore } from "@/app/store";
import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import {
hdtflZod,
type customHtdflBdst,
} from "@/app/store/ruseFcltMng/fcltReg/htdflReg";
import { useEffect } from "react";
import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg";
import {
formatCommaNumber,
handleCommaNumberChange,
} from "@/components/lib/func/commom";
import { formH1Class } from "@/components/lib/class/comClass";
export default function HtdflPrfmncSttnPopup({
htdflForm,
isModal,
}: {
htdflForm: customHtdflBdst;
isModal: boolean;
}) {
const { code, htdflBdstUpdate, htdflBdstReg } = useWrStore((state) => state);
const defaultValues: z.infer<typeof hdtflZod> = hdtflZod.parse({});
const form = useForm<z.infer<typeof hdtflZod>>({
resolver: zodResolver(hdtflZod),
defaultValues: defaultValues,
});
const onSubmit = (values: z.infer<typeof hdtflZod>) => {
if (isModal) {
htdflBdstReg(values, '41');
} else {
htdflBdstUpdate(values, '41');
}
};
const cleanHtdflData = (raw: any): z.infer<typeof hdtflZod> => {
return {
...raw,
frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null),
archChgPrmsnDt: raw?.archChgPrmsnDt
? new Date(raw?.archChgPrmsnDt)
: null,
chgBuilding: (raw?.chgBuilding || []).map((item: any) => ({
...item,
archChgPrmsnDt:
item?.archChgPrmsnDt === null
? new Date()
: new Date(item?.archChgPrmsnDt),
bdstChgNm: item?.bdstChgNm ?? "", // null → ""
})),
reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({
...item,
instlCmptnDt: new Date(item?.instlCmptnDt),
htdflObj: {
...item?.htdflObj,
},
})),
};
};
useEffect(() => {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
useEffect(() => {
if (hdtflZod) form.reset(cleanHtdflData(htdflForm));
}, [htdflForm]);
return (
<div className="!mx-[1.6rem] !p-0r">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
{/* <div className="flex justify-between items-center mx-3"> */}
<div>
<div className="grid grid-cols-12 flex items-center card-header">
<h1 className="text-2xl font-medium text-label col-span-10">온배수 시설 정보</h1>
</div>
</div>
{isModal && htdflForm != undefined ? <BdstReg form={form} /> : <></>}
{isModal ? (
<h1 className="text-xl col-span-10 mt-4">
온배수 시설 정보{" "}
</h1>
) : (
<></>
)}
<div className="grid grid-cols-6 gap-2 mt-4">
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.etgtType"
render={({ field }) => (
<FormUi label="발전유형(코드정비후 수정)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.fclCpct" // 이름 이상함.
render={({ field }) => (
<FormUi label="설비용량">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.plwtYn"
render={({ field }) => (
<FormUi label="담수화 여부">
<SelectBox
className={""}
label={""}
selectClass={selectClass}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className=" my-4">
<h1 className={formH1Class}>
온배수 시설 운영현황
</h1>
</div>
<div className="grid grid-cols-10 gap-1 mt-4">
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.orgwtWtritSpt"
render={({ field }) => (
<FormUi label="원수취수 지점(코드정비후 수정)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
value={field.value ?? 0}
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.orgwtOcrnProcs"
render={({ field }) => (
<FormUi label="원수발생 공정">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.wtritEntrcTp"
render={({ field }) => (
<FormUi label="취수구 온도">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.amtgnr"
render={({ field }) => (
<FormUi label="발생량(m³/년)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.prcsProcsYn"
render={({ field }) => (
<FormUi label="처리공정">
<SelectBox
className={""}
label={""}
selectClass={selectClass}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.prcsFcltCpct"
render={({ field }) => (
<FormUi label="처리시설 용량(m³/년)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.fmYn"
render={({ field }) => (
<FormUi label="유량계유무">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.utztnCpct"
render={({ field }) => (
<FormUi label="이용량(m³/년)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.utztnUsg"
render={({ field }) => (
<FormUi label="이용용도">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className=" my-2">
<h1 className={formH1Class}>
요구수질
</h1>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.grssClfg"
render={({ field }) => (
<FormUi label="총대장균군">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.tn"
render={({ field }) => (
<FormUi label="총질소">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.combResdch"
render={({ field }) => (
<FormUi label="결합잔류염소">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6 ">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.tph"
render={({ field }) => (
<FormUi label="총인">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.trbt"
render={({ field }) => (
<FormUi label="탁도">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.ph"
render={({ field }) => (
<FormUi label="수소이온농도">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.bod"
render={({ field }) => (
<FormUi label="생물학적 산소요구량">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.clIonQnt"
render={({ field }) => (
<FormUi label="염화물">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.smll"
render={({ field }) => (
<FormUi label="냄새">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.cdtnv"
render={({ field }) => (
<FormUi label="전기전도도">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.crmty"
render={({ field }) => (
<FormUi label="색도">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.emsnExitTp"
render={({ field }) => (
<FormUi label="배출구온도">
<Input
className={'w-full !text-xl'}
placeholder="10"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
</form>
</Form>
</div>
);
}
+160
View File
@@ -0,0 +1,160 @@
"use client";
import Gis from "@common/business_components/ui/map/Gis";
import { useEffect, useRef, useState, useCallback } from "react";
import FeatureClickEventPopup from "../featureClickEventPopup";
import { mapService } from "../service";
import { MapSearchBar } from "../mapSearchBar";
import NewWindowPopup from "../newWindowPopup";
import GisApp from "@common/business_components/ui/map/lib/gis.core";
const Map = () => {
const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정
const [selectedFeature, setSelectedFeature] = useState(null); // 선택된 피처 상태
const gisMaps = useRef<any | null>(null);
const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태
const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터
const [checkedLayers, setCheckedLayers] = useState<string[]>([]);
const [checkedLayersData, setCheckedLayersData] = useState<any[]>([]);
const fetchData = async () => {
try {
const response = await mapService.getLyrStngs();
setTreeData(response); // API 응답 저장
} catch (error) {
console.error("데이터 불러오기 실패:", error);
}
};
// 팝업 닫기 핸들러를 useCallback으로 메모이제이션
const handlePopupClose = useCallback(() => {
setIsOpen(false);
setFeatureData(null);
}, []);
// 컴포넌트 마운트 시 1회 실행
useEffect(() => {
fetchData();
}, []);
// 체크된 레이어 정보를 받는 핸들러
const handleCheckedLayersChange = (checkedIds: string[], checkedNodesData: any[]) => {
setCheckedLayers(checkedIds);
setCheckedLayersData(checkedNodesData);
};
// SearchModal, GisContent 등에서 호출
const onFeatureSelect = (data: any) => {
let map = gisMaps.current;
if (!map) {
console.warn("지도가 초기화되지 않았습니다.");
return;
}
// 항상 같은 select 객체 사용
const select = GisApp.Module.select as any;
if (!select) return;
// 1. 기존 선택 초기화
select.resetSelectedFeature();
// 2. data가 OpenLayers Feature 인스턴스인지 확인
let foundFeature = null;
if (
data &&
typeof data.getGeometry === "function" &&
typeof data.setStyle === "function"
) {
// 이미 Feature 인스턴스라면 그대로 사용
foundFeature = data;
} else {
// 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용
const featureId = data.fid || data.ftrIdn || data.id;
map
.getLayers()
.getArray()
.forEach((layer: any) => {
const source = layer.getSource?.();
if (source?.getFeatureById) {
const feature = source.getFeatureById(String(featureId).trim());
if (feature) foundFeature = feature;
}
});
}
// 3. 피처 선택
if (foundFeature) {
let geometry = foundFeature.getGeometry();
if (geometry) {
let extent = geometry.getExtent();
map.getView().fit(extent, {
duration: 300,
padding: [50, 50, 50, 50],
maxZoom: 16,
constrainResolution: true,
nearest: true,
});
}
select.selectFeature(foundFeature, true);
} else {
console.warn("OpenLayers Feature를 찾을 수 없습니다:", data);
}
setFeatureData(data);
setIsOpen(true);
setIsSearchModalOpen(false);
};
// MapSearchBar 래퍼 컴포넌트
// const CustomMapSearchBar = (props: any) => {
// return (
// <MapSearchBar
// {...props}
// searchType="공급처 후보지 조회"
// showApplyButton={false}
// />
// );
// };
return (
<div className="bg-container">
<Gis
treeData={treeData}
isSearch={false}
gisMaps={gisMaps}
isMiniMap={false}
searchView={undefined}
// searchView={(props: any) => (
// <MapSearchBar
// searchType="공급처 후보지 조회"
// showApplyButton={false}
// checkedLayers={checkedLayers}
// checkedLayersData={checkedLayersData}
// {...props}
// />
// )
// }
detailView={(props: any) => (
<NewWindowPopup
onClose={handlePopupClose} // 메모이제이션된 함수 사용
checkedLayers={checkedLayers}
checkedLayersData={checkedLayersData}
{...props}
/>
)}
onFeatureSelect={undefined}
setIsOpen={setIsOpen}
setFeatureData={setFeatureData}
featureData={featureData}
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={handleCheckedLayersChange}
/>
</div>
);
};
export default Map;
+414
View File
@@ -0,0 +1,414 @@
// "use client"
// import { useWrStore } from "@/app/store";
// import SearchInputText from "@/components/ui/searchInput/searchInput";
// import type { EnvPartSelectDataType, ReturnType, SelectDataType } from "@common/business_components/ui";
// import { SelectBox } from "@common/business_components/ui";
// import { Button, Input, Label } from "@common/components/ui";
// import React, { useEffect, useMemo, useRef, useState } from "react";
// import { MapSearchFormLeft } from "./mapSearchForm";
// // 건물 검색 타입 정의
// interface BuildingInfo {
// bdstInfoPk: number;
// ctpvCd: number;
// sggCd: number;
// daddr: string;
// bdstNm: string;
// frstArchPrmsnDt: string;
// archArea: number;
// ctpvNm: string | null;
// sggNm: string | null;
// lat: number | null;
// lot: number | null;
// regDt: string;
// rgtr: string | null;
// mdfcnDt: string;
// mdfr: string | null;
// }
// type SearchBaseType = {
// searchType: string;
// selectSidoData?: SelectDataType[] | [];
// selectSigunData?: SelectDataType[] | [];
// selectRegionData?: SelectDataType[] | [];
// selectUnitWaterShedData?: SelectDataType[] | [];
// selectMiddleRegionData?: SelectDataType[] | [];
// selectEnvPartData?: EnvPartSelectDataType[] | [];
// onFeatureSelect: () => void;
// btnClass?: string;
// ref?: React.RefObject<ReturnType>;
// isAdmin?: boolean;
// leftClassName?: string;
// rightClassName?: string;
// showQueryButton?: boolean;
// showInitButton?: boolean;
// showApplyButton?: boolean;
// checkedLayers?: string[];
// checkedLayersData?: any[];
// }
// const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`;
// const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`);
// interface SearchModalProps {
// open: boolean;
// onOpenChange: (open: boolean) => void;
// onFeatureSelect: (row: any) => void;
// }
// interface SelectedDataTypeWithSggNm {
// text: string;
// val: string;
// sggNm: string;
// }
// export function MapSearchBar({
// searchType,
// selectSidoData,
// selectSigunData,
// selectRegionData,
// selectUnitWaterShedData,
// selectMiddleRegionData,
// selectEnvPartData,
// btnClass,
// onFeatureSelect,
// isAdmin = false,
// leftClassName,
// rightClassName,
// showQueryButton = true,
// showInitButton = true,
// showApplyButton = true,
// checkedLayers = [],
// checkedLayersData = [],
// }: SearchBaseType) {
// // 로딩바
// const [isLoading, setIsLoading] = useState(false);
// // 상세 정보 조회를 위한 상태 추가
// const [popupFeature, setPopupFeature] = useState<any>(null);
// const [isChHead, setIsChHead] = useState(false);
// const ref = useRef<ReturnType>({} as ReturnType);
// // zustand store에서 건물 검색 함수와 데이터 가져오기
// const {
// code,
// bdst,
// findBdst,
// wrMap,
// buildingSearchRes,
// buildingNmSearch,
// buildingNmSearchMultiple
// } = useWrStore((state) => state);
// const [selectedBuilding, setSelectedBuilding] = useState<BuildingInfo | null>(null);
// const [buildingSearchText, setBuildingSearchText] = useState("");
// const [buildingList, setBuildingList] = useState<BuildingInfo[]>([]);
// // 전체 목록
// const [partValue, setPartValue] = useState<string>("A");
// const [sidoValue, setSidoValue] = useState<string>("0");
// const [sigunValue, setSigunValue] = useState<string>("0");
// const [regionValue, setRegionValue] = useState<string>("0");
// const [untitWaterShedValue, setUntitWaterShedValue] = useState<string>("0");
// const [middleRegionValue, setMiddleRegionValue] = useState<string>("0");
// const [envPartValue, setEnvPartValue] = useState<string>("0");
// const [resetTrigger, setResetTrigger] = useState(false);
// const [sidoText, setSidoText] = useState<string>("0");
// const [sigunText, setSigunText] = useState<string>("0");
// // 공급처 후보지 조회 필터 목록
// const [selectedSido, setSelectedSido] = useState<SelectDataType>({ text: "전체", val: '0' });
// const [selectedSigun, setSelectedSigun] = useState<SelectDataType>({ text: "전체", val: '0' });
// const [selectedFclt, setSelectedFclt] = useState<SelectedDataTypeWithSggNm>({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태
// // 전체 시설
// let allFclt: SelectedDataTypeWithSggNm[] = [{ text: "전체", val: '0', sggNm: '전체' }];
// const [selectedDong, setSelectedDong] = useState<SelectedDataTypeWithSggNm>({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태 // 행정동
// const filteredSigunData = useMemo(() => {
// if (!selectedSido || selectedSido.val === '0' || !selectedSido.val) return [{ text: "전체", val: '0' }];
// return [
// { text: "전체", val: '0' },
// ...code.sggCd.filter((item: { val: string; }) => item.val.substring(0, 2) === selectedSido.val.substring(0, 2))
// ];
// }, [selectedSido]);
// const filteredFcltData = useMemo(() => {
// if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }];
// return [
// { text: "전체", val: '0', sggNm: '전체' },
// ...allFclt.filter(item => item.sggNm === selectedSido.text)
// ];
// }, [selectedSigun]);
// const filteredDong = useMemo(() => {
// if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }];
// return [
// { text: "전체", val: '0', sggNm: '전체' },
// // ...code.dong.filter(item => item.sggNm === selectedSido.text)
// ];
// }, [selectedSigun]);
// // 조회 함수
// const queryVal = () => {
// // 46 : 하수처리시설
// // 47 : 하수재이용시설
// // 48 : 빗물이용시설
// // 49 : 중수도시설
// // 50 : 온배수시설
// console.log("조회버튼 클릭: ", checkedLayers);
// if (checkedLayers && checkedLayers.includes('48')) {
// // searchBuildings
// }
// }
// // 초기화 함수
// const initVal = () => {
// setPartValue("A");
// setSidoValue("0");
// setSigunValue("0");
// setRegionValue("0");
// setUntitWaterShedValue("0");
// setMiddleRegionValue("0");
// setEnvPartValue("0");
// setSidoText("전체");
// setSigunText("전체");
// setSelectedSido({ text: "전체", val: '0' });
// setSelectedSigun({ text: "전체", val: '0' });
// setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' });
// setSelectedDong({ text: "전체", val: '0', sggNm: '전체' });
// setResetTrigger(true);
// };
// // 적용 버튼
// const applyVal = () => {
// onFeatureSelect();
// };
// useEffect(() => {
// if (resetTrigger) {
// setResetTrigger(false);
// }
// }, [resetTrigger]);
// // 시설 조회
// useEffect(() => {
// const fetchData = async () => {
// setIsLoading(true);
// if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) {
// allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]);
// setIsLoading(false);
// return;
// }
// try {
// const queryParamsFacility = new URLSearchParams({
// lyrTypeNm: 'tb_il_swage_prcs_fclt',
// page: '0',
// size: '100000',
// sort: 'string',
// });
// const cqlFiltersFacility: string[] = [];
// if (selectedSigun.text && selectedSigun.val !== '0') {
// cqlFiltersFacility.push(`sgg_nm ILIKE '%${selectedSigun.text}%'`);
// }
// if (cqlFiltersFacility.length > 0) {
// queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and '));
// }
// console.log("BaseLayout API URL for Facilities:", `v1/fclts?${queryParamsFacility.toString()}`);
// const facilityRes = await fetch(`${API_URL}/v1/fclts?${queryParamsFacility.toString()}`, {
// method: "GET",
// headers: { accept: "application/json" }
// }).then(res => res.json());
// const facilities = (facilityRes.data || []).map((item: any) => ({
// text: item.swtrmtFcltNm,
// val: item.fid,
// sggNm: item.sggNm
// }));
// allFclt = (facilities);
// } catch (error) {
// console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error);
// allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]);
// } finally {
// setIsLoading(false);
// }
// };
// fetchData();
// }, [selectedSigun]);
// useEffect(() => {
// if (ref && ref.current) {
// const customRef = ref.current;
// customRef.partValue = partValue;
// customRef.sidoValue = sidoValue;
// customRef.sigunValue = sigunValue;
// customRef.regionValue = regionValue;
// customRef.unitWaterShedValue = untitWaterShedValue;
// customRef.middleRegionValue = middleRegionValue;
// customRef.envPartValue = envPartValue;
// customRef.sidoText = selectedSido.text;
// customRef.sigunText = selectedSigun.text;
// }
// }, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, ref]);
// // buildingSearchText가 변경될 때 API 호출
// useEffect(() => {
// if (buildingSearchText && buildingSearchText.length > 1) {
// if (checkedLayers && checkedLayers.length > 0) {
// // 다중 레이어 검색
// // buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", buildingSearchText);
// buildingNmSearch("RNB", selectedSigun.val, buildingSearchText);
// } else {
// // 단일 레이어 검색
// // buildingNmSearch(checkedLayers[0], selectedSigun.val, buildingSearchText);
// }
// } else {
// // 검색 텍스트가 짧으면 리스트 초기화
// if (checkedLayers && checkedLayers.length > 0) {
// // buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", "");
// } else {
// buildingNmSearch("RNB", selectedSigun.val || "", "");
// }
// }
// }, [buildingSearchText]);
// return (
// <div className="absolute left-1/2 -translate-x-1/2 bg-white flex items-center justify-center p-4 rounded-md shadow-lg w-auto min-w-fit">
// <div className={leftClassName ? leftClassName : `flex items-center justify-center space-x-2 `}>
// <div className="flex gap-8 items-center">
// {searchType == "공급처 후보지 조회" && (
// <>
// <SelectBox
// className="flex items-center"
// label={'시도'}
// selectArray={code.cityCd}
// onSelectValue={(val) => {
// const selected = code.cityCd.find((item: { val: string | number; }) => (item.val === val));
// setSelectedSido(selected || { text: "전체", val: '0' });
// setSelectedSigun({ text: "전체", val: '0' });
// }}
// value={selectedSido.val}
// selectClass={'w-32'}
// />
// <SelectBox
// className="flex items-center"
// label={'시군구'}
// selectArray={filteredSigunData}
// onSelectValue={(val) => {
// const selected = filteredSigunData.find(item => (item.val === val));
// setSelectedSigun(selected || { text: "전체", val: '0' });
// setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' });
// }}
// value={selectedSigun.val}
// selectClass={'w-32'}
// />
// <div className="flex items-center">
// <Label className="w-20 text-xl text-gray-600">시설명</Label>
// <div className="w-64">
// <SearchInputText
// label="시설명"
// initValue={buildingSearchText}
// setValue={(searchNm?: string) => {
// setBuildingSearchText(searchNm || "");
// }}
// setRtnData={function (rtnData: any): void {
// // 선택된 건물 데이터 처리
// // if (rtnData && rtnData.bdstInfoPk) {
// // setSelectedBuilding(rtnData);
// // setBuildingSearchText(rtnData.bdstNm || "");
// // }
// }}
// searchList={buildingSearchRes || []}
// />
// </div>
// </div>
// </>
// )}
// {searchType == "우리동네가뭄지도보기" && (
// <>
// <SelectBox
// className="flex items-center"
// label={'시도'}
// selectArray={code.cityCd}
// onSelectValue={(val) => {
// const selected = code.cityCd.find((item: { val: string | number; }) => (item.val === val));
// setSelectedSido(selected || { text: "전체", val: '0' });
// setSelectedSigun({ text: "전체", val: '0' });
// }}
// value={selectedSido.val}
// selectClass={'w-44'}
// />
// <SelectBox
// className="flex items-center"
// label={'시군구'}
// selectArray={filteredSigunData}
// onSelectValue={(val) => {
// const selected = filteredSigunData.find(item => (item.val === val));
// setSelectedSigun(selected || { text: "전체", val: '0' });
// setSelectedDong({ text: "전체", val: '0', sggNm: '전체' });
// }}
// value={selectedSigun.val}
// selectClass={'w-32'}
// />
// <SelectBox
// className="flex items-center mr-8"
// label={'행정동'}
// selectArray={filteredDong}
// onSelectValue={(val) => {
// const selected = filteredDong.find(item => (item.val === val));
// setSelectedDong(selected || { text: "전체", val: '0', sggNm: '전체' });
// }}
// value={selectedDong.val}
// selectClass={'w-32'}
// />
// </>
// )}
// {searchType == "지도보기" && (
// <div className="flex items-center gap-2 w-[800px]">
// <div className="flex min-w-0"> {/* SearchFormLeft 래퍼 */}
// <MapSearchFormLeft
// isShowPart={true}
// selectSidoData={code.cityCd}
// selectSigunData={filteredSigunData}
// selectRegionData={code.rvbsnCd}
// selectUnitWaterShedData={selectUnitWaterShedData}
// selectMiddleRegionData={selectMiddleRegionData}
// selectEnvPartData={selectEnvPartData}
// setRtnPartValue={setPartValue}
// setRtnSidoValue={setSidoValue}
// setRtnSigunValue={setSigunValue}
// setRtnRegionValue={setRegionValue}
// setRtnUnitWaterShedValue={setUntitWaterShedValue}
// setRtnMiddleRegionValue={setMiddleRegionValue}
// setRtnEnvPartValue={setEnvPartValue}
// setRtnEnvPartSidoValue={setSidoValue}
// setRtnEnvPartSigunValue={setSigunValue}
// setRtnSidoText={setSidoText}
// setRtnSigunText={setSigunText}
// resetTrigger={resetTrigger}
// isAdmin={isAdmin}
// children={undefined}
// />
// </div>
// <Label className="w-auto text-xl text-gray-600 whitespace-nowrap">시설명</Label>
// <div className="w-44">
// <Input className="mr-4" />
// </div>
// </div>
// )}
// </div>
// {/* 버튼 부분 */}
// <div className="flex items-center justify-center gap-4">
// {showQueryButton && <Button className={btnClass} onClick={queryVal}>조회</Button>}
// {showInitButton && <Button className={btnClass} onClick={initVal}>초기화</Button>}
// {showApplyButton && <Button className={btnClass} onClick={applyVal}>적용</Button>}
// </div>
// </div>
// </div>
// )
// }
+340
View File
@@ -0,0 +1,340 @@
"use client"
import React, { useEffect, useState } from 'react';
import type { SelectDataType } from '@common/business_components/ui/search_input_select';
import { SelectBox } from '@common/business_components/ui/select';
/**
* 검색 폼 왼쪽 컴포넌트 타입
*
* @param isShowPart 구분 조회 여부
* @param children 자식 컴포넌트
* @param selectSidoData 시도 데이터
* @param selectSigunData 시군구 데이터
* @param selectRegionData 유역 데이터
* @param selectUnitWaterShedData 단위유역 데이터
* @param selectMiddleRegionData 중권역 데이터
* @param selectEnvPartData 환경청 데이터
* @param selectEnvPartSidoData 환경청 시도 데이터
* @param selectEnvPartSigunData 환경청 시군구 데이터
* @param setRtnPartValue 부분 값 변경 핸들러
*/
type SearchFormLeftProps = {
isShowPart?: boolean | undefined;
children: React.ReactNode;
selectSidoData?: SelectDataType[] | [];
selectSigunData?: SelectDataType[] | [];
selectRegionData?: SelectDataType[] | [];
selectUnitWaterShedData?: SelectDataType[] | [];
selectMiddleRegionData?: SelectDataType[] | [];
selectEnvPartData?: EnvPartSelectDataType[] | [];
selectEnvPartSidoData?: SelectDataType[] | [];
selectEnvPartSigunData?: SelectDataType[] | [];
setRtnPartValue?: (value: string) => void;
setRtnSidoValue?: (value: string) => void;
setRtnSigunValue?: (value: string) => void;
setRtnRegionValue?: (value: string) => void;
setRtnUnitWaterShedValue?: (value: string) => void;
setRtnMiddleRegionValue?: (value: string) => void;
setRtnEnvPartValue?: (value: string) => void;
setRtnEnvPartSidoValue?: (value: string) => void;
setRtnEnvPartSigunValue?: (value: string) => void;
setRtnSidoText?: (value: string) => void;
setRtnSigunText?: (value: string) => void;
resetTrigger?: boolean;
isAdmin?: boolean;
}
export type EnvPartSelectDataType = SelectDataType & {
adstDliStrCd: string,
}
const selectPartData: SelectDataType[] = [
{ text: "행정별", val: 'A' },
{ text: "유역별", val: 'R' },
{ text: "환경청별", val: 'G' },
];
function MapSearchFormLeft({ isShowPart = false, children, selectSidoData = [], selectSigunData = [], selectRegionData = [], selectUnitWaterShedData = [], selectMiddleRegionData = [], selectEnvPartData = [], setRtnPartValue, setRtnSidoValue, setRtnSidoText, setRtnSigunValue, setRtnSigunText, setRtnRegionValue, setRtnUnitWaterShedValue, setRtnMiddleRegionValue, setRtnEnvPartValue, setRtnEnvPartSidoValue, setRtnEnvPartSigunValue, resetTrigger = false, isAdmin = false }: SearchFormLeftProps) {
/******************선택된 데이터******************/
const [partValue, setPartValue] = useState<string>('A');
//행정별 시도 시군구
const [sidoValue, setSidoValue] = useState<string>('0');
const [sigunValue, setSigunValue] = useState<string>('0');
const [sidoText, setSidoText] = useState<string>('');
const [sigunText, setSigunText] = useState<string>('');
//유역별
const [regionValue, setRegionValue] = useState<string>('0');
const [unitWaterShedValue, setUnitWaterShedValue] = useState<string>('0');
const [middleRegionValue, setMiddleRegionValue] = useState<string>('0');
//환경청별 시도 시군구
const [envPartValue, setEnvPartValue] = useState<string>('0');
// 환경청
const [filteredEnvPartSigunDataSelected, setFilteredEnvPartSigunDataSelected] = useState<SelectDataType[]>([]);
/****************** 셀렉트 리스트 데이터. ******************/
// 조회된 조건에 맞는 시도 시군 유역
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
const [filteredUnitWaterShedData, setFilteredUnitWaterShedData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
const [filteredMiddleRegionData, setFilteredMiddleRegionData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
// 조회된 조건에 맞는 환경청 시도 시군
const [filteredEnvPartData, setFilteredEnvPartData] = useState<EnvPartSelectDataType[]>([{ text: "전체", val: '0', adstDliStrCd: '' }]);
const [filteredEnvPartSidoData, setFilteredEnvPartSidoData] = useState<EnvPartSelectDataType[]>([{ text: "전체", val: '0', adstDliStrCd: '' }]);
const [filteredEnvPartSigunData, setFilteredEnvPartSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
// 시군구 세팅
const initializeSigunData = (sidoCd: string) => {
if (sidoCd === '0') {
setSigunValue('0');
setFilteredSigunData([{ text: "전체", val: '0' }]);
} else {
const newFilteredData = [
{ text: "전체", val: '0' },
...selectSigunData.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd)
];
setSigunValue('0');
setFilteredSigunData(newFilteredData);
}
}
// 단위유역 세팅
const initializeUnitWaterShedData = (regionCd: string) => {
if (regionCd === '0') {
setUnitWaterShedValue('0');
setFilteredUnitWaterShedData([{ text: "전체", val: '0' }]);
} else {
const newFilteredData = [
{ text: "전체", val: '0' },
...selectUnitWaterShedData.filter((item: SelectDataType) => item.val.substring(0, 1) === regionCd)
];
setUnitWaterShedValue('0');
setFilteredUnitWaterShedData(newFilteredData);
}
}
// 중권역 세팅
const initializeMiddleRegionData = (unitWaterShedCd: string) => {
if (unitWaterShedCd === '0') {
setMiddleRegionValue('0');
setFilteredMiddleRegionData([{ text: "전체", val: '0' }]);
} else {
const newFilteredData = [
{ text: "전체", val: '0' },
...selectMiddleRegionData.filter((item: SelectDataType) => item.val.substring(0, 2) === unitWaterShedCd)
];
setMiddleRegionValue('0');
setFilteredMiddleRegionData(newFilteredData);
}
}
// 환경청 세팅 - 시도
const initializeCityDataByEnvPartSido = (envPartCd: string) => {
const foundEnvPart = selectEnvPartData.find((item: EnvPartSelectDataType) => item.val === envPartCd);
const cityArray = foundEnvPart?.adstDliStrCd.toString().split('|').join('').split(',');
// 시군구 데이터
const envSigunData = selectSigunData.filter((item: SelectDataType) => cityArray?.includes(item.val));
// 환경청 시도 데이터
const sidoArray = [...new Set(envSigunData.map((item: SelectDataType) => item.val.substring(0, 2)))];
const envSidoData = selectSidoData.filter((item: SelectDataType) => sidoArray?.includes(item.val));
setFilteredEnvPartSigunDataSelected(envSigunData);
setFilteredEnvPartSidoData([
{ text: "전체", val: '0', adstDliStrCd: '' },
...envSidoData.map((item: SelectDataType) => ({ text: item.text, val: item.val, adstDliStrCd: '' }))
]);
}
// 환경청 세팅 - 시군구
const initializeCityDataByEnvPartSigun = (sidoCd: string) => {
const envSigunData = filteredEnvPartSigunDataSelected.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd);
setFilteredEnvPartSigunData([
{ text: "전체", val: '0' },
...envSigunData.map((item: SelectDataType) => ({ text: item.text, val: item.val }))
]);
}
const onSelectValue = (val: string, type: string) => {
let selectedSido;
let selectedSigun;
let selectedEnvPartSido;
let selectedEnvPartSigun;
switch (type) {
case 'part':
setPartValue(val);
if (setRtnPartValue) {
setRtnPartValue(val);
}
// 구분
if (val == 'R') {
initializeMiddleRegionData('0');
initializeUnitWaterShedData('0');
} else if (val == 'G') {
setFilteredEnvPartData([
{ text: "전체", val: '0', adstDliStrCd: '' },
...selectEnvPartData.filter((item: EnvPartSelectDataType) => item.val !== null && item.adstDliStrCd !== '')
]);
setEnvPartValue('0');
initializeCityDataByEnvPartSido('0');
} else {
initializeSigunData('0');
onSelectValue(selectSigunData[0]?.val || '0', 'sigun');
}
// 다른 selectbox 전체로 초기화
onSelectValue(selectSidoData[0]?.val || '0', 'sido');
onSelectValue(selectSigunData[0]?.val || '0', 'sigun');
break;
case 'sido':
setSidoValue(val);
selectedSido = selectSidoData.find(item => item.val === val);
setSidoText(selectedSido?.text || '');
if (setRtnSidoValue) {
setRtnSidoValue(val);
}
if (setRtnSidoText) {
setRtnSidoText(selectedSido?.text || '');
}
initializeSigunData(val);
onSelectValue(selectSigunData[0]?.val || '0', 'sigun');
break;
case 'sigun':
if (setRtnSigunValue) {
setRtnSigunValue(val);
}
setSigunValue(val);
selectedSigun = filteredSigunData.find(item => item.val === val);
if (setRtnSigunText) {
setRtnSigunText(selectedSigun?.text || '');
}
setSigunText(selectedSigun?.text || '');
break;
// 유역 세팅
case 'region':
setRegionValue(val);
if (setRtnRegionValue) {
setRtnRegionValue(val);
}
initializeUnitWaterShedData(val);
onSelectValue(selectUnitWaterShedData[0]?.val || '0', 'unitWaterShed');
break;
case 'unitWaterShed':
setUnitWaterShedValue(val);
if (setRtnUnitWaterShedValue) {
setRtnUnitWaterShedValue(val);
}
initializeMiddleRegionData(val);
onSelectValue(selectMiddleRegionData[0]?.val || '0', 'middleRegion');
break;
case 'middleRegion':
setMiddleRegionValue(val);
if (setRtnMiddleRegionValue) {
setRtnMiddleRegionValue(val);
}
break;
// 환경청 세팅
case 'envPart':
setEnvPartValue(val);
if (setRtnEnvPartValue) {
setRtnEnvPartValue(val);
}
initializeCityDataByEnvPartSido(val);
onSelectValue(filteredEnvPartSidoData[0]?.val || '0', 'envPartSido');
break;
case 'envPartSido':
setSidoValue(val);
selectedEnvPartSido = filteredEnvPartSidoData.find(item => item.val === val);
setSidoText(selectedEnvPartSido?.text || '');
if (setRtnEnvPartSidoValue) {
setRtnEnvPartSidoValue(val);
}
initializeCityDataByEnvPartSigun(val);
onSelectValue(filteredEnvPartSigunData[0]?.val || '0', 'envPartSigun');
break;
case 'envPartSigun':
setSigunValue(val);
selectedEnvPartSigun = filteredEnvPartSigunData.find(item => item.val === val);
setSigunText(selectedEnvPartSigun?.text || '');
if (setRtnEnvPartSigunValue) {
setRtnEnvPartSigunValue(val);
}
break;
}
}
// 리셋 함수
const resetAllValues = () => {
setPartValue('A');
setSidoValue('0');
setSigunValue('0');
setRegionValue('0');
setUnitWaterShedValue('0');
setMiddleRegionValue('0');
setEnvPartValue('0');
setFilteredSigunData([{ text: "전체", val: '0' }]);
setFilteredUnitWaterShedData([{ text: "전체", val: '0' }]);
setFilteredMiddleRegionData([{ text: "전체", val: '0' }]);
setFilteredEnvPartData([{ text: "전체", val: '0', adstDliStrCd: '' }]);
setFilteredEnvPartSidoData([{ text: "전체", val: '0', adstDliStrCd: '' }]);
setFilteredEnvPartSigunData([{ text: "전체", val: '0' }]);
if (setRtnPartValue) setRtnPartValue('A');
if (setRtnSidoValue) setRtnSidoValue('0');
if (setRtnSigunValue) setRtnSigunValue('0');
if (setRtnRegionValue) setRtnRegionValue('0');
if (setRtnUnitWaterShedValue) setRtnUnitWaterShedValue('0');
if (setRtnMiddleRegionValue) setRtnMiddleRegionValue('0');
if (setRtnEnvPartValue) setRtnEnvPartValue('0');
if (setRtnEnvPartSidoValue) setRtnEnvPartSidoValue('0');
if (setRtnEnvPartSigunValue) setRtnEnvPartSigunValue('0');
};
// 리셋 트리거 변경 감지
useEffect(() => {
if (resetTrigger) {
resetAllValues();
// 리셋 후 트리거를 false로 설정
if (setRtnPartValue) {
setRtnPartValue('0');
}
}
}, [resetTrigger]);
return (
isShowPart ? (
<div className="flex gap-5">
<SelectBox className="w-auto whitespace-nowrap" label={'구분'} selectArray={isAdmin ? selectPartData.slice(0, 1) : selectPartData} onSelectValue={(val) => onSelectValue(val.toString(), 'part')} value={partValue} selectClass={'w-32'} />
{partValue === 'R' ? (
<>
<SelectBox className="w-auto whitespace-nowrap" label={'유역'} selectArray={selectRegionData} onSelectValue={(val) => onSelectValue(val.toString(), 'region')} value={regionValue} selectClass={'w-44'} />
<SelectBox className="w-auto whitespace-nowrap" label={'단위유역'} selectArray={filteredUnitWaterShedData} onSelectValue={(val) => onSelectValue(val.toString(), 'unitWaterShed')} value={unitWaterShedValue} selectClass={'w-44'} />
<SelectBox className="w-auto whitespace-nowrap" label={'중권역'} selectArray={filteredMiddleRegionData} onSelectValue={(val) => onSelectValue(val.toString(), 'middleRegion')} value={middleRegionValue} selectClass={'w-44'} />
</>
) : partValue === 'G' ? (
<>
<SelectBox className="w-auto whitespace-nowrap" label={'환경청'} selectArray={filteredEnvPartData} onSelectValue={(val) => onSelectValue(val.toString(), 'envPart')} value={envPartValue} selectClass={'w-44'} />
<SelectBox className="w-auto whitespace-nowrap" label={'시도'} selectArray={filteredEnvPartSidoData} onSelectValue={(val) => onSelectValue(val.toString(), 'envPartSido')} value={sidoValue} selectClass={'w-44'} />
<SelectBox className="w-auto whitespace-nowrap" label={'시군구'} selectArray={filteredEnvPartSigunData} onSelectValue={(val) => onSelectValue(val.toString(), 'envPartSigun')} value={sigunValue} selectClass={'w-44'} />
</>
) : (
<>
<SelectBox className="w-auto whitespace-nowrap" label={'시도'} selectArray={selectSidoData} onSelectValue={(val) => onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={'w-44'} />
<SelectBox className="w-auto whitespace-nowrap" label={'시군구'} selectArray={filteredSigunData} onSelectValue={(val) => onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={'w-44'} />
</>
)}
</div>
) : (
<div className="grid grid-cols-12 gap-5 w-11/12 m-4">
{children}
</div>
)
)
}
export { MapSearchFormLeft };
+382
View File
@@ -0,0 +1,382 @@
import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import type { customPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
import { excelDownload } from "@/components/lib/func/commom";
import { Title, UiTable, UITab, ContentsDiv } from '@common/business_components';
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
import { Button } from "@common/components/ui";
import React, { useCallback, useState, useEffect } from 'react';
import NewWindowPortal from '@/app/map/newWindowPortal';
import PbnstPrfmncSttnPopup from "@/app/map/pbnstPrfmncSttnPopup";
import { PbnstSttnModal } from "@/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal";
import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type";
import type { chgBuilding, customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import RainPrfmncSttnPopup from "@/app/map/rainPrfmncSttnPopup";
import RswtrPrfmncsSttnPopup from "@/app/map/rswtrPrfmncSttnPopup";
import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
import HtdflPrfmncSttnPopup from "@/app/map/htdflPrfmncSttnPopup";
import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg";
interface NewWindowPopupProps {
featureData: any;
checkedLayers?: string[];
checkedLayersData?: any[];
onClose: () => void;
}
// 탭
const TABS = [
{ val: "0", text: "재이용시설1" },
{ val: "1", text: "재이용시설2" },
{ val: "2", text: "재이용시설n" },
];
// 데이터의 속성(key)을 사용자에게 보여줄 한글 이름으로 변환하기 위한 맵
// 실제 데이터에 맞게 이 부분을 확장해야 합니다.
const propertyNameMap: { [key: string]: string } = {
fid: '고유 ID',
fcltNm: '시설명',
opertDe: '운영일자',
addr: '주소',
// ... 필요한 만큼 속성 매핑 추가
};
const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
featureData,
checkedLayers = [],
checkedLayersData = [],
onClose
}) => {
const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "0");
// swcho : useState의 true, false 값 변경하여 목록, 상태 테스트 하였음
const [showDetailView, setShowDetailView] = useState(true); // 상세정보 화면 표시 상태 추가
// onClose 함수를 useCallback으로 메모이제이션
const memoizedOnClose = useCallback(() => {
onClose();
}, [onClose]);
// 하수재이용시설 변수
const {
code,
pbnstSttn,
pbnstSttnSearch,
pbnSttnInit,
getPbnStnDetail,
pbnNmSearch,
} = useWrStore((state) => state);
const [bdstTable, setBdstTable] = useState<WaterReuseFacility[]>();
const [pageNation, setPageNation] = useState(1);
const [pbnDetaileData, setPbnDetaileData] = useState<{
isModal: boolean;
data: customPbnst;
}>({ isModal: false, data: {} as customPbnst });
// 빗물재이용시설 변수
const { rainSttn, rainSttnSearch, rainSttnInit } = useWrStore(
(state) => state
);
const { rainSttnRes } = rainSttn;
const [rainTable, setRainTable] = useState<customRainBdst[]>();
const [rainBdstChgProps, setRainBdstChgProps] = useState<{
isOpen: boolean;
chBuilding: chgBuilding[];
}>({ isOpen: false, chBuilding: [] });
const [rainDetailModal, setRainDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false });
// 중수도시설 변수
const { rswtSttn, rswtSttnSearch, rswtSttnInit } = useWrStore(
(state) => state
);
const { rswtSttnRes } = rswtSttn;
const [rswtrTable, setRswtrTable] = useState<customRswtBdst[]>();
const [rswtrDetailModal, setRswtrDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false });
const [rswtrBdstChgProps, setRswtrBdstChgProps] = useState<{
isOpen: boolean;
chBuilding: chgBuilding[];
}>({ isOpen: false, chBuilding: [] });
// 온배수시설 변수
const { htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore((state) => state);
const { htdflSttnRes } = htdflSttn;
const [htdflTable, setHtdflTable] = useState<customHtdflBdst[]>();
const [htdflDetailModal, setHtdflDetailModal] = useState<{ isOpen: boolean, data: customHtdflBdst | undefined }>({ data: undefined, isOpen: false });
const [bdstChgProps, setBdstChgProps] = useState<{ isOpen: boolean, chBuilding: chgBuilding[] }>({ isOpen: false, chBuilding: [] });
if (!featureData) {
return null;
}
// 2. RuseMngTable에 필요한 tableData를 생성합니다. (단일 행 데이터)
const tableData = [featureData];
console.log("tableData", tableData);
// 물재이용시설 cellClick 이벤트 (상세정보)
const pbnstCellClick = (index: number | undefined) => {
if (
index !== undefined &&
pbnstSttn.pbnstSttnRes &&
pbnstSttn.pbnstSttnRes[index]
) {
const pbnstRuseFcltPk = pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk;
getPbnStnDetail(pbnstRuseFcltPk);
}
};
useEffect(() => {
setBdstTable(pbnstSttn.pbnstSttnRes);
}, [pbnstSttn.pbnstSttnRes]);
useEffect(() => {
if (pbnstSttn.pbnstDetail.pbnstRuseFcltPk) setShowDetailView(true);
}, [pbnstSttn.pbnstDetail]);
// 빗물재이용시설 cellClick 이벤트 (상세정보)
const rainCellClick = (index: number | undefined, headId: string) => {
if (
index !== undefined &&
rainTable !== undefined &&
headId !== 'chBuildingYn') {
setRainDetailModal({ isOpen: true, data: rainTable[index] })
} else if (headId === 'chBuildingYn' && index !== undefined && rainTable !== undefined) {
if (rainTable[index] && rainTable[index]?.chgBuilding[0]?.bdstChgNm !== null)
setRainBdstChgProps({ isOpen: true, chBuilding: rainTable[index].chgBuilding });
}
}
// 중수도시설 cellClick 이벤트 (상세정보)
const rswtCellClick = (index: number | undefined, headId: string) => {
if (headId !== 'chBuildingYn' && index !== undefined && rswtrTable !== undefined) {
setRswtrDetailModal({ isOpen: true, data: rswtrTable[index] })
} else if (headId === 'chBuildingYn' && index !== undefined && rswtrTable !== undefined) {
if (rswtrTable[index] && rswtrTable[index]?.chBuildingYn === 'Y') {
setRswtrBdstChgProps({ isOpen: true, chBuilding: rswtrTable[index].chgBuilding });
}
}
}
// 온배수시설 cellClick 이벤트 (상세정보)
const htdflCellClick = (index: number | undefined, headId: string) => {
if (
headId !== "chBuildingYn" &&
index !== undefined &&
htdflTable !== undefined
) {
setHtdflDetailModal({ isOpen: true, data: htdflTable[index] });
}
};
const excelClick = useCallback(() => {
// if (codeData) pbnstSttnSearch(1, "10000", codeData);
excelDownload(
[[]],
"하수재이용시설 현황",
"sample_pbnstSttn.xlsx",
"A1:U2"
);
}, [pbnstSttnSearch]);
// 체크된 레이어에 따른 조건부 컴포넌트 렌더링 함수
const renderConditionalContent = () => {
// 특정 레이어가 체크되었는지 확인
const hasWaterTreatmentLayer = checkedLayersData.some(layer =>
layer.data?.lyrNm?.includes('하수처리') || layer.data?.lyrNm?.includes('재이용시설')
);
// swcho : true, false 값 변경하여 시설 변경하여 테스트하였음
// 재이용시설
const isPbnst = true;
// 빗물이용시설
const isRain = false;
// 중수도시설
const isRswt = false;
// 온배수시설
const isHtdfl = false;
return (
<div className="mt-4">
{/* 체크된 레이어가 없는 경우 (재이용시설 목록) */}
{isPbnst && (
<>
{showDetailView ? (
// 재이용시설 상세
<>
<div className="flex justify-between items-center mb-4">
<Title title='하수재이용시설 상세정보' />
</div>
<div className="flex justify-between items-center mb-8">
<UITab
defaultValue={TABS[0]?.val}
tabList={TABS.map((tab) => ({
value: tab.val,
label: tab.text,
onClick: () => setActiveTab(tab.val),
}))}
children={undefined}
/>
</div>
<PbnstSttnModal
data={pbnDetaileData.data || ({} as customPbnst)}
titleVisible={false}
/>
<PbnstPrfmncSttnPopup isVisible={false} />
</>
) : (
// 재이용시설 목록
<>
<div className="flex justify-between items-center">
<Title title='하수재이용시설 현황' />
<div className="flex gap-2 mt-8">
<Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button>
</div>
</div>
<UiTable
headName={[]}
tableData={bdstTable || []}
children={
<HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} />
}
headlist={pbnstSttn.pbnstSttHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
pbnstCellClick(index);
}}
onPageChange={setPageNation}
/>
</>
)}
</>
)}
{/* 빗물이용시설 */}
{isRain && (
<>
{showDetailView ? (
// 빗물이용시설 상세
<>
<div className="flex justify-between items-center mb-4">
<Title title='빗물이용시설 상세정보' />
</div>
<RainPrfmncSttnPopup rainForm={rainDetailModal.data as customRainBdst} isModal={false} />
{/* TODO: 지도 점검 결과 현황 추가 할 것 */}
</>
) : (
// 빗물이용시설 목록
<>
<div className="flex justify-between items-center">
<Title title='빗물이용시설 현황' />
<div className="flex gap-2 mt-8">
<Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button>
</div>
</div>
<UiTable
headName={[]}
tableData={rainTable || []}
children={
<HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} />
}
headlist={rainSttn.rainSttHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
rainCellClick(index ?? undefined, headId)
}}
onPageChange={setPageNation}
/>
</>
)}
</>
)}
{/* 중수도시설 */}
{isRswt && (
<>
{showDetailView ? (
// 재이용시설 상세
<>
<div className="flex justify-between items-center mb-4">
<Title title='중수도시설 상세정보' />
</div>
<RswtrPrfmncsSttnPopup rswtForm={rswtrDetailModal.data as customRswtBdst} isModal={false} />
{/* TODO: 지도 점검 결과 현황 추가 할 것 */}
</>
) : (
// 재이용시설 목록
<>
<div className="flex justify-between items-center">
<Title title='중수도시설 현황' />
<div className="flex gap-2 mt-8">
<Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button>
</div>
</div>
<UiTable
headName={[]}
tableData={rswtrTable || []}
children={
<HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} />
}
headlist={rswtSttn.rswtSttHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
rswtCellClick(index, headId);
}}
onPageChange={setPageNation}
/>
</>
)}
</>
)}
{/* 온배수시설 */}
{isHtdfl && (
<>
{showDetailView ? (
// 재이용시설 상세
<>
<div className="flex justify-between items-center mb-4">
<Title title='온배수시설 상세정보' />
</div>
{/* <HtdflPrfmncSttnPopup htdflForm={htdflDetailModal.data as customHtdflBdst} isModal={false} /> */}
<HtdflPrfmncSttnPopup htdflForm={htdflDetailModal.data as customHtdflBdst} isModal={false} />
</>
) : (
// 재이용시설 목록
<>
<div className="flex justify-between items-center">
<Title title='온배수시설 현황' />
<div className="flex gap-2 mt-8">
<Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button>
</div>
</div>
<UiTable
headName={[]}
tableData={bdstTable || []}
children={
<HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} />
}
headlist={htdflSttn.htdflSttHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
htdflCellClick(index ?? undefined, headId);
}}
onPageChange={setPageNation}
/>
</>
)}
</>
)}
</div>
);
};
return (
<NewWindowPortal onClose={memoizedOnClose} title="시설 상세 정보">
{/* 체크된 레이어에 따른 조건부 렌더링 */}
{renderConditionalContent()}
</NewWindowPortal>
);
};
export default NewWindowPopup;
+90
View File
@@ -0,0 +1,90 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { createPortal } from 'react-dom';
// 현재 문서의 스타일(CSS)을 새 창으로 복사하는 함수
const copyStyles = (sourceDoc: Document, targetDoc: Document) => {
Array.from(sourceDoc.styleSheets).forEach(styleSheet => {
try {
if (styleSheet.cssRules) { // <style> 태그로 주입된 CSS
const newStyleEl = sourceDoc.createElement('style');
Array.from(styleSheet.cssRules).forEach(cssRule => {
newStyleEl.appendChild(sourceDoc.createTextNode(cssRule.cssText));
});
targetDoc.head.appendChild(newStyleEl);
} else if (styleSheet.href) { // <link> 태그로 연결된 CSS 파일
const newLinkEl = sourceDoc.createElement('link');
newLinkEl.rel = 'stylesheet';
newLinkEl.href = styleSheet.href;
targetDoc.head.appendChild(newLinkEl);
}
} catch (e) {
console.warn('스타일시트를 복사할 수 없습니다:', e);
}
});
};
interface NewWindowPortalProps {
children: React.ReactNode;
onClose: () => void;
title?: string;
}
const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, title = "Detail" }) => {
const [container, setContainer] = useState<HTMLElement | null>(null);
const newWindowRef = useRef<Window | null>(null);
const onCloseRef = useRef(onClose);
// onClose 함수를 ref로 저장하여 의존성 문제 해결
useEffect(() => {
onCloseRef.current = onClose;
}, [onClose]);
const handleUnload = useCallback(() => {
onCloseRef.current();
}, []);
useEffect(() => {
// 이미 창이 열려있으면 새로 열지 않음
if (newWindowRef.current && !newWindowRef.current.closed) return;
// 1. 새 창을 열고 참조를 저장합니다.
newWindowRef.current = window.open('', title, 'width=1400,height=1024,resizable=yes,scrollbars=yes');
if (newWindowRef.current) {
newWindowRef.current.document.title = title;
// 2. React 컴포넌트를 마운트할 컨테이너 div를 생성하여 새 창의 body에 추가합니다.
const div = newWindowRef.current.document.createElement('div');
newWindowRef.current.document.body.appendChild(div);
setContainer(div);
// 3. 메인 창의 스타일을 새 창으로 복사합니다. (매우 중요)
copyStyles(document, newWindowRef.current.document);
// 4. 사용자가 창을 닫을 때 부모에게 알리기 위한 이벤트 리스너를 설정합니다.
// const handleUnload = () => {
// onClose();
// };
newWindowRef.current.addEventListener('unload', handleUnload);
// 5. 컴포넌트가 사라질 때 창을 닫는 클린업 함수를 반환합니다.
return () => {
if (newWindowRef.current) {
newWindowRef.current.removeEventListener('unload', handleUnload);
if (!newWindowRef.current.closed) {
newWindowRef.current.close();
}
}
};
} else {
alert('팝업이 차단되었습니다. 팝업 차단을 해제하고 다시 시도해주세요.');
onCloseRef.current();
// onClose();
}
}, [onClose, title]);
// `createPortal`을 사용해 children을 새 창의 container div에 렌더링합니다.
return container ? createPortal(children, container) : null;
};
export default NewWindowPortal;
+596
View File
@@ -0,0 +1,596 @@
"use client";
import { useWrStore } from "@/app/store";
import { initValInUsage, initValOutUsage, subTotalObj, totalObj, type FcPrfType, type PrfType } from "@/app/store/prfmncReg/pbnstPrfmncReg/type";
import { CHART_FORM, ContentsDiv, MainChart, ScrollArea, SelectBox, UiTable, type ChartConfig, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useEffect, useRef, useState } from "react";
import { Toaster } from "react-hot-toast";
interface PbnstPrfmncSttnPopupProps {
isVisible?: boolean;
}
export default function PbnstPrfmncSttnPopup({ isVisible = true }: PbnstPrfmncSttnPopupProps) {
const { code, tableHeadMaker, pbnstPrfmncSttn, pbnstPrfmncInit, pbnstPrfmncSearch, prfmnc } = useWrStore((state) => state);
const { monthHeadList } = prfmnc
const { pbnstPrfHeadList, pbnstPrfmncRes, prfmncRes, rawData, selectYearList } = pbnstPrfmncSttn;
const [isChHead, setIsChHead] = useState(false);
const [fromDate, setFromDate] = useState<string>(String(new Date().getFullYear()));
const [toDate, setToDate] = useState<string>(String(new Date().getFullYear()));
const [year, setYear] = useState<string>(String(new Date().getFullYear()));
const [bdstTable, setbdstTable] = useState<FcPrfType[]>();
const ref = useRef<ReturnType>({} as ReturnType);
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>();
const [chartConfig, setChartConfig] = useState<ChartConfig>({});
const [chartData, setChartData] = useState<dnChartForm[]>([]);
const [dnInUsage, setDnInUsage] = useState<dnChartForm[]>([]);
const [dnInChartConfig, setDnInChartConfig] = useState<ChartConfig>({});
const [dnOutUsage, setDnOutUsage] = useState<dnChartForm[]>([]);
const [dnOutChartConfig, setDnOutChartConfig] = useState<ChartConfig>({});
const [barData, setBarData] = useState<barChartForm[]>([]);
const [barChartConfig, setBarChartConfig] = useState<ChartConfig>({});
const [prfData, setPrfData] = useState<any[]>();
const [lnInChartConfig, setLnInChartConfig] = useState<ChartConfig>({});
const [lnOutChartConfig, setLnOutChartConfig] = useState<ChartConfig>({});
const [inLineData, setInLineData] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
chartData3: lineChartDataProps[];
chartData4: lineChartDataProps[];
chartData5: lineChartDataProps[];
chartData6: lineChartDataProps[];
chartData7: lineChartDataProps[];
chartData8: lineChartDataProps[];
chartData9: lineChartDataProps[];
}>();
// const [lnInChartConfig, setLnInChartConfig] = useState<ChartConfig>({});
const [outLineData, setOutLineData] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
chartData3: lineChartDataProps[];
chartData4: lineChartDataProps[];
chartData5: lineChartDataProps[];
chartData6: lineChartDataProps[];
}>();
type dnChartForm = { label: string, val: number, fill?: string };
type barChartForm = { label: string, "장내용수": number, "장외용수": number };
type lineChartDataProps = { label: string, val: number, color: string };
const prfTableData = (prfList: PrfType[], year: string) => {
const inUsageData = initValInUsage;
const outUsageData = initValOutUsage;
const inSubtotal = subTotalObj;
const outSubtotal = subTotalObj;
console.log(' prfList ', prfList)
const prfFilter = prfList?.filter((e) => e.ym.slice(0, 4) === year)
if (prfList && prfFilter.length > 0) {
prfList?.forEach((prfItem) => {
const month = Number(prfItem?.ym?.slice(4)); // "MM"
const monthKey = `${month}월`;
inUsageData.forEach((inData: any) => {
const usageKey = inData.usage;
const value = (prfItem as any)[usageKey] ?? 0;
inData[monthKey] = value;
inData.total += value;
if (inSubtotal && usageKey !== "subtotal") {
(inSubtotal as any)[monthKey] =
((inSubtotal as any)[monthKey] ?? 0) + value;
inSubtotal.total += value;
}
});
outUsageData.forEach((outData: any) => {
const usageKey = outData.usage;
const value = (prfItem as any)[usageKey] ?? 0;
outData[monthKey] = value;
outData.total += value;
if (usageKey !== "subtotal") {
(outSubtotal as any)[monthKey] =
((outSubtotal as any)[monthKey] ?? 0) + value;
outSubtotal.total += value;
}
});
});
inUsageData.push(inSubtotal);
outUsageData.push(outSubtotal);
const usageTotalObj: { [key: string]: any } = totalObj;
for (const key in usageTotalObj) {
if (["usage", "gubun", "edit"].includes(key)) continue;
const val =
(Number((inSubtotal as any)[key]) || 0) +
(Number((outSubtotal as any)[key]) || 0);
(usageTotalObj as any)[key] = val;
}
const tableData = [...inUsageData, ...outUsageData, usageTotalObj];
setPrfData(tableData);
} else {
setPrfData([]);
}
}
useEffect(() => {
let wshwtr = 0;
let coolUswtr = 0;
let cleanUswtr = 0;
let drwtUswtr = 0;
let dltnUswtr = 0;
let ithlRswtr = 0;
let ithlEtcUswtr = 0;
let cleanToilUswtr = 0;
let resn = 0;
let lndwtr = 0;
let hydUswtr = 0;
let ugwtChr = 0;
let farmUswtr = 0;
let rvrKeepUswtr = 0;
let indstUswtr = 0;
if (rawData) {
rawData.forEach((item) => {
prfTableData(item.prfList, year);
item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
wshwtr += element.wshwtr; //세척수
coolUswtr += element.coolUswtr; //냉각수
cleanUswtr += element.cleanUswtr; //청소수
drwtUswtr += element.drwtUswtr; //식수대
dltnUswtr += element.dltnUswtr //희석용수
ithlRswtr += element.ithlRswtr //중수도
ithlEtcUswtr += element.ithlEtcUswtr //기타
cleanToilUswtr += element.cleanToilUswtr //청소화장실용수
resn += element.resn //세척살수용수
lndwtr += element.lndwtr; //조경용수
hydUswtr += element.hydUswtr; //친수용수
ugwtChr += element.ugwtChr; //지하수충전
farmUswtr += element.farmUswtr //농업용수
rvrKeepUswtr += element.rvrKeepUswtr //하천등유지용수
indstUswtr += element.indstUswtr //공업용수
});
})
const data: dnChartForm[] = []
data?.push({ label: "세척수", val: wshwtr, fill: "red" })
data?.push({ label: "냉각수", val: coolUswtr, fill: "orange" })
data?.push({ label: "청소수", val: cleanUswtr, fill: "yellow" })
data?.push({ label: "식수대", val: drwtUswtr, fill: "green" })
data?.push({ label: "희석용수", val: dltnUswtr, fill: "skyblue" })
data?.push({ label: "중수도", val: ithlRswtr, fill: "blue" })
data?.push({ label: "기타", val: ithlEtcUswtr, fill: "purple" })
data?.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "red" })
data?.push({ label: "세척살수용수", val: resn, fill: "orange" })
data?.push({ label: "조경용수", val: lndwtr, fill: "yellow" })
data?.push({ label: "친수용수", val: hydUswtr, fill: "green" })
data?.push({ label: "지하수충전", val: ugwtChr, fill: "skyblue" })
data?.push({ label: "농업용수", val: farmUswtr, fill: "blue" })
data?.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "purple" })
data?.push({ label: "공업용수", val: indstUswtr, fill: "black" })
setChartData(data);
}
}, [rawData, year])
useEffect(() => {
const newChartConfig = chartData.reduce((config, element) => {
config[element.label] = {
label: element.label,
fill: element.fill
};
return config;
}, {} as Record<string, { label: string; fill?: string }>) satisfies ChartConfig;
setChartConfig(newChartConfig);
}, [chartData]);
// 장내용수 도넛차트
useEffect(() => {
let wshwtr = 0;
let coolUswtr = 0;
let cleanUswtr = 0;
let drwtUswtr = 0;
let dltnUswtr = 0;
let ithlRswtr = 0;
let ithlEtcUswtr = 0;
let cleanToilUswtr = 0;
let resn = 0;
if (rawData) {
rawData.forEach((item) => {
item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
wshwtr += element.wshwtr; //세척수
coolUswtr += element.coolUswtr; //냉각수
cleanUswtr += element.cleanUswtr; //청소수
drwtUswtr += element.drwtUswtr; //식수대
dltnUswtr += element.dltnUswtr //희석용수
ithlRswtr += element.ithlRswtr //중수도
ithlEtcUswtr += element.ithlEtcUswtr //기타
cleanToilUswtr += element.cleanToilUswtr //청소화장실용수
resn += element.resn //세척살수용수
});
})
const data: dnChartForm[] = []
data?.push({ label: "세척수", val: wshwtr, fill: "yellow" })
data?.push({ label: "냉각수", val: coolUswtr, fill: "green" })
data?.push({ label: "청소수", val: cleanUswtr, fill: "yellow" })
data?.push({ label: "식수대", val: drwtUswtr, fill: "green" })
data?.push({ label: "희석용수", val: dltnUswtr, fill: "yellow" })
data?.push({ label: "중수도", val: ithlRswtr, fill: "green" })
data?.push({ label: "기타", val: ithlEtcUswtr, fill: "yellow" })
data?.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "green" })
data?.push({ label: "세척살수용수", val: resn, fill: "yellow" })
setDnInUsage(data);
}
}, [rawData, year])
useEffect(() => {
const newChartConfig = dnInUsage.reduce((config, element) => {
config[element.label] = {
label: element.label,
fill: element.fill
};
return config;
}, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig;
setDnInChartConfig(newChartConfig);
}, [dnInUsage]);
// 장외용수 도넛차트
useEffect(() => {
let lndwtr = 0;
let hydUswtr = 0;
let ugwtChr = 0;
let farmUswtr = 0;
let rvrKeepUswtr = 0;
let indstUswtr = 0;
if (rawData) {
rawData.forEach((item) => {
item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
lndwtr += element.lndwtr; //조경용수
hydUswtr += element.hydUswtr; //친수용수
ugwtChr += element.ugwtChr; //지하수충전
farmUswtr += element.farmUswtr //농업용수
rvrKeepUswtr += element.rvrKeepUswtr //하천등유지용수
indstUswtr += element.indstUswtr //공업용수
});
})
const data: dnChartForm[] = []
data?.push({ label: "조경용수", val: lndwtr, fill: "yellow" })
data?.push({ label: "친수용수", val: hydUswtr, fill: "black" })
data?.push({ label: "지하수충전", val: ugwtChr, fill: "yellow" })
data?.push({ label: "농업용수", val: farmUswtr, fill: "black" })
data?.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "yellow" })
data?.push({ label: "공업용수", val: indstUswtr, fill: "black" })
setDnOutUsage(data);
}
}, [rawData, year])
useEffect(() => {
const newChartConfig = dnOutUsage.reduce((config, element) => {
config[element.label] = {
label: element.label,
fill: element.fill
};
return config;
}, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig;
setDnOutChartConfig(newChartConfig);
}, [dnOutUsage]);
//월별 장내외 용수 총 실적 막대차트
useEffect(() => {
let wshwtr = 0;
let coolUswtr = 0;
let cleanUswtr = 0;
let drwtUswtr = 0;
let dltnUswtr = 0;
let ithlRswtr = 0;
let ithlEtcUswtr = 0;
let cleanToilUswtr = 0;
let resn = 0;
let lndwtr = 0;
let hydUswtr = 0;
let ugwtChr = 0;
let farmUswtr = 0;
let rvrKeepUswtr = 0;
let indstUswtr = 0;
let inUsageSum = 0;
let outUsageSum = 0;
let inUsageArr = [];
let outUsageArr = [];
if (rawData) {
const data: barChartForm[] = []
rawData.forEach((item) => {
item.prfList
.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
wshwtr = element.wshwtr; //세척수
coolUswtr = element.coolUswtr; //냉각수
cleanUswtr = element.cleanUswtr; //청소수
drwtUswtr = element.drwtUswtr; //식수대
dltnUswtr = element.dltnUswtr //희석용수
ithlRswtr = element.ithlRswtr //중수도
ithlEtcUswtr = element.ithlEtcUswtr //기타
cleanToilUswtr = element.cleanToilUswtr //청소화장실용수
resn = element.resn //세척살수용수
lndwtr = element.lndwtr; //조경용수
hydUswtr = element.hydUswtr; //친수용수
ugwtChr = element.ugwtChr; //지하수충전
farmUswtr = element.farmUswtr //농업용수
rvrKeepUswtr = element.rvrKeepUswtr //하천등유지용수
indstUswtr = element.indstUswtr //공업용수
inUsageSum = wshwtr + coolUswtr + cleanUswtr + drwtUswtr + dltnUswtr + ithlRswtr + ithlEtcUswtr + cleanToilUswtr + resn;
inUsageArr.push(inUsageSum)
outUsageSum = lndwtr + hydUswtr + ugwtChr + farmUswtr + rvrKeepUswtr + indstUswtr;
outUsageArr.push(outUsageSum)
const month = Number(element.ym.slice(4, 6))
data.push({ label: `${month}월`, "장내용수": inUsageSum, "장외용수": outUsageSum })
},
);
})
setBarData(data);
}
}, [rawData, year])
useEffect(() => {
const newChartConfig = barData.reduce((config, element) => {
config[element.label] = {
label: element.label,
};
return config;
}, {} as Record<string, { label: string }>) satisfies ChartConfig;
setBarChartConfig(newChartConfig);
}, [barData]);
//월별 장내 용수 실적 라인차트
useEffect(() => {
if (rawData) {
const wshwtrData: lineChartDataProps[] = []
const coolUswtrData: lineChartDataProps[] = []
const cleanUswtrData: lineChartDataProps[] = []
const drwtUswtrData: lineChartDataProps[] = []
const dltnUswtrData: lineChartDataProps[] = []
const ithlRswtrData: lineChartDataProps[] = []
const ithlEtcUswtrData: lineChartDataProps[] = []
const cleanToilUswtrData: lineChartDataProps[] = []
const resnData: lineChartDataProps[] = []
rawData.forEach((item) => {
item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
const month = Number(element.ym.slice(4, 6))
wshwtrData.push({ label: `${month}월`, val: element.wshwtr, color: "red" })
coolUswtrData.push({ label: `${month}월`, val: element.coolUswtr, color: "red" })
cleanUswtrData.push({ label: `${month}월`, val: element.cleanUswtr, color: "red" })
drwtUswtrData.push({ label: `${month}월`, val: element.drwtUswtr, color: "red" })
dltnUswtrData.push({ label: `${month}월`, val: element.dltnUswtr, color: "red" })
ithlRswtrData.push({ label: `${month}월`, val: element.ithlRswtr, color: "red" })
ithlEtcUswtrData.push({ label: `${month}월`, val: element.ithlEtcUswtr, color: "red" })
cleanToilUswtrData.push({ label: `${month}월`, val: element.cleanToilUswtr, color: "red" })
resnData.push({ label: `${month}월`, val: element.resn, color: "red" })
},
);
})
const rtndata = {
chartData1: wshwtrData,
chartData2: coolUswtrData,
chartData3: cleanUswtrData,
chartData4: drwtUswtrData,
chartData5: dltnUswtrData,
chartData6: ithlRswtrData,
chartData7: ithlEtcUswtrData,
chartData8: cleanToilUswtrData,
chartData9: resnData
}
setInLineData(rtndata)
}
}, [rawData, year])
useEffect(() => {
if (inLineData?.chartData1) {
const newChartConfig = {
chartData1: {
label: '세척수',
color: "red",
},
chartData2: {
label: '냉각수',
color: "orange",
},
chartData3: {
label: '청소수',
color: "yellow",
},
chartData4: {
label: '식수대',
color: "pink",
},
chartData5: {
label: '희석용수',
color: "purple",
},
chartData6: {
label: '중수도',
color: "green",
},
chartData7: {
label: '기타',
color: "blue",
},
chartData8: {
label: '청소화장실용수',
color: "magenta",
},
chartData9: {
label: '세척살수용수',
color: "skyblue",
},
} satisfies ChartConfig;
setLnInChartConfig(newChartConfig);
}
}, [inLineData]);
//월별 장외 용수 실적 라인차트
useEffect(() => {
if (rawData) {
const lndwtrData: lineChartDataProps[] = []
const hydUswtrData: lineChartDataProps[] = []
const ugwtChrData: lineChartDataProps[] = []
const farmUswtrData: lineChartDataProps[] = []
const rvrKeepUswtrData: lineChartDataProps[] = []
const indstUswtrData: lineChartDataProps[] = []
rawData.forEach((item) => {
item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => {
const month = Number(element.ym.slice(4, 6))
lndwtrData.push({ label: `${month}월`, val: element.lndwtr, color: "red" })
hydUswtrData.push({ label: `${month}월`, val: element.hydUswtr, color: "red" })
ugwtChrData.push({ label: `${month}월`, val: element.ugwtChr, color: "red" })
farmUswtrData.push({ label: `${month}월`, val: element.farmUswtr, color: "red" })
rvrKeepUswtrData.push({ label: `${month}월`, val: element.rvrKeepUswtr, color: "red" })
indstUswtrData.push({ label: `${month}월`, val: element.indstUswtr, color: "red" })
},
);
})
setOutLineData({
chartData1: lndwtrData,
chartData2: hydUswtrData,
chartData3: ugwtChrData,
chartData4: farmUswtrData,
chartData5: rvrKeepUswtrData,
chartData6: indstUswtrData,
})
}
}, [rawData, year])
useEffect(() => {
if (outLineData?.chartData1) {
const newChartConfig = {
chartData1: {
label: '조경용수',
color: "red",
},
chartData2: {
label: '친수용수',
color: "orange",
},
chartData3: {
label: '지하수충전',
color: "yellow",
},
chartData4: {
label: '농업용수',
color: "green",
},
chartData5: {
label: '하천등유지용수',
color: "blue",
},
chartData6: {
label: '공업용수',
color: "purple",
},
} satisfies ChartConfig;
setLnOutChartConfig(newChartConfig);
}
}, [outLineData]);
useEffect(() => {
if (!pbnstPrfmncSttn.isInit) {
pbnstPrfmncInit("1", "11");
}
}, [pbnstPrfmncSttn.isInit, pbnstPrfmncInit]);
useEffect(() => {
setbdstTable(prfmncRes);
}, [prfmncRes]);
return (
<>
<ContentsDiv>
<ListDiv>
<hr />
<h3 className="mt-4 ml-2 text-2xl font-medium text-label">하수재이용실적 상세현황</h3>
<SelectBox
className="w-1/12 text-2xl mt-4 ml-2"
label="연도"
selectClass={""}
selectArray={selectYearList}
value={year}
onSelectValue={(val: string | number) => setYear(String(val))} />
<h1 className="mt-4 ml-2">장내·외 용도별 하수재이용실적 현황(계)</h1>
<div className="flex">
<div className="w-1/3">
<MainChart chartConfig={chartConfig} chartData={chartData} chartTitle={"장내·외 용수 총 실적"} chartForm={CHART_FORM.DN} />
</div>
<div className="w-1/3">
<MainChart chartConfig={dnInChartConfig} chartData={dnInUsage} chartTitle={"장내용수 실적"} chartForm={CHART_FORM.DN} />
</div>
<div className="w-1/3">
<MainChart chartConfig={dnOutChartConfig} chartData={dnOutUsage} chartTitle={"장외용수 실적"} chartForm={CHART_FORM.DN} />
</div>
</div>
<h1 className="">장내·외 월별 하수재이용실적 현황</h1>
<div className="flex">
<div className="w-1/3">
<MainChart chartConfig={barChartConfig} chartData={barData} chartTitle={"월별 장내·외 용수 총 실적"} chartForm={CHART_FORM.DOUBLEBAR} />
</div>
<div className="w-1/3">
<MainChart chartConfig={lnInChartConfig} chartData={inLineData} chartTitle={"월별 장내 용수 실적"} chartForm={CHART_FORM.LINE} />
</div>
<div className="w-1/3">
<MainChart chartConfig={lnOutChartConfig} chartData={outLineData} chartTitle={"월별 장외 용수 총 실적"} chartForm={CHART_FORM.LINE} />
</div>
</div>
{/* 하수재이용실적 현황 - 조건부 렌더링 */}
{isVisible && (
<>
<h1 className="">하수재이용실적 현황</h1>
<ListDiv className="col-span-8 pr-6 ">
<ScrollArea className="h-[60vh]">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">
{"하수처리수재이용 실적(m3) 등록결과"}
</h3>
</div>
<UiTable
headName={undefined}
tableData={prfData || []}
headlist={monthHeadList}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}}>
</UiTable>
</ScrollArea>
</ListDiv>
</>
)}
</ListDiv>
</ContentsDiv>
<Toaster position="top-center" />
</>
)
}
+421
View File
@@ -0,0 +1,421 @@
"use client";
import { useWrStore } from "@/app/store";
import {
rainZod,
selectClass,
TEST_UUID,
type customRainBdst,
} from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { DatePicker, FormUi } from "@common/business_components";
import { Form, FormField, SelectBox } from "@common/business_components/ui";
import { Button, Input } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useState } from "react";
import { useForm, useWatch, type UseFormReturn } from "react-hook-form";
import { z } from "zod";
import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg";
import {
formatCommaNumber,
handleCommaNumberChange,
} from "@/components/lib/func/commom";
import { formH1Class, formH2Class } from "@/components/lib/class/comClass";
import { Label } from "@common/business_components/ui/form/lib/label";
export default function RainPrfmncSttnPopup({
rainForm,
isModal,
}: {
rainForm: customRainBdst;
isModal: boolean;
}) {
const { code, rainBdstReg, rainBdstUpdate, rain } = useWrStore(
(state) => state
);
const { rainFcltCd } = code;
const defaultValues: z.infer<typeof rainZod> = rainZod.parse({});
const form = useForm<z.infer<typeof rainZod>>({
resolver: zodResolver(rainZod),
defaultValues: defaultValues,
});
const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn] = form.watch([
"reuseBuilding.0.grntchBdstCertYn",
"reuseBuilding.0.instlCstSprtYn",
"reuseBuilding.0.rainyObj.filtrTrtmfctYn",
]);
const cleanRainData = (raw: any): z.infer<typeof rainZod> => {
return {
...raw,
frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null),
archChgPrmsnDt: raw?.archChgPrmsnDt
? new Date(raw?.archChgPrmsnDt)
: null,
reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({
...item,
instlCmptnDt: new Date(item?.instlCmptnDt),
operMngCd: item?.operMngCd ?? "", // null → ""
grntchBdstCertYr:
typeof item?.grntchBdstCertYr === "string"
? item?.grntchBdstCertYr
: "", // null → ""
rainObj: {
...item?.rainObj,
},
})),
};
};
useEffect(() => {
if (grntchBdstCertYn === "N") {
form.setValue("reuseBuilding.0.grntchBdstCertYr", "");
form.setValue("reuseBuilding.0.grntchBdstCertNo", "");
form.setValue("reuseBuilding.0.grntchBdstCertGrd", "");
} else if (instlCstSprtYn === "N") {
form.setValue("reuseBuilding.0.instlCstSprtAmt", 0);
} else if (filtrTrtmfctYn === "N") {
form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctCpct", 0);
form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctPrs", "");
}
}, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn]);
// form 값이 변경될 때마다 로그 출력
useEffect(() => {
const subscription = form.watch((value) => {
});
return () => subscription.unsubscribe();
}, [form]);
useEffect(() => {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
const onSubmit = (values: z.infer<typeof rainZod>) => {
if (isModal) {
rainBdstReg(values, "41");
} else {
rainBdstUpdate(values, "41");
}
};
useEffect(() => {
if (rainZod) form.reset(cleanRainData(rainForm));
}, [rainForm]);
return (
<div className="!mx-[1.6rem] !p-0r">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div>
<div className="grid grid-cols-12 flex items-center card-header">
<h1 className="text-2xl font-medium text-label col-span-10">
{isModal ? "건축물 운영정보" : "빗물이용시설 정보"}
</h1>
</div>
</div>
{isModal && rainForm != undefined ? <BdstReg form={form} /> : <></>}
{isModal ? (
<h1 className="pl-3 text-xl col-span-10 mt-4">
빗물이용시설 정보{" "}
</h1>
) : (
<></>
)}
<h1 className={formH1Class}>
기본정보
</h1>
<div className="grid grid-cols-4 gap-2 mt-4">
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.instlCmptnDt"
render={({ field }) => (
<FormUi label="설치완료일">
<DatePicker
mode="single"
field={{
value:
field.value && !isNaN(new Date(field.value).getTime())
? new Date(field.value)
: new Date(), // Provide a default Date value
onChange: (date) => field.onChange(date),
}}
selectClass={"w-full"}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.instlCst"
render={({ field }) => (
<FormUi label="설치비">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="">
<h1 className={formH1Class}>
빗물이용시설 정보
</h1>
</div>
<div>
<h2 className={formH2Class}>집수면</h2>
</div>
<div className="grid grid-cols-10 gap-4 mt-4 ">
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.pntfColKnd"
render={({ field }) => (
<FormUi label="종류">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={rainFcltCd}
onSelectValue={field.onChange}
value={field.value ?? rainFcltCd[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.pntfColArea"
render={({ field }) => (
<FormUi label="면적(m³)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
/>
</FormUi>
)}
/>
</div>
</div>
<div>
<h2 className={formH2Class}>여과 등 처리시설</h2>
</div>
<div className="grid grid-cols-12 gap-2 mt-4">
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctYn"
render={({ field }) => (
<FormUi label="유무">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctCpct"
render={({ field }) => (
<FormUi label="용량(m³/일)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={filtrTrtmfctYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rainyObj.filtrTrtmfctPrs"
render={({ field }) => (
<FormUi label="처리공정">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={filtrTrtmfctYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
</div>
<div className=" my-2">
<h1 className={formH1Class}>
인센티브 정보
</h1>
</div>
<div>
<h2 className={formH2Class}>설치비 지원</h2>
</div>
<div className="grid grid-cols-10 gap-2 mt-4">
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtYn"
render={({ field }) => (
<FormUi label="지원 여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-5">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtAmt"
render={({ field }) => (
<FormUi label="지원금액(원)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={instlCstSprtYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
</div>
<div>
<h2 className={formH2Class}>녹색건축물 인증</h2>
</div>
<div className="grid grid-cols-6 gap-2 mt-4">
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYn"
render={({ field }) => (
<FormUi label="인증여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYr"
render={({ field }) => (
<FormUi label="인증년도">
<Input
className={'w-full !text-xl'}
placeholder="2024"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={grntchBdstCertYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3 ">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertNo"
render={({ field }) => (
<FormUi label="인증번호">
<Input
className={'w-full !text-xl'}
placeholder="ab1234678"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={grntchBdstCertYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertGrd"
render={({ field }) => (
<FormUi label="인증등급">
<Input
className={'w-full !text-xl'}
placeholder="A"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={grntchBdstCertYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
</div>
</form>
</Form>
</div>
);
}
+404
View File
@@ -0,0 +1,404 @@
"use client";
import {
DatePicker,
Form,
FormField,
FormUi,
SelectBox,
} from "@common/business_components/ui";
import { Button, Input } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { useWrStore } from "@/app/store";
import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import {
rswtZod,
type customRswtBdst,
} from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
import { useEffect } from "react";
import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg";
import {
formatCommaNumber,
handleCommaNumberChange,
} from "@/components/lib/func/commom";
import { formH1Class, formH2Class } from "@/components/lib/class/comClass";
import { Label } from "@common/business_components/ui/form/lib/label";
export default function RswtrPrfmncsSttnPopup({
rswtForm,
isModal,
}: {
rswtForm: customRswtBdst;
isModal: boolean;
}) {
const { rswt, code, rswtBdstUpdate, rswtBdstReg } = useWrStore(
(state) => state
);
const defaultValues: z.infer<typeof rswtZod> = rswtZod.parse({});
const form = useForm<z.infer<typeof rswtZod>>({
resolver: zodResolver(rswtZod),
defaultValues: defaultValues,
});
const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctProcs] = form.watch([
"reuseBuilding.0.grntchBdstCertYn",
"reuseBuilding.0.instlCstSprtYn",
"reuseBuilding.0.rswtrObj.filtrTrtmfctProcs",
]);
const onSubmit = (values: z.infer<typeof rswtZod>) => {
if (isModal) {
rswtBdstReg(values, "41");
} else {
rswtBdstUpdate(values, "41");
}
};
const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => {
return {
...raw,
frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null),
archChgPrmsnDt: raw?.archChgPrmsnDt
? new Date(raw?.archChgPrmsnDt)
: null,
reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({
...item,
instlCmptnDt: new Date(item?.instlCmptnDt),
operMngCd: item?.operMngCd ?? "", // null → ""
grntchBdstCertYr:
typeof item?.grntchBdstCertYr === "string"
? item?.grntchBdstCertYr
: "", // null → ""
rswtrObj: {
...item?.rswtrObj,
},
})),
};
};
useEffect(() => {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
useEffect(() => {
console.log("filtrTrtmfctProcs ", filtrTrtmfctProcs);
if (grntchBdstCertYn === "N") {
form.setValue("reuseBuilding.0.grntchBdstCertYr", "");
form.setValue("reuseBuilding.0.grntchBdstCertNo", "");
form.setValue("reuseBuilding.0.grntchBdstCertGrd", "");
} else if (instlCstSprtYn === "N") {
form.setValue("reuseBuilding.0.instlCstSprtAmt", 0);
}
}, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctProcs]);
useEffect(() => {
if (rswtZod) form.reset(cleanRswtData(rswtForm));
}, [rswtForm]);
return (
<div className="!mx-[1.6rem] !p-0r">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
{/* <div className="flex justify-between items-center mx-3"> */}
<div>
<div className="grid grid-cols-12 flex items-center card-header">
<h1 className="text-2xl font-medium text-label col-span-10">
건축물 운영정보
</h1>
</div>
</div>
{isModal && rswtForm != undefined ? <BdstReg form={form} /> : <></>}
{isModal ? (
<h1 className="pl-3 text-xl col-span-10 mt-4 ">
중수도 시설 정보{" "}
</h1>
) : (
<></>
)}
<h1 className={formH1Class}>기본정보</h1>
<div className="grid grid-cols-4 gap-2 mt-4">
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.instlCmptnDt"
render={({ field }) => (
<FormUi label="설치완료일">
<DatePicker
mode="single"
field={{
value:
field.value && !isNaN(new Date(field.value).getTime())
? new Date(field.value)
: new Date(), // Provide a default Date value
onChange: (date) => field.onChange(date),
}}
selectClass={"w-full"}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.instlCst"
render={({ field }) => (
<FormUi label="설치비">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-4 gap-2 mt-4">
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.rswtrObj.wtrUsqty"
render={({ field }) => (
<FormUi label="물사용량(m³)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-2">
<FormField
control={form.control}
name="reuseBuilding.0.rswtrObj.oblgTrgtWtrUsqty"
render={({ field }) => (
<FormUi label="의무대상 물사용량(m³)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className=" my-4">
<h1 className={formH1Class}>중수도 시설 정보</h1>
</div>
<div className="grid grid-cols-12 gap-1 mt-4">
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rswtrObj.filtrTrtmfctCpct"
render={({ field }) => (
<FormUi label="처리시설 용량">
<Input
className={'w-full !text-xl'}
placeholder="0"
type="number"
value={field.value ?? ""}
onChange={(e)=>{field.onChange(Number(e.target.value))} }
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rswtrObj.orgwtKndCd"
render={({ field }) => (
<FormUi label="원수 종류">
<SelectBox
className={""}
label={""}
selectClass={selectClass}
selectArray={code.orgwtKndCd}
onSelectValue={field.onChange}
value={field.value ?? code.orgwtKndCd[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-4">
<FormField
control={form.control}
name="reuseBuilding.0.rswtrObj.filtrTrtmfctProcs"
render={({ field }) => (
<FormUi label="주요처리공정">
<Input
className={'w-full !text-xl'}
placeholder="주요처리공정"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
<div className=" my-2">
<h1 className={formH1Class}>인센티브 정보</h1>
</div>
<div>
<h2 className={formH2Class}>설치비 지원</h2>
</div>
<div className="grid grid-cols-2 gap-2 mt-4">
<div className="col-span-1">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtYn"
render={({ field }) => (
<FormUi label="설치비 지원여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-1">
<FormField
control={form.control}
name="reuseBuilding.0.instlCstSprtAmt"
render={({ field }) => (
<FormUi label="지원금액(원)">
<Input
className={'w-full !text-xl'}
placeholder="1,000,000"
type="text"
onChange={(e) => {
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={instlCstSprtYn !== "Y"}
/>
</FormUi>
)}
/>
</div>
</div>
<div>
<h2 className={formH2Class}>녹색건축물 인증</h2>
</div>
<div className="grid grid-cols-6 gap-2 mt-4">
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYn"
render={({ field }) => (
<FormUi label="인증여부">
<SelectBox
className={""}
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={field.onChange}
value={field.value ?? code.yn[0]?.val}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertYr"
render={({ field }) => (
<FormUi label="인증년도">
<Input
className={'w-full !text-xl'}
placeholder="2024"
type="text"
{...field}
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertNo"
render={({ field }) => (
<FormUi label="인증번호">
<Input
className={'w-full !text-xl'}
placeholder="ab1234678"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
<div className="col-span-3">
<FormField
control={form.control}
name="reuseBuilding.0.grntchBdstCertGrd"
render={({ field }) => (
<FormUi label="인증등급">
<Input
className={'w-full !text-xl'}
placeholder="A"
type=""
value={field.value ?? ""}
onChange={field.onChange}
readOnly={true}
/>
</FormUi>
)}
/>
</div>
</div>
</form>
</Form>
</div>
);
}
+1
View File
@@ -0,0 +1 @@
export * from './map'
+47
View File
@@ -0,0 +1,47 @@
/**
* 관망도 API
*/
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map`
//const API_URL = "http://con.mind-one.co.kr:11083/sewerage-service/api/v1/map";
export type LyrStngProps = {
lyr_sn: string
lyr_nm: string
upper_lyr_sn: string
drawOrdr: string
flterSql: string
gobjTy: string
lyrTpNm: string
useYn: string
minViewSize: string
iconNm: string
lblAtrbNm: string
fontNm: string
fontIntnColor: string
fontExtnColor: string
fontExtnWdth: string
lnWdth: string
lnColor: string
polgnColor: string
initEprssYn: string
clickPsblYn: string
}
export const mapService = {
//레이어 설정 조회
getLyrStngs: async () => {
const response = await fetch(`${API_URL}/lyrStngs/WR_NETWORK`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
}
+36 -19
View File
@@ -9,6 +9,7 @@ import {
} from "@/app/store/matDmd/coMatRvw/type";
import { formH1Class, formTableName } from "@/components/lib/class/comClass";
import {
formatPhoneNumber,
getCity,
getdmdMatFlag,
tableData,
@@ -33,7 +34,7 @@ import {
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Input, Label } from "@common/components/ui";
import { useEffect, useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { useForm, useWatch } from "react-hook-form";
import toast, { Toaster } from "react-hot-toast";
import { z } from "zod";
import moment from "moment";
@@ -45,7 +46,7 @@ import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/b
export default function CoMatRvw() {
const { code, coRvw, coMatRvwInit, coMatRvwSearch, coMatRvwInqua } =
useWrStore((state) => state);
const { coMatRvwRes } = coRvw;
const { coMatRvwRes,coMatRvwCount } = coRvw;
//init, sub, match
const initRef = useRef<boolean>(false);
const { coRvwHeadList } = coRvw;
@@ -68,6 +69,7 @@ export default function CoMatRvw() {
const [selectData, setSelectData] = useState<DmdCoReqType>(
{} as DmdCoReqType
);
const [selectionVersion, setSelectionVersion] = useState(0);
const getSearch = (
startYear: number,
@@ -91,39 +93,45 @@ export default function CoMatRvw() {
const form = useForm<z.infer<typeof DmdCoReqSchema>>({
resolver: zodResolver(DmdCoReqSchema),
shouldUnregister: false,
// defaultValues: DmdCoReqSchema.parse({})
});
const TableCellClick = (index: number) => {
console.log(coMatRvwRes);
if (coMatRvwRes && coMatRvwRes[index]) {
setSelectData(coMatRvwRes[index]);
setSelectionVersion((v) => v + 1); // 행을 클릭할 때마다 버전을 1씩 증가
}
};
const useUsgCdWatch = useWatch({ control: form.control, name: "useUsgCd" });
useEffect(() => {
if (form?.getValues("useUsgCd")) {
if (useUsgCdWatch) {
form.setValue(
"demandCst.usgPrcsProcs",
usageProcData?.find(
(item) => item.usage === (form?.getValues("useUsgCd") as string)
(item) => item.usage === useUsgCdWatch
)?.process ?? ""
);
}
}, [form.watch("useUsgCd")]);
}, [useUsgCdWatch, form,selectionVersion]);
useEffect(() => {
if (coMatRvwRes.length > 0) {
if (coMatRvwRes&&coMatRvwRes.length > 0) {
const data = coMatRvwRes?.map((item, index) => {
let flag = "";
flag = getdmdMatFlag(item?.demandMatching?.splyFcltFlg ??"");
flag = getdmdMatFlag(item?.demandMatching?.splyFcltFlg ||undefined);
return {
...item,
ctpvCd: item.demandCo?.ctpvCd,
sggCd: item.demandCo?.sggCd,
dmdCoNm: item.demandCo?.dmdCoNm,
daddr: item.demandCo?.daddr,
telno: item.demandCo?.telno,
regDt: moment(item?.demandCo?.regDt).format("YYYY-MM-DD"),
telno: formatPhoneNumber(item?.demandCo?.telno||''),
dmndTmng: item?.dmndTmng,
splyFcltFlg: flag,
};
});
@@ -151,14 +159,23 @@ export default function CoMatRvw() {
useEffect(() => {
if (selectData) {
console.log(selectData);
form.reset(selectData);
try {
DmdCoReqSchema.parse(selectData);
} catch (e) {
if (e instanceof z.ZodError) {
console.log(e.flatten());
}
}
form.reset( selectData);
setSelectionVersion((v) => v + 1); // 강제로 바뀌었다고 알리기
}
}, [selectData, form]);
return (
<>
<ContentsDiv>
<div className="grid grid-cols-4 gap-4">
<div className="col-span-4">
<Title title={"수요매칭검토"} />
<SearchDiv>
@@ -181,7 +198,7 @@ export default function CoMatRvw() {
<div className="col-span-7 w-full">
<div className="flex flex-row items-center">
<Label className="text-xl text-gray-600 mx-6">
하수 재이용년도
수요신청년도
</Label>
<SelectBox
className="h-full w-32 text-2xl"
@@ -259,7 +276,7 @@ export default function CoMatRvw() {
tableData={dmdMatData || []}
headlist={coRvwHeadList}
pageSize={20}
total={100000}
total={coMatRvwCount}
cellClick={(index: number | undefined, headId: string) => {
if (index !== undefined && index >= 0) {
TableCellClick(index);
@@ -269,7 +286,7 @@ export default function CoMatRvw() {
></UiTable>
</ListDiv>
<hr />
<ScrollArea className="h-[49vh]">
{/* <ScrollArea className="h-[49vh]"> */}
<ListDiv>
<h3 className="mt-4 ml-2 text-2xl font-medium text-label">
수요신청 상세현황
@@ -279,7 +296,7 @@ export default function CoMatRvw() {
<div className="col-span-3 mt-4">
<FormField
control={form.control}
name="demandCo.regDt"
name="dmndTmng"
render={({ field }) => (
<FormUi label="요구시기">
<Input
@@ -660,9 +677,9 @@ export default function CoMatRvw() {
</div>
</div>
</Form>
<ConCostForm form={form} type={"mat"} />
<ConCostForm form={form} type={"mat"} selectionVersion={selectionVersion} />
</ListDiv>
</ScrollArea>
{/* </ScrollArea> */}
</div>
</div>
</ContentsDiv>
+186 -46
View File
@@ -1,30 +1,172 @@
"use client";
import { useWrStore } from "@/app/store";
import { ruseUtzType } from "@/app/store/matDmd/ruseCaseMng/headList";
import { getCity } from "@/components/lib/func/commom";
import { excelDownload, getCity } from "@/components/lib/func/commom";
import RuseCaseMngExcel from "@/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import { ContentsDiv, SearchDiv, SearchFormLeft, SearchFormRight, SearchInput, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import {
ContentsDiv,
filePickedBlobArray,
SearchDiv,
SearchFormLeft,
SearchFormRight,
SearchInput,
SelectBox,
Title,
UiModal,
UiTable,
type SelectDataType,
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components/ui";
import { useState } from "react";
import { useEffect, useState } from "react";
import { toast, Toaster } from "react-hot-toast";
export default function RuseCaseMng() {
const { code,ruseCase} = useWrStore((state) => state);
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>()
const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state);
const { ruseCaseMngList, status, count } = ruseCaseMng;
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>([]);
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [selectUtz, setSelectUtz] = useState<string[] | []>()
const [searchTxt, setSearchTxt] = useState<string>()
const [sidoCd, setSidoCd] = useState<string>("");
const [sigunguCd, setSigunguCd] = useState<string>("");
const [selectUtz, setSelectUtz] = useState<string[] | []>(["조경용수"]);
const [name, setName] = useState<string>("");
// 물재이용사례 조회
const searchRuseCaseMng = async (pageNo: number, pageSize: number) => {
const res = await selectRuseCaseMng(
pageNo,
pageSize,
sidoCd ?? "",
sigunguCd ?? "",
selectUtz[0] ?? "",
name ?? ""
);
}
// 시도 선택
const getSelCity = (val: string | number) => {
const cityValue = String(val);
const cityText = code.cityCd.find((item: { val: string; text: string }) => item.val === cityValue)?.text;
setSidoCd(cityText || "");
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
setObjSearch({ city: cityValue, sgg: '' });
setObjSearch({ city: cityValue, sgg: "" });
};
// 시군구 선택
const getSgg = (val: string | number) => {
setObjSearch({ ...objSearch, sgg: String(val) })
const sggValue = String(val);
const sggText = selectSgg?.find((item: { val: string; text: string }) => item.val === sggValue)?.text;
setSigunguCd(sggText || "");
setObjSearch({ ...objSearch, sgg: String(val) });
};
// 엑셀 등록 모달
const [isExcelOpen, setIsExcelOpen] = useState<{
isModal: boolean;
}>({ isModal: false });
// 다운로드
const download = () => {
searchRuseCaseMng(1, 10000);;
fetch(`/sample_ruseCaseMng.xlsx`, {}).then(async (response) => {
if (!response.ok) {
throw Error(response.statusText);
}
// 받은 파일 바인너리 처리
const blobRet = await response.arrayBuffer();
const excelData1 = ruseCaseMngList.map((item) => {
return [
item.ruseBizNm,
item.bizBgngDate,
item.bizEndDate,
item.splyFcltKnd,
item.splyFcltNm,
item.splyFcltCpct,
item.dmdFcltNm,
item.dmdFcltAddr,
item.ruseFcltCpct,
item.ruseFcltPplLen,
item.ruseFcltUsg,
item.ruseFcltSplyCpct,
item.ruseFcltPrcsMsrc,
item.grnteWqUsg,
item.grnteWqGrssClfg,
item.grnteWqCombResdch,
item.grnteWqTrbt,
item.grnteWqBod,
item.grnteWqSmll,
item.grnteWqCrmty,
item.grnteWqTn,
item.grnteWqTp,
item.grnteWqPh,
item.grnteWqClIonQnt,
item.grnteWqCndctStat,
item.grnteWqRmrk,
item.reqBizGrssCst,
item.reqBizReuseFcltCst,
item.reqBizPplCst,
item.reqBizArchCst,
item.reqBizMchnCst,
item.reqBizMtrngCst,
item.reqBizGisCst,
item.reqBizCctvCst,
item.reqBizCmcnCst,
item.reqBizWstCst,
item.reqBizDesignCst,
item.reqBizSpvsCst,
item.reqBizSbsdCst,
item.reqBizEtc,
item.prcmPlanGrssCst,
item.prcmPlanNlep,
item.prcmPlanLcep,
item.prcmPlanBrdnCst,
item.prcmPlanEcqrRt,
item.clclnGrssCst,
item.clclnNlep,
item.clclnLcep,
item.clclnBrdnCst,
item.clclnEcqrRt,
];
});
const sheetParameter = [
{
data: excelData1,
headerNum: 2,
headerRange: 'A1:AX1',
sheetName: '물재이용사례',
startCol: 'A'
}
];
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
await filePickedBlobArray(blobRet, '물재이용사례현황', sheetParameter);
});
}
// 양식받기
const sampleDownLoad = () => {
excelDownload(
[[]],
"물재이용사례 관리",
"sample_ruseCaseMng.xlsx",
"A1:AX1",
2
);
}
useEffect(() => {
if(status === 200){
setIsExcelOpen({ isModal: false });
}
}, [status]);
return (
<>
<ContentsDiv>
@@ -54,47 +196,32 @@ export default function RuseCaseMng() {
selectClass={""}
selectArray={ruseUtzType}
onSelectValue={(val) => {
if(val)
setSelectUtz([String(val)])}}
if (val) setSelectUtz([String(val)]);
}}
/>
<SearchInput
className={"text-2xl"}
label="수요처명"
textValue={""}
textValue={name}
setText={(e) => {
setSearchTxt(e)
setName(e);
}}
/>
</div>
</SearchFormLeft>
<SearchFormRight
className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2">
<div className="space-y">
<div className="flex flex-row space-x-2">
<Button
size={"sm"}
onClick={(e) => { }}
>
<Button size={"sm"} onClick={() => { searchRuseCaseMng(1, 20) }}>
조회
</Button>
<Button
size={"sm"}
onClick={(e) => { }}
>
<Button size={"sm"} onClick={() => { setIsExcelOpen({ isModal: true }); }}>
엑셀등록
</Button>
<Button
size={"sm"}
onClick={(e) => { }}
>
<Button size={"sm"} onClick={() => { download() }}>
다운로드
</Button>
<Button
size={"sm"}
onClick={(e) => { }}
>
<Button size={"sm"} onClick={() => { sampleDownLoad() }}>
양식받기
</Button>
</div>
@@ -103,24 +230,37 @@ export default function RuseCaseMng() {
</SearchDiv>
</div>
</div>
<div className="grid grid-cols-6 gap-4 ">
<div className="grid grid-cols-6 gap-4">
<div className="col-span-6">
<ListDiv className="overflow-x-auto">
<UiTable
headName={"물재이용사례 현황"}
tableData={[]}
children={undefined}
headlist={ruseCase.ruseHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
}}
onPageChange={() => { }}
></UiTable>
<div className="w-[350%]">
<UiTable
headName={"물재이용사례 현황"}
tableData={ruseCaseMngList}
children={undefined}
headlist={ruseCaseMng.ruseHeadList}
pageSize={20}
total={count}
cellClick={(index: number | undefined, headId: string) => {}}
onPageChange={(pageNo: number) => {
searchRuseCaseMng(pageNo, 10);
}}
/>
</div>
</ListDiv>
</div>
</div>
</ContentsDiv>
<Toaster position="top-center" />
{/* 엑셀 등록 모달 */}
<UiModal
open={isExcelOpen.isModal}
onOpenChange={() => {
setIsExcelOpen({ isModal: !isExcelOpen.isModal })
}}
bodyClass={"w-[40vw] max-w-none"}
body={<RuseCaseMngExcel />}
/>
</>
)
);
}
+59 -39
View File
@@ -7,6 +7,7 @@ import {
type RvwHistTableType,
} from "@/app/store/matDmd/coMatRvw/type";
import {
formatPhoneNumber,
getCity,
getdmdMatFlag,
yearOptions,
@@ -43,7 +44,7 @@ export default function RuseRvwHstry() {
ruseRvwInqua,
ruseRvwComplete,
} = useWrStore((state) => state);
const { rvwHistRes, ruseHistHeadList } = ruseHist;
const { rvwHistRes, ruseHistHeadList,rvwHistCount } = ruseHist;
const [pageNation, setPageNatiion] = useState<number>(1);
const [dmdCoNm, setDmdCoNm] = useState<string>("");
@@ -69,6 +70,36 @@ export default function RuseRvwHstry() {
setObjSearch({ city: cityValue, sgg: "0" });
};
useEffect(() =>{
getButtonSearch()
},[pageNation])
const getButtonSearch=()=>{
if (fromDate && toDate) {
const fromNum = Number(fromDate);
const toNum = Number(toDate);
if (fromNum > toNum) {
toast.error(
"시작년도가 종료년도 이후로 설정할 수 없습니다."
);
} else {
let con: string | number = "";
if (String(objSearch.city).length > 0) {
con = String(objSearch.city);
}
if (String(objSearch.sgg).length > 0 && objSearch.sgg !== "0") {
con = String(objSearch.sgg);
}
getSearch(
fromNum,
toNum,
con,
dmdCoNm,
ruseUtztnFcltNm
);
}
}
}
const getSgg = (val: string | number) => {
console.log(val);
setObjSearch({ ...objSearch, sgg: String(val) });
@@ -163,23 +194,29 @@ export default function RuseRvwHstry() {
const data = rvwHistRes?.map((item, index) => {
let flag = "";
flag = getdmdMatFlag(item.demandMatching?.splyFcltFlg ?? "");
const formatDate = (dateStr: string | null | undefined) => {
// null, undefined, 빈 문자열인 경우 '없음'을 반환합니다.
if (!dateStr) return "없음";
// "YYYYMMDD" 형식으로 날짜를 파싱합니다.
const d = moment(dateStr, "YYYYMMDD");
// 파싱된 날짜가 유효한 경우에만 포맷하고, 그렇지 않으면 '없음'을 반환합니다.
return d.isValid() ? d.format("YYYY-MM-DD") : "없음";
};
return {
...item,
ctpvCd: item.demandCo?.ctpvCd,
sggCd: item.demandCo?.sggCd,
dmdCoNm: item.demandCo?.dmdCoNm,
daddr: item.demandCo?.daddr,
telno: item.demandCo?.telno,
regDt: item?.demandCo?.regDt
? moment(item?.demandCo?.regDt).format("YYYY-MM-DD")
: "없음",
rvwCmptnYmd: item?.rvwCmptnYmd
? moment(item?.rvwCmptnYmd).format("YYYY-MM-DD")
: "없음",
mtchnCmptnYmd: item?.mtchnCmptnYmd
? moment(item?.mtchnCmptnYmd).format("YYYY-MM-DD")
telno: formatPhoneNumber(item.demandCo?.telno ||''),
dmndTmng: item?.dmndTmng
? moment(item?.dmndTmng).format("YYYY-MM-DD")
: "없음",
rvwCmptnYmd: formatDate(item.demandMatching?.rvwCmptnYmd),
mtchnCmptnYmd: formatDate(item.demandMatching?.mtchnCmptnYmd),
ruseUtztnFcltNm: item.ruseUtztnFcltNm,
dmndQty: item.dmndQty,
splyFcltFlg: flag,
@@ -272,30 +309,7 @@ export default function RuseRvwHstry() {
<Button
size={"sm"}
onClick={(e) => {
if (fromDate && toDate) {
const fromNum = Number(fromDate);
const toNum = Number(toDate);
if (fromNum > toNum) {
toast.error(
"시작년도가 종료년도 이후로 설정할 수 없습니다."
);
} else {
let con: string | number = "";
if (String(objSearch.city).length > 0) {
con = String(objSearch.city);
}
if (String(objSearch.sgg).length > 0 && objSearch.sgg !== "0") {
con = String(objSearch.sgg);
}
getSearch(
fromNum,
toNum,
con,
dmdCoNm,
ruseUtztnFcltNm
);
}
}
getButtonSearch()
}}
>
조회
@@ -318,17 +332,23 @@ export default function RuseRvwHstry() {
tableData={rvwHistData || []}
headlist={ruseHistHeadList}
pageSize={20}
total={100000}
total={rvwHistCount}
cellClick={(index: number | undefined, headId: string) => {
TableCellClick(index);
}}
onPageChange={setPageNatiion}
onPageChange={
(page)=>{
setPageNatiion(page)
}
}
></UiTable>
<hr />
{/* <hr />
<h3 className="mt-4 ml-2 text-2xl font-medium text-label">
수요신청 상세현황
</h3>
<ConCostForm form={form} type={"hist"} />
<ConCostForm form={form} type={"hist"} selectionVersion={0} /> */}
</ListDiv>
</div>
</div>
+47 -22
View File
@@ -5,6 +5,7 @@ import { btnClass } from "@/app/store/common/code";
import { fetchType, getFetch } from "@/components/lib/api";
import {
excelDownload,
formatPhoneNumber,
getCity,
tableData
} from "@/components/lib/func/commom";
@@ -26,6 +27,7 @@ import ListDiv from "@common/business_components/ui/list_div";
import SdmInsertForm from "@common/business_components/ui/sdmInsertForm";
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
import { Button } from "@common/components/ui";
import moment from "moment";
import { useCallback, useEffect, useState } from "react";
import toast, { Toaster } from "react-hot-toast";
@@ -59,17 +61,29 @@ export default function CoSttn() {
const [seartchTxt, setSearchTxt] = useState<string>();
const [upadateData, setUpdateData] = useState<DemandCompType>();
const getSelCity = (val: string | number) => {
const cityValue = String(val); // Ensure val is treated as a string
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
setObjSearch({ city: cityValue, sgg: "" });
};
const getSelCity = useCallback((val: string | number) => {
const cityValue = String(val);
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
// 시/도를 변경하면 시/군/구는 '전체'("0")로 초기화합니다.
setObjSearch({ city: cityValue, sgg: "0" });
}, [code.sggCd]);
const getSgg = (val: string | number) => {
setObjSearch({ ...objSearch, sgg: String(val) });
};
const getSgg = useCallback((val: string | number) => {
setObjSearch((prev) => ({ ...prev, sgg: String(val) }));
}, []);
useEffect(() => {
// code.cityCd 데이터가 로드되었고, 아직 시/도 선택이 안된 초기 상태일 때 실행
if (code.cityCd && code.cityCd.length > 0 && !objSearch.city) {
const firstCityVal = code.cityCd[0]?.val;
if (firstCityVal) {
// 첫 번째 시/도 값으로 상태를 초기화합니다.
getSelCity(firstCityVal);
}
}
}, [code.cityCd, objSearch.city, getSelCity]);
useEffect(() => {
if (!coSttn.isInit) {
coSttnInit("1", "13", "0");
@@ -77,15 +91,21 @@ export default function CoSttn() {
}, [coSttn.isInit, coSttnInit]);
useEffect(() => {
if (coSttnRes?.length > 0 && code.cityCd.length > 0) {
const data = tableData(coSttnRes, { code });
const chData = data.map((item: any) => ({
...item,
}));
// .filter((item: any) => item !== undefined);
setBdstTable(chData);
} else if (coSttnRes.length == 0) {
setBdstTable([]);
if(coSttnRes){
if (coSttnRes?.length > 0 && code.cityCd.length > 0) {
const data = tableData(coSttnRes, { code });
const chData = data.map((item: any) => ({
...item,
telno:formatPhoneNumber(item.telno||undefined),
regDt: moment(item.regDt).format("YYYY-MM-DD")
}));
// .filter((item: any) => item !== undefined);
setBdstTable(chData);
} else if (coSttnRes?.length == 0) {
setBdstTable([]);
}
}
}, [coSttnRes, code]);
@@ -116,8 +136,11 @@ export default function CoSttn() {
const searchClick = () => {
const con = (objSearch.sgg && objSearch.sgg !== "0") ? objSearch.sgg : objSearch.city;
if (con) {
if (seartchTxt && seartchTxt?.length > 0) {
console.log(' seartchTxt ', seartchTxt)
coSttnTxtSearch("1", "13", String(con), seartchTxt ?? "");
} else {
coSttnSearch("1", "13", String(con));
@@ -139,13 +162,15 @@ export default function CoSttn() {
selectClass={""}
selectArray={code.cityCd || []}
onSelectValue={getSelCity}
value={objSearch.city}
/>
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl"
label="시군구"
selectClass={""}
selectArray={selectSgg?selectSgg : [{text:"전체",val:"0"}]}
selectArray={selectSgg || []}
onSelectValue={getSgg}
value={objSearch.sgg}
/>
<SearchInput
className={"!text-3xl"}
@@ -159,7 +184,7 @@ export default function CoSttn() {
</SearchFormLeft>
<SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="flex flex-row space-x-2">
<Button size={"sm"} onClick={searchClick}>
<Button className="mt-2" size={"sm"} onClick={searchClick}>
조회
</Button>
<Button
@@ -179,7 +204,7 @@ export default function CoSttn() {
onClick={() => {
/* 저장 로직 */
if (!upadateData) {
<ToastFormUi body={"수요처 변경을 원하는 행을 선택해주세요"} title={"수요처관리"}></ToastFormUi>;
toast.error("수요처 변경을 원하는 행을 선택해주세요")
}else{
setIsDmdModal({
...isDmdModal,
@@ -187,7 +212,7 @@ export default function CoSttn() {
}
}}
>
저장
수정
</Button>
<Button
size={"sm"}
+193 -49
View File
@@ -1,42 +1,126 @@
"use client";
import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import type { customCoSttnBdst } from "@/app/store/matDmd/coSttn";
import type { customDrgt } from "@/app/store/matDmd/drgtSttn";
import { drgtHeadListExport } from "@/app/store/matDmd/drgtSttn/headList";
import { excelDownload, getCity } from "@/components/lib/func/commom";
import { getCity } from "@/components/lib/func/commom";
import DrgtDetailModal from "@/components/ui/modal/coMat/drgtDetailModal";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import { ContentsDiv, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui";
import { ContentsDiv, DateMonthPicker, downloadExcel, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiModal, UiTable, type SelectDataType } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, DatePicker, Label } from "@common/components/ui";
import { useCallback, useState } from "react";
import { Button, Label, ScrollArea } from "@common/components/ui";
import { useEffect, useState } from "react";
import { toast, Toaster } from "react-hot-toast";
export default function DrgtSttn() {
const { code, } = useWrStore((state) => state);
const [fromDate, setFromDate] = useState<Date>(new Date());
const [toDate, setToDate] = useState<Date>(new Date());
const { code, drgt, drgtSearch, drgtModalSearch, drgtInit } = useWrStore((state) => state);
const { drgtRes, drgtMoRes } = drgt;
const [fromDate, setFromDate] = useState<string>();
const [toDate, setToDate] = useState<string>();
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>();
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [tableData, setTableData] = useState<customDrgt[]>();
const [selectedSgg, setSelectedSgg] = useState<string>('');
const [excelData, setExcelData] = useState<any[]>();
const [drgtModal, setDrgtModal] = useState<{
isOpen: boolean;
data: customDrgt | undefined;
}>({ data: undefined, isOpen: false });
const cellClick = (index: number | undefined, headId: string) => {
if (fromDate && toDate) {
drgtModalSearch(fromDate, toDate, drgtRes[index].sggNm);
} else {
toast.error("조회기간을 선택해주세요.");
}
}
const excelClick = () => {
downloadExcel({
data: excelData ?? [],
fileName: "우리동네가뭄 현황",
sheetNameArray: ["Sheet1"],
headerRange: "A1:G1"
})
}
useEffect(() => {
if (!drgt.isInit) {
drgtInit();
}
}, [drgt.isInit, drgtInit]);
useEffect(() => {
let resData = Array.isArray(drgtRes) ? drgtRes : [drgtRes];
resData = resData.map((item) => {
if (item.sggNm === '세종시') {
return {
...item,
ctpvCd: '세종특별자치시',
sggNm: ''
}
}
return {
...item,
ctpvCd: code.cityCd.find(ct => (ct.text == item.sggNm || (code.sggCd.find(sgg => sgg.text == item.sggNm)?.val.substring(0, 2) == ct.val)))?.text,
sggNm: code.sggCd.find(sgg => sgg.text == item.sggNm)?.text
}
})
const data = Array.isArray(resData)
? resData.map((item: customDrgt) => {
return {
"시도": item.ctpvCd,
"시군구": item.sggNm,
"정상(횟수)": item.normal,
"관심(횟수)": item.attention,
"주의(횟수)": item.caution,
"관심+주의(횟수)": item.warning,
"가뭄순위": item.ranking,
}
}) : [];
setExcelData(data)
setTableData(resData)
}, [drgtRes])
useEffect(() => {
if (drgtMoRes && drgtMoRes.length > 0) {
setDrgtModal({ isOpen: true, data: drgtMoRes });
}
}, [drgtMoRes]);
const getSelCity = (val: string | number) => {
const cityValue = String(val); // Ensure val is treated as a string
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
setObjSearch({ city: cityValue, sgg: '' });
const cityValue = String(val);
if (cityValue === "0") {
setSelectSgg([]);
setSelectedSgg('');
setObjSearch({ city: cityValue, sgg: '' });
} else {
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? "");
setSelectedSgg(firstSggVal);
setObjSearch({ city: cityValue, sgg: firstSggVal });
}
};
const getSgg = (val: string | number) => {
console.log(val);
setObjSearch({ ...objSearch, sgg: String(val) })
setSelectedSgg(String(val));
setObjSearch({ ...objSearch, sgg: String(val) });
};
const excelClick = useCallback(() => {
excelDownload(
[[]],
"우리동네가뭄",
"sample_drgtSttn.xlsx",
"A1:H1"
);
}, []);
useEffect(() => {
if (fromDate === undefined && toDate === undefined) {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const formatted = `${year}${month}`;
setFromDate(formatted);
setToDate(formatted);
}
}, [fromDate, toDate])
return (
<>
<ContentsDiv>
@@ -54,23 +138,48 @@ export default function DrgtSttn() {
onSelectValue={getSelCity}
/>
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl"
className="col-span-2 w-1/6 h-full text-2xl ml-8"
label="시군구"
selectClass={""}
selectArray={selectSgg || []}
onSelectValue={getSgg}
value={selectedSgg}
/>
<div className="col-span-5 w-auto">
<div className="flex flex-row items-center">
<Label className="text-xl text-gray-600">조회기간</Label>
<Label className="text-xl text-gray-600 ml-8">조회기간</Label>
<div className="flex flex-row w-1/4">
<DatePicker mode={'single'}
selected={fromDate}
onSelect={(date: Date | undefined) => { if (date) setFromDate(date) }} />
<span>~</span>
<DatePicker mode={'single'}
selected={toDate}
onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} />
<DateMonthPicker
field={{
value: fromDate,
onChange: (date: string) => {
const year = date.slice(0, 4);
let month = date.slice(4);
if (month.length < 2) {
month = month.padStart(2, "0");
}
const formatted = `${year}${month}`;
setFromDate(formatted);
}
}}
selectClass="ml-4 text-2xl"
/>
<span className="ml-2 text-center">~</span>
<DateMonthPicker
field={{
value: toDate,
onChange: (date: string) => {
const year = date.slice(0, 4);
let month = date.slice(4);
if (month.length < 2) {
month = month.padStart(2, "0");
}
const formatted = `${year}${month}`;
setToDate(formatted);
}
}}
selectClass="ml-4"
/>
</div>
</div>
</div>
@@ -78,20 +187,42 @@ export default function DrgtSttn() {
</SearchFormLeft>
<SearchFormRight
className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="flex flex-row space-x-2">
<div className="flex flex-row space-x-2 items-center">
<Button
size={"sm"}
onClick={(e) => { }}
onClick={(e) => {
let sidoText = '';
let sggText = [];
if (objSearch?.city.toString().length > 0) {
sidoText = code.cityCd.find((item) => item.val === objSearch?.city)?.text ?? '';
}
if (objSearch?.sgg.toString().length > 1) {
sggText = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? '';
} else {
sggText = code.sggCd.filter((item) => item.val.substring(0, 2) === objSearch?.city).map((item) => item.text);
}
if (sidoText === '전체') { sidoText = '' }
if (sidoText === '세종특별자치시') {
sidoText = '세종시';
sggText = sidoText;
}
const searchArr = [];
if (sidoText) searchArr.push(sidoText);
if (Array.isArray(sggText)) {
searchArr.push(...sggText);
} else if (sggText) {
searchArr.push(sggText);
}
drgtSearch(fromDate ?? "", toDate ?? "", searchArr)
}}
>
조회
</Button>
<Button
size={"sm"}
className={btnClass}
onClick={() => { }}
>
상세보기
</Button>
<Button
size={"sm"}
className={`${btnClass} green`}
@@ -114,18 +245,31 @@ export default function DrgtSttn() {
<div className="grid grid-cols-6 gap-4 ">
<div className="col-span-6">
<ListDiv>
<UiTable
headName={[]}
tableData={[]}
headlist={drgtHeadListExport}
pageSize={20}
total={10000}
cellClick={(index: number | undefined, headId: string) => { }}
onPageChange={() => { }}
></UiTable>
<ScrollArea className="h-[66vh]">
<UiTable
headName={undefined}
tableData={tableData || []}
headlist={drgtHeadListExport}
pageSize={14}
total={0}
cellClick={(index: number | undefined, headId: string) => {
cellClick(index ?? undefined, headId);
}}
onPageChange={() => { }}
></UiTable>
</ScrollArea>
</ListDiv>
</div>
</div>
<UiModal
open={drgtModal.isOpen}
onOpenChange={() => {
setDrgtModal({ ...drgtModal, isOpen: !drgtModal.isOpen });
}}
bodyClass={"w-[50vw] max-w-none h-[80vh]"}
body={<DrgtDetailModal data={drgtModal.data} />}
/>
<Toaster position="top-center" />
</ContentsDiv>
</>
)
+50 -31
View File
@@ -44,7 +44,7 @@ export default function LdrmntCoSttn() {
const [searchTxt, setSearchTxt] = useState<string>();
const ref = useRef<ReturnType>({} as ReturnType);
const [excelData, setExcelData] = useState<any[]>();
const [isExcelDownload, setIsExcelDownload] = useState(false);
const excelClick = useCallback(() => {
excelDownload(
@@ -57,14 +57,22 @@ export default function LdrmntCoSttn() {
}, []);
const excelDownloadClick = () => {
downloadExcel({
data: excelData ?? [],
fileName: "잠재수요처 관리",
sheetNameArray: ["Sheet1"],
headerRange: "A1:F1"
})
ldrmntSearch(1, "10000", "0", "");
setIsExcelDownload(true);
}
useEffect(() => {
if (isExcelDownload && excelData && excelData.length > 0) {
downloadExcel({
data: excelData,
fileName: "잠재수요처 관리",
sheetNameArray: ["Sheet1"],
headerRange: "A1:F1"
});
setIsExcelDownload(false);
}
}, [excelData, isExcelDownload]);
const [isExcelOpen, setIsExcelOpen] = useState<{
isModal: boolean;
}>({ isModal: false });
@@ -73,14 +81,21 @@ export default function LdrmntCoSttn() {
if (index !== undefined) {
setDetailModal({ isOpen: true, data: ldrmntRes[index] });
}
}
const getSelCity = (val: string | number) => {
const cityValue = String(val); // Ensure val is treated as a string
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
setObjSearch({ city: cityValue, sgg: '' });
const cityValue = String(val);
if (cityValue === "0") {
setSelectSgg([]);
setObjSearch({ city: cityValue, sgg: "" });
} else {
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? "");
setObjSearch({ city: cityValue, sgg: firstSggVal });
}
};
const getSgg = (val: string | number) => {
@@ -99,8 +114,8 @@ export default function LdrmntCoSttn() {
const tbData = Array.isArray(data)
? data.map((item: customLdrmnt) => ({
...item,
ctpvCd: item.fctryAddr.split(" ")[0] || "",
sggCd: item.fctryAddr.split(" ")[1] || ""
ctpvCd: item.fctryAddr?.split(" ")[0] || "",
sggCd: item.fctryAddr?.split(" ")[1] || ""
}))
: [];
@@ -114,8 +129,8 @@ export default function LdrmntCoSttn() {
const data = Array.isArray(exData)
? exData.map((item: customLdrmnt) => {
return {
"시도": item.fctryAddr.split(" ")[0] || "",
"시군구": item.fctryAddr.split(" ")[1] || "",
"시도": item.fctryAddr?.split(" ")[0] || "",
"시군구": item.fctryAddr?.split(" ")[1] || "",
"회사명": item.dmdCoNm,
"공장주소": item.fctryAddr,
"공장관리번호": item.fctryMngNo,
@@ -125,10 +140,9 @@ export default function LdrmntCoSttn() {
setExcelData(data)
}, [excelList])
useEffect(() => {
let cityTxt = '';
let sggTxt = '';
let cityTxt = ''
let sggTxt = ''
if (objSearch?.city.toString().length > 0) {
cityTxt = code.cityCd.find((item) => item.val === objSearch?.city)?.text ?? '';
@@ -138,21 +152,23 @@ export default function LdrmntCoSttn() {
sggTxt = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? '';
}
let cityNm = "";
if (cityTxt === '전체' || cityTxt === '') {
cityTxt = '0'
}
let cityNm = ""
if (sggTxt !== "0") {
cityNm = cityTxt + " " + sggTxt;
cityNm = cityTxt + " " + sggTxt
} else {
cityNm = cityTxt;
cityNm = cityTxt
}
if (cityNm) {
if (cityTxt === '') cityNm = "0";
ldrmntSearch(pageNation, cityNm, searchTxt || "");
ldrmntSearch(pageNation, '20', cityNm, searchTxt || "");
} else {
ldrmntSearch(pageNation, "", searchTxt || "");
ldrmntSearch(pageNation, '20', '0', searchTxt || "");
}
}, [pageNation, objSearch, code, searchTxt, ldrmntSearch])
}, [pageNation]);
return (
<>
@@ -175,6 +191,7 @@ export default function LdrmntCoSttn() {
label="시군구"
selectClass={""}
selectArray={selectSgg || []}
value={objSearch.sgg} // value props 추가
onSelectValue={getSgg}
/>
<SearchInput
@@ -189,7 +206,7 @@ export default function LdrmntCoSttn() {
</SearchFormLeft>
<SearchFormRight
className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="flex flex-row space-x-2">
<div className="flex flex-row space-x-2 items-center">
<Button
size={"sm"}
onClick={(e) => {
@@ -205,6 +222,10 @@ export default function LdrmntCoSttn() {
sggTxt = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? '';
}
if (cityTxt === '전체' || cityTxt === '') {
cityTxt = '0'
}
let cityNm = ""
if (sggTxt !== "0") {
@@ -212,10 +233,8 @@ export default function LdrmntCoSttn() {
} else {
cityNm = cityTxt
}
if (cityNm) {
if (cityTxt === '') cityNm = "0";
ldrmntSearch(pageNation, cityNm, searchTxt || "");
ldrmntSearch(pageNation, '20', cityNm, searchTxt || "");
}
}}
>
+12
View File
@@ -153,6 +153,18 @@ export default function Home() {
</div>
</div>
</div>
{/* 시설 현황(지도) */}
<div>
<h2 className="text-xl font-semibold mb-4">시설 현황(지도)</h2>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-2">
<Button className="w-full" onClick={() => {router.push('/map/htdflSttn')}}>
시설 현황(지도)
</Button>
</div>
</div>
</div>
</div>
</>
);
@@ -16,73 +16,80 @@ import { Button } from "@common/components";
import ListDiv from "@common/business_components/ui/list_div";
import { useEffect, useState } from "react";
import { tableData } from "@/components/lib/func/commom";
import HtdflRegForm from "@/components/ui/form/fcltReg/htdflReg/htdflRegForm";
import HtdflRegForm from "@/components/ui/form/fcltReg/htdflReg/htdflRegForm";
import type { chgBuilding } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal";
import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg";
import { BdstReg } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import { Toaster } from "react-hot-toast";
export default function Home() {
const { code, htdfl, initHtdfl,getHtdflBdstData } = useWrStore((state) => state);
const { code, htdfl, initHtdflReg, getHtdflBdstData ,htdflNmSearch} = useWrStore(
(state) => state
);
const [searchTxt, setSearchTxt] = useState("");
const [pageNation, setPageNation] = useState(1);
const [htdflForm, setHtdflForm] = useState<customHtdflBdst>();
const [bdstTable, setbdstTable] = useState<customHtdflBdst[]>();
const [issNewReg,setIsNewReg] = useState<boolean>( false);
const [issNewReg, setIsNewReg] = useState<boolean>(false);
const [bdstChgProps, setBdstChgProps] = useState<{
isOpen: boolean;
chgBuilding: chgBuilding[];
}>({ isOpen: false, chgBuilding: [] });
const [bdstChgProps, setBdstChgProps] = useState<{
isOpen: boolean;
chgBuilding: chgBuilding[];
}>({ isOpen: false, chgBuilding: [] });
const { htdflRes } = htdfl;
useEffect(() => {
if (!htdfl.isInit) {
initHtdfl('0');
initHtdflReg('41')
}
}, [htdfl.isInit, initHtdfl]);
}, [htdfl.isInit, initHtdflReg]);
useEffect(() => {
if (code.sggCd.length > 0) {
if (htdflRes&&htdflRes.length>0) {
const htdflData = tableData(htdflRes, { code }).map(
(item: customHtdflBdst) => {
return {
...item,
bdstChgYn: item?.chgBuilding && item.chgBuilding.length > 0 && item.chgBuilding[0]?.bdstChgNm !== null ? 'Y' : 'N',
bdstUsgCd: item.reuseBuilding[0]?.bdstUsgCd
};
}
);
setbdstTable(htdflData);
console.log('htdflRes ', htdflRes)
const htdflData = tableData(htdflRes, { code }).map(
(item: customHtdflBdst) => {
let archArea = item?.archArea.split('.')[0]!=='-1'?item?.archArea.split('.')[0]:'확인불가'
return {
...item,
archArea:archArea
};
}
);
setbdstTable(htdflData);
}else{
setbdstTable([]);
}
}, [code, htdflRes]);
const cellClick: (val: number|undefined ,headId: string) => void = (
val
,headId) => {
const cellClick: (val: number | undefined, headId: string) => void = (
val,
headId
) => {
if ((bdstTable ?? []).length > 0 && val !== undefined) {
{
setHtdflForm(htdflRes[val]);
}
}
if (headId === "bdstChgYn" && val !== undefined) {
if (
htdflRes[val]?.bdstInfoPk != null &&
htdflRes[val]?.chgBuilding?.length > 0
) {
bdstChgModal(htdflRes[val]?.chgBuilding as chgBuilding[]);
}
}
};
const bdstChgModal = (chgBuilding: chgBuilding[]) => {
setBdstChgProps({ isOpen: true, chgBuilding: chgBuilding });
// if (headId === "bdstChgYn" && val !== undefined) {
// if (
// htdflRes[val]?.bdstInfoPk != null &&
// htdflRes[val]?.chgBuilding?.length > 0
// ) {
// bdstChgModal(htdflRes[val]?.chgBuilding as chgBuilding[]);
// }
// }
};
// const bdstChgModal = (chgBuilding: chgBuilding[]) => {
// setBdstChgProps({ isOpen: true, chgBuilding: chgBuilding });
// };
useEffect(() => {
getHtdflBdstData(pageNation, "0");
getHtdflBdstData(pageNation, "41");
}, [pageNation]);
return (
@@ -103,14 +110,23 @@ export default function Home() {
/>
</SearchFormLeft>
<SearchFormRight>
<div className="flex flex-row space-x-2 items-center">
<Button size={"sm"} onClick={()=>{initHtdfl(searchTxt||"")}}>조회</Button>
<Button
<div className="flex flex-row space-x-2 items-center">
<Button
size={"sm"}
onClick={() => setIsNewReg(!issNewReg)}
>
신규등록
</Button>
onClick={() => {
if(searchTxt&&searchTxt.length>0){
htdflNmSearch(String(41),searchTxt)
}else{
initHtdflReg(String(41) );
}
}}
>
조회
</Button>
<Button size={"sm"} onClick={() => setIsNewReg(!issNewReg)}>
신규등록
</Button>
</div>
</SearchFormRight>
</SearchDiv>
@@ -126,41 +142,50 @@ export default function Home() {
pageSize={100}
total={10}
headName={"건축물 목록"}
cellClick={(val, headId) => { cellClick(val || undefined, headId); } }
cellClick={(val, headId) => {
cellClick(val || undefined, headId);
}}
onPageChange={setPageNation}
>
</UiTable>
></UiTable>
</ListDiv>
</div>
<div className="col-span-2 max-h-full">
<ListDiv>
<ScrollArea className="h-[60vh]">
<HtdflRegForm
htdflForm={htdflForm ?? ({} as customHtdflBdst)}
isModal={false}
/>
</ScrollArea>
<ListDiv>
<ScrollArea className="h-[60vh]">
<HtdflRegForm
htdflForm={htdflForm ?? ({} as customHtdflBdst)}
isModal={false}
/>
</ScrollArea>
</ListDiv>
</div>
</div>
<UiModal
body={<BdstReg moType="HTB"></BdstReg>}
bodyClass="w-[68vw] max-w-none h-[90vh]"
open={issNewReg}
onOpenChange={(open) => {
setIsNewReg(open);
}}
/>
<UiModal
<UiModal
body={
<BdstReg
moType="HTB"
isModal={false}
setIsModal={setIsNewReg}
></BdstReg>
}
bodyClass="w-[68vw] max-w-none h-[90vh]"
open={issNewReg}
onOpenChange={(open) => {
setIsNewReg(open);
}}
/>
<Toaster position="top-center" />
{/* <UiModal
bodyClass={""}
open={bdstChgProps.isOpen}
body={<BdstChgModal chgBuilding={bdstChgProps.chgBuilding}></BdstChgModal>}
body={
<BdstChgModal chgBuilding={bdstChgProps.chgBuilding}></BdstChgModal>
}
onOpenChange={(open) => {
setBdstChgProps({ ...bdstChgProps, isOpen: open });
}
}
/>
}}
/> */}
</ContentsDiv>
</>
);
+213 -102
View File
@@ -29,18 +29,22 @@ import {
import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm";
import { PbnstBuseForm } from "@/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm";
import { PbnstRegModal } from "@/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal";
import toast, { Toaster } from "react-hot-toast";
import toast, { Toaster } from "react-hot-toast"; // toast 이미 import 되어 있음
import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg";
import { useFieldArray, useForm } from "react-hook-form";
import { useFieldArray, useForm, type FieldErrors } from "react-hook-form"; // FieldErrors 타입 추가
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createDefaultPbnstItem } from "./utilsZod";
export default function PublicReuseFaciltyInsert() {
const {
code,
pbnst,
searchFclts,
pubHasuInit,
pbnstInsert,
pbnstRegClick,
pbnstRuseFcltUpdate,
pubHasuSearch,
@@ -50,17 +54,15 @@ export default function PublicReuseFaciltyInsert() {
const [currentTab, setCurrentTab] = useState<string>("mng");
const [isRuseReg, setIsRuseReg] = useState<{
isModal: boolean;
data: pubHasuType;
}>({ isModal: false, data: {} as pubHasuType });
}>({ isModal: false });
const [indexMng, setIndexMng] = useState<number>(0);
const [indexBiz, setIndexBiz] = useState<number>(0);
const [searchTxt, setSearchTxt] = useState<string>();
const [bdstTable, setbdstTable] = useState<pubHasuType[]>();
const [selectRow, setSelectRow] = useState<pubHasuType>();
const [pageNation, setPageNation] = useState<number>(1);
const form = useForm<{
items: z.infer<typeof pbnstRegZod>[];
}>({
@@ -68,6 +70,8 @@ export default function PublicReuseFaciltyInsert() {
// defaultValues: { items: pbnstRegArrayZod.parse([]) },
mode: "onChange",
});
const { isDirty } = form.formState;
const {
fields: items,
append,
@@ -76,6 +80,8 @@ export default function PublicReuseFaciltyInsert() {
control: form.control,
name: `items`,
});
const formWatch = form.watch("items");
useEffect(() => {
if (!pbnst.isInit) {
pubHasuInit("1", "41");
@@ -83,37 +89,98 @@ export default function PublicReuseFaciltyInsert() {
}, [pbnst.isInit, pubHasuInit]);
const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => {
const data = writeRgtrAndMdfr(valuse.items);
if (!isDirty) {
toast.error("변경사항이 없습니다."); // 변경사항이 없으면 메시지 표시
return;
} const data = writeRgtrAndMdfr(valuse.items);
let bizs = [] as fcBizType[];
data.forEach((fclt) => {
bizs = [...bizs, ...fclt.fcBizs];
});
const pbnst =data.map((fclt) => {
return({
pbnstRuseFcltPk: fclt.pbnstRuseFcltPk,
fcltCpct: fclt.fcltCpct,
ruseCpct: fclt.ruseCpct,
splyExtraCpct: fclt.splyExtraCpct,
rgtr: fclt.rgtr,
mdfr: fclt.mdfr,
fcFiles: fclt.fcFiles,
fcOpers: fclt.fcOpers,
})
})
await pbnstRuseFcltUpdate(pbnst as customPbnst[]);
const pbnst = data.map((fclt) => {
return {
pbnstRuseFcltPk: fclt.pbnstRuseFcltPk,
fcltCpct: fclt.fcltCpct,
ruseCpct: fclt.ruseCpct,
splyExtraCpct: fclt.splyExtraCpct,
rgtr: fclt.rgtr,
mdfr: fclt.mdfr,
fcFiles: fclt.fcFiles,
fcOpers: fclt.fcOpers,
};
});
await pbnstRuseFcltUpdate(pbnst as customPbnst[])
await pbnstRuseBizUpdate(bizs);
};
useEffect(() => {
if (code.sggCd.length) {
if (searchFclts) {
setbdstTable(tableData(searchFclts, { code }));
} else if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) {
setbdstTable(tableData(pbnstHasuRes, { code }));
}
// 폼 유효성 검사 실패 시 호출될 함수
const onInvalid = (errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }>) => {
console.log("errors:", errors);
const findAllErrors = (
errorObject: any,
path: (string | number)[] = []
): { message: string; path: (string | number)[] }[] => {
if (!errorObject) return [];
return Object.keys(errorObject).flatMap((key) => {
const value = errorObject[key];
const currentKey = !isNaN(parseInt(key)) ? parseInt(key) : key;
const newPath = [...path, currentKey];
if (value && typeof value === "object") {
if (value.message) {
return [{ message: value.message, path: newPath }];
}
return findAllErrors(value, newPath);
}
return [];
});
};
const errorList = findAllErrors(errors);
if (errorList.length > 0) {
errorList.forEach((errorInfo) => {
const { message, path } = errorInfo;
let prefix = "";
const itemsIndex = path.indexOf("items");
if (itemsIndex > -1 && path.length > itemsIndex + 1) {
const itemIdx = path[itemsIndex + 1] as number;
const facilityName = form.getValues(`items.${itemIdx}.ruseUtztnFcltNm`);
if (facilityName) {
prefix += `[${facilityName}] `;
}
const fcBizsIndex = path.indexOf("fcBizs");
if (fcBizsIndex > -1 && path.length > fcBizsIndex + 1) {
const bizIdx = path[fcBizsIndex + 1] as number;
const cycle = form.getValues(`items.${itemIdx}.fcBizs.${bizIdx}.cycl`);
if (cycle) {
prefix += `[${cycle}차수] `;
}
}
}
// 최종 오류 메시지 출력
toast.error(`${prefix}${message}`);
});
}
}, [code, pbnstHasuRes, searchFclts]);
};
useEffect(() => {
console.log("form errors:", form.formState.errors);
}, [form.formState.errors]);
useEffect(() => {
if (code.sggCd.length && pbnstHasuRes.length > 0) {
setbdstTable(tableData(pbnstHasuRes, { code }));
}
}, [code, pbnstHasuRes]);
const cellClick = (val: number | undefined) => {
if (val === undefined) return;
@@ -121,6 +188,8 @@ export default function PublicReuseFaciltyInsert() {
setSelectRow(pbnstHasuRes?.[val]);
if (selectRow) {
pbnstRegClick(selectRow);
} else {
console.error("선택된 행이 없습니다.");
}
};
@@ -129,12 +198,13 @@ export default function PublicReuseFaciltyInsert() {
};
useEffect(() => {
if (pbnst.pbnstRes&& pbnst.pbnstRes?.length>0) {
console.log("pbnst.pbnstRes", pbnstRes);
form.reset({ items: pbnstRegArrayZod.parse(pbnst.pbnstRes) });
}else{
if (pbnst.pbnstRes && pbnst.pbnstRes?.length > 0) {
console.log("pbnst.pbnstRes is null", pbnstRes);
form.reset({ items: pbnst.pbnstRes });
} else {
form.reset({ items: [] });
}
}, [pbnst.pbnstRes]);
@@ -150,52 +220,53 @@ export default function PublicReuseFaciltyInsert() {
const appendPbnstRuseFclt = useCallback(() => {
const selecTableRow = selectRow;
if (pbnstRes && pbnstRes.length > 0) {
const lastPbnst = pbnstRes[pbnstRes.length - 1];
if (lastPbnst && selecTableRow) {
// append(
// pbnstRegZod.parse({
// pbnstRuseFcltPk:
// String(selecTableRow.bscFctCd) + String(pbnstRes.length + 1),
// ruseUtztnFcltNm: null,
// fcBizs: [
// fcBizSchema.parse({
// pbnstRuseFcltFk:
// String(lastPbnst.pbnstRuseFcltPk) +
// String(pbnstRes.length + 1),
// cycl: 1,
// }),
// ] as fcBizType[],
// })
// );
append(createDefaultPbnstItem(selecTableRow, pbnstRes.length));
setIsRuseReg({ isModal: true });
}
} else {
if (selecTableRow) {
append(
pbnstRegZod.parse({
pbnstRuseFcltPk:
String(selecTableRow.bscFctCd) + String(pbnstRes.length + 1),
pbnstRuseFcltPk: String(selecTableRow.bscFctCd) + "1",
ruseUtztnFcltNm: "",
regDt: null,
mdfcnDt: null,
fcBizs: [
fcBizSchema.parse({
pbnstRuseFcltFk:
String(lastPbnst.pbnstRuseFcltPk) +
String(pbnstRes.length + 1),
pbnstRuseFcltFk: String(selecTableRow.bscFctCd) + "1",
cycl: 1,
}),
] as fcBizType[],
})
);
setIsRuseReg({ ...isRuseReg, isModal: true });
}
} else {
if (selecTableRow) {
append(
pbnstRegZod.parse({
pbnstRuseFcltPk: String(selecTableRow.bscFctCd)+'1',
ruseUtztnFcltNm: "",
regDt: null,
mdfcnDt: null,
fcBizs: [fcBizSchema.parse({pbnstRuseFcltFk:String(selecTableRow.bscFctCd)+'1',cycl:1})] as fcBizType[],
})
)
setIsRuseReg({ ...isRuseReg, isModal: true });
setIsRuseReg({ isModal: true });
}
}
}, [pbnstRegZod, pbnstRes, append, selectRow, form]);
useEffect(() => {
// console.log(" page getgetvaslue ", form.getValues());
}, [form.getValues()]);
pubHasuInit(String(pageNation), "41");
}, [pageNation, pubHasuInit]);
useEffect(() =>{
pubHasuInit(String(pageNation), "41");
},[pageNation,pubHasuInit])
return (
<>
<ContentsDiv>
@@ -206,7 +277,7 @@ useEffect(() =>{
<SearchFormLeft>
<SearchInput
className={"col-span-3 my-7 !text-3xl"}
label="건물명"
label="시설명"
textValue={searchTxt || ""}
setText={(e) => {
setSearchTxt(e);
@@ -228,7 +299,7 @@ useEffect(() =>{
size={"sm"}
className=""
onClick={() => {
// pbnstMngData([] as customPbnst[]);
// pbnstMngData([] as customPbnst[]);
if (!selectRow) {
toast.error(
"하수리시시설을 테이블을 선택하고 등록버튼을 눌러주세요"
@@ -251,6 +322,8 @@ useEffect(() =>{
<div className="col-span-4 h-full min-h-0">
<ListDiv className="min-h-0">
<ScrollArea className="h-[60vh]">
{" "}
{/* 높이 조정 */}
<UiTable
tableData={bdstTable || []}
headlist={pubFacHeadList}
@@ -266,59 +339,97 @@ useEffect(() =>{
</div>
<ListDiv className="col-span-2 pr-6 ">
<ScrollArea className="h-[60vh]">
<Tabs defaultValue="mng" onValueChange={handleTabChange}>
<TabsList className="">
<TabsTrigger className="!text-xl" value="mng">
시설정보
</TabsTrigger>
<TabsTrigger className="!text-xl" value="buse">
차수별정보
</TabsTrigger>
<Button
{formWatch && formWatch[0]?.pbnstRuseFcltPk ? (
<Tabs defaultValue="mng" onValueChange={handleTabChange}>
<TabsList className="">
<TabsTrigger className="!text-xl" value="mng">
시설정보
</TabsTrigger>
<TabsTrigger className="!text-xl" value="buse">
차수별정보
</TabsTrigger>
<Button
size={"sm"}
onClick={form.handleSubmit(onSubmit, onInvalid)}
className="!justify-end" // 저장 버튼 위치 조정
>
저장
</Button>
</TabsList>
<TabsContent className="max-h-full" value="mng">
<PbnstMngForm
formType="update"
form={form}
setIndexMng={(index: number) => {
console.log("setIndexMng", index);
setIndexMng(index);
}}
indexMng={indexMng}
/>
</TabsContent>
<TabsContent className="max-h-full" value="buse">
<PbnstBuseForm
formType="update"
form={form}
setIndexBiz={(index: number) => {
setIndexBiz(index);
}}
indexMng={indexMng}
indexBiz={indexBiz}
/>
</TabsContent>
</Tabs>
) : (
<>
<div className="card-header text-2xl">
등록된 재이용시설 정보가 없습니다.
</div>
<div className="!mx-[1.6rem] !p-0r flex flex-col items-center justify-center h-64 border rounded-md">
<p className="text-3xl mb-4">
신규등록 버튼을 클릭하여 재이용시설을 등록해주세요.
</p>
<Button
size={"sm"}
onClick={form.handleSubmit(onSubmit)}
className="!justify-end"
className=""
onClick={() => {
// pbnstMngData([] as customPbnst[]);
if (!selectRow) {
toast.error(
"하수리시시설을 테이블을 선택하고 등록버튼을 눌러주세요"
);
return;
} else {
appendPbnstRuseFclt();
}
}}
>
저장
신규등록
</Button>
</TabsList>
<TabsContent className="max-h-full" value="mng">
<PbnstMngForm
formType="update"
form={form}
setIndexMng={(index: number) => {
console.log("setIndexMng", index);
setIndexMng(index);
}}
indexMng={indexMng}
/>
</TabsContent>
<TabsContent className="max-h-full" value="buse">
<PbnstBuseForm
formType="update"
form={form}
setIndexBiz={(index: number) => {
setIndexBiz(index);
}}
indexMng={indexMng}
indexBiz={indexBiz}
/>
</TabsContent>
</Tabs>
</div>
</>
)}
</ScrollArea>
</ListDiv>
<UiModal
body={
<PbnstRegModal
form={form}
data={isRuseReg.data || ({} as customPbnst)}
form={form} // form prop 전달
setPbnstInsert={(data) => {
if (data) {
pbnstInsert(data); // pbnstInsert 함수 호출
setIsRuseReg({ isModal: false });
}
}}
onInvalid ={onInvalid}
></PbnstRegModal>
}
bodyClass="w-[68vw] max-w-none h-[90vh]"
bodyClass="w-[68vw] max-w-none h-[90vh]"
open={isRuseReg.isModal}
onOpenChange={(open) => {
remove(form.getValues("items").length - 1);
setIsRuseReg({ data: {} as pubHasuType, isModal: open });
setIsRuseReg({ isModal: open });
}}
/>
</div>
@@ -0,0 +1,38 @@
import type { fcBizType, pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type";
export function createDefaultPbnstItem(
selecTableRow: pubHasuType,
index: number
) {
const pk = String(selecTableRow.bscFctCd) + String(index + 1);
return {
pbnstRuseFcltPk: pk,
swtrmtFcltFk: "",
ruseUtztnFcltNm: "", // 필수 입력이므로 빈 문자열로
fcltCpct: 0,
ruseCpct: 0,
splyExtraCpct: 0,
regDt: null,
rgtr: "test",
mdfcnDt: null,
mdfr: "test",
fcFiles: [],
fcBizs: [
createDefaultBiz(pk, 1), // 첫 차수
],
fcOpers: [],
};
}
export function createDefaultBiz(
pbnstRuseFcltFk: string,
cycl: number
): fcBizType {
return {
pbnstRuseFcltFk,
cycl,
// 필요한 값 있으면 여기에 추가
} as fcBizType;
}
@@ -21,11 +21,14 @@ import {
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components";
import { useEffect, useState } from "react";
import { Toaster } from "react-hot-toast";
export default function Home() {
const { rain, code, initRain, rainSearch, getRainData } = useWrStore((state) => {
return state;
});
const { rain, code, initRain, rainSearch, getRainData } = useWrStore(
(state) => {
return state;
}
);
const { rainRes } = rain;
const [rainForm, setRainForm] = useState<customRainBdst>();
const [issNewReg, setIsNewReg] = useState<boolean>(false);
@@ -38,8 +41,9 @@ export default function Home() {
}>({ isOpen: false, chgBuilding: [] });
useEffect(() => {
setIsNewReg(false);
if (!rain.isInit) {
initRain('41');
initRain("41");
}
}, [rain.isInit, initRain]);
@@ -47,8 +51,11 @@ export default function Home() {
if (code.sggCd.length > 0) {
const rainData = tableData(rainRes, { code }).map(
(item: customRainBdst) => {
let archArea = item?.archArea.split('.')[0]!=='-1'?item?.archArea.split('.')[0]:'확인불가'
return {
...item,
archArea:archArea,
bdstChgYn: item.chgBuilding.length > 0 ? "Y" : "N",
};
}
@@ -80,7 +87,6 @@ export default function Home() {
};
useEffect(() => {
console.log("getPageNation", pageNation);
getRainData(pageNation, "41");
}, [pageNation]);
@@ -129,35 +135,39 @@ export default function Home() {
<div className="col-span-4 h-full min-h-0">
<ListDiv className="min-h-0">
{/* <ScrollArea className="h-[60vh]"> */}
<UiTable
tableData={bdstTable || []}
headlist={rain.upHeadList}
pageSize={20}
total={10000}
headName={"건축물 목록"}
cellClick={(val, headId) => {
cellClick(val, headId);
}}
onPageChange={setPageNation}
>
</UiTable>
<UiTable
tableData={bdstTable || []}
headlist={rain.upHeadList}
pageSize={20}
total={10000}
headName={"건축물 목록"}
cellClick={(val, headId) => {
cellClick(val, headId);
}}
onPageChange={setPageNation}
></UiTable>
{/* </ScrollArea> */}
</ListDiv>
</div>
<div className="col-span-2 max-h-full">
<ListDiv className="">
<ScrollArea className="h-[60vh]">
<RainRegForm
rainForm={rainForm ?? ({} as customRainBdst)}
isModal={false}
></RainRegForm>
<RainRegForm
rainForm={rainForm ?? ({} as customRainBdst)}
isModal={false}
></RainRegForm>
</ScrollArea>
</ListDiv>
</div>
</div>
<UiModal
body={<BdstReg moType="RNB"></BdstReg>}
body={
<BdstReg
moType="RNB"
isModal={issNewReg}
setIsModal={setIsNewReg}
></BdstReg>
}
bodyClass="w-[68vw] max-w-none h-[90vh]"
open={issNewReg}
onOpenChange={(open) => {
@@ -175,6 +185,8 @@ export default function Home() {
setBdstChgProps({ ...bdstChgProps, isOpen: open });
}}
/>
<Toaster position="top-center" />
</ContentsDiv>
</>
);
@@ -20,6 +20,7 @@ import {
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components";
import { useEffect, useState } from "react";
import { Toaster } from "react-hot-toast";
export default function Home() {
const { code, rswt, initRswt, rswtBdstSearch, getRswtBdstData } = useWrStore(
@@ -42,11 +43,16 @@ export default function Home() {
}
}, [rswt.isInit, initRswt]);
useEffect(() => {
if (code.sggCd.length > 0) {
const rswtData = tableData(rswtRes, { code }).map(
if (code.sggCd.length > 0 && rswtRes?.length > 0) {
const rswtData = tableData(rswtRes, { code })?.map(
(item: customRswtBdst) => {
let archArea =
item?.archArea.split(".")[0] !== "-1"
? item?.archArea.split(".")[0]
: "확인불가";
return {
...item,
archArea: archArea,
bdstChgYn: item.chgBuilding[0]?.bdstChgNm != null ? "Y" : "N",
bdstUsgCd: item.reuseBuilding[0]?.bdstUsgCd,
};
@@ -151,7 +157,13 @@ export default function Home() {
</div>
<UiModal
body={<BdstReg moType="RST"></BdstReg>}
body={
<BdstReg
moType="RST"
setIsModal={setIsNewReg}
isModal={issNewReg}
></BdstReg>
}
bodyClass="w-[68vw] max-w-none h-[90vh]"
open={issNewReg}
onOpenChange={(open) => {
@@ -168,6 +180,8 @@ export default function Home() {
setBdstChgProps({ ...bdstChgProps, isOpen: open });
}}
/>
<Toaster position="top-center" />
</ContentsDiv>
</>
);
@@ -8,6 +8,7 @@ import type {
} from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import {
excelDownload,
getCity,
getCityOrSgg,
tableData,
} from "@/components/lib/func/commom";
@@ -16,16 +17,23 @@ import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDeta
import {
ContentsDiv,
SearchBaseForm,
SearchDiv,
SearchFormLeft,
SearchFormRight,
SearchInput,
SelectBox,
Title,
UiModal,
UiTable,
type ReturnType,
type SelectDataType,
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components/ui";
import { Button, Label } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import { excelHtdflSttnParsing } from "@/app/store/ruseFcltMng/fcltSttn/htdflSttn/excel";
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
export default function HtdflSttn() {
const { code, htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore(
@@ -38,6 +46,8 @@ export default function HtdflSttn() {
const [pageNation, setPageNation] = useState(1);
const [isChHead, setIsChHead] = useState(false);
const [bdstTable, setBdstTable] = useState<customRainBdst[]>();
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>()
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [detailModal, setDetailModal] = useState<{
isOpen: boolean;
data: customRainBdst | undefined;
@@ -46,17 +56,47 @@ export default function HtdflSttn() {
isOpen: boolean;
chBuilding: chgBuilding[];
}>({ isOpen: false, chBuilding: [] });
const codeData = getCityOrSgg(ref);
const [fcltNm, setFcltNm] = useState<string>("");
const excelClick = useCallback(() => {
if (codeData) htdflSttnSearch(1, "10000", codeData);
}, [htdflSttnSearch, codeData]);
// 최신 검색 조건 반영
const con = getConValue();
htdflSttnSearch(1, "10000", String(con), fcltNm, code.cityCd, code.sggCd);
}, [htdflSttnSearch, objSearch, code.cityCd, code.sggCd]);
useEffect(() => {
if (codeData != null) htdflSttnSearch(pageNation, "20", codeData);
htdflSttnSearch(pageNation, "20", String(getConValue()), fcltNm, code.cityCd, code.sggCd);
}, [pageNation]);
const getSelCity = (val: string | number) => {
const cityValue = String(val);
if (cityValue === "0") {
setSelectSgg([]);
setObjSearch({ city: cityValue, sgg: "" });
} else {
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? "");
setObjSearch({ city: cityValue, sgg: firstSggVal });
}
};
const getSgg = (val: string | number) => {
setObjSearch({ ...objSearch, sgg: String(val) })
};
const getConValue = () => {
if (!objSearch.city || objSearch.city === "0") {
return "0";
}
if (objSearch.sgg && objSearch.sgg !== "0") {
return objSearch.sgg;
}
return objSearch.city;
};
const cellClick = (index: number | undefined, headId: string) => {
if (
headId !== "chBuildingYn" &&
@@ -67,42 +107,34 @@ export default function HtdflSttn() {
}
};
useEffect(() => {
const excelDown = async () => {
if (htdflSttn.excelList.length > 0) {
if (htdflSttn.excelList.length === 0) return;
const excelData = await tableData(htdflSttn.excelList, { code });
const htdflExcel = excelHtdflSttnParsing(excelData, code.cityCd, code.sggCd);
excelDownload(
htdflExcel as [[]],
"온배수시설 현황",
"sample_htdflSttn.xlsx",
"A1:S3"
);
}
};
excelDown();
if (htdflSttn.excelList.length > 0) {
if (htdflSttn.excelList.length === 0) return;
excelDownload(
htdflSttn.excelList,
"온배수시설 현황",
"sample_htdflSttn.xlsx",
"A1:S3"
);
}
}, [htdflSttn.excelList, code]);
useEffect(() => {
if (!htdflSttn.isInit) {
initHtdfl(codeData ?? "0");
initHtdfl(1, "20", "0", fcltNm, code.cityCd, code.sggCd);
}
}, [htdflSttn.isInit, initHtdfl]);
useEffect(() => {
if (htdflSttnRes.length > 0 && code.cityCd.length > 0) {
const data = tableData(htdflSttnRes, { code });
const chData = data
.map((item: any) => ({
...item,
...item.reuseBuilding[0],
...item.reuseBuilding[0].htdflObj,
chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N",
}))
.filter((item: any) => item.htdflObj !== undefined);
setBdstTable(chData);
}
const data = tableData(htdflSttnRes, { code });
const chData = data
.map((item: any) => ({
...item,
...item.reuseBuilding[0],
...item.reuseBuilding[0].htdflObj,
chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N",
}))
.filter((item: any) => item.htdflObj !== undefined);
setBdstTable(chData);
}, [code, htdflSttnRes, htdflSttn.htdflSttHeadList]);
return (
@@ -111,43 +143,76 @@ export default function HtdflSttn() {
<div className="grid grid-cols-4 gap-4 ">
<div className="col-span-4">
<Title title={"온배수 시설 현황"} />
<SearchBaseForm
selectSidoData={code.cityCd}
selectSigunData={code.sggCd}
selectRegionData={code.rvbsnCd}
selectUnitWaterShedData={code.lgbsnCd}
selectMiddleRegionData={code.mdmbsnCd}
selectEnvPartData={code.lclgvrnmntCd}
rightChildren={<Button
className={btnClass}
onClick={() => {
setIsChHead(true);
} }
>
조회항목설정
</Button>}
leftChildren={undefined}
queryClick={function (returnType: ReturnType): void {
// '{partValue: 'A', sidoValue: '0', sigunValue: '0', regionValue: '0', unitWaterShedValue: '0', …}'
let con;
if (returnType.sigunValue != "0") {
con = returnType.sigunValue;
} else {
con = returnType.sidoValue;
}
htdflSttnSearch(pageNation, "20", con);
} }
excelClick={excelClick}
initClick={function (): void { } }
btnClass={btnClass}
ref={ref} isAdmin={false} />
<SearchDiv>
<SearchFormLeft>
<div className="col-span-10 flex flex-row">
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl"
label="시도"
selectClass={""}
selectArray={code.cityCd}
onSelectValue={getSelCity}
/>
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl ml-4"
label="시군구"
selectClass={""}
selectArray={selectSgg || []}
value={objSearch.sgg} // value props 추가
onSelectValue={getSgg}
/>
<div className="col-span-2 flex flex-row space-x-2">
<Label className="w-28 mt-2 text-xl text-gray-600">
{"시설명"}
</Label>
<SearchInput
className={"w-full h-full text-2xl"}
label={""}
textValue={fcltNm || ""}
setText={setFcltNm}
/>
</div>
</div>
</SearchFormLeft>
<SearchFormRight
className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="flex flex-row space-x-2 items-center">
<Button
size={"sm"}
onClick={() => {
const con = getConValue();
htdflSttnSearch(pageNation, "20", String(con), fcltNm, code.cityCd, code.sggCd);
}}
>
조회
</Button>
<Button
size={"sm"}
className={`${btnClass} green`}
onClick={excelClick}
>
다운로드
</Button>
<Button
size={"sm"}
className={btnClass}
onClick={() => {
setIsChHead(true);
}}
>
조회항목설정
</Button>
</div>
</SearchFormRight>
</SearchDiv>
</div>
</div>
<div className="grid grid-cols-6 gap-4 ">
<div className="col-span-6">
<ListDiv>
<UiTable
headName={[]}
headName={undefined}
tableData={bdstTable || []}
children={
<HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} />
@@ -4,6 +4,7 @@ import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import {
excelDownload,
getCity,
getCityOrSgg,
tableData,
} from "@/components/lib/func/commom";
@@ -11,13 +12,19 @@ import HeadChModal from "@/components/ui/modal/sttn/headChModal/headChModal";
import {
ContentsDiv,
SearchBaseForm,
SearchDiv,
SearchFormLeft,
SearchFormRight,
SearchInput,
SelectBox,
Title,
UiModal,
UiTable,
type ReturnType,
type SelectDataType,
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components/ui";
import { Button, Label } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import type {
chgBuilding,
@@ -26,6 +33,7 @@ import type {
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal";
import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
export default function RainSttn() {
const { code, rainSttn, rainSttnSearch, rainSttnInit } = useWrStore(
@@ -37,65 +45,94 @@ export default function RainSttn() {
const [pageNation, setPageNation] = useState(1);
const [isChHead, setIsChHead] = useState(false);
const [bdstTable, setBdstTable] = useState<customRainBdst[]>();
const [detailModal, setDetailModal] = useState<{
isOpen: boolean;
data: customRainBdst | undefined;
}>({ data: undefined, isOpen: false });
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>()
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false })
const [bdstChgProps, setBdstChgProps] = useState<{
isOpen: boolean;
chBuilding: chgBuilding[];
}>({ isOpen: false, chBuilding: [] });
const codeData = getCityOrSgg(ref);
const [fcltNm, setFcltNm] = useState<string>("");
const cellClick = (index: number | undefined, headId: string) => {
if (
headId !== "chBuildingYn" &&
index !== undefined &&
bdstTable !== undefined
) {
setDetailModal({ isOpen: true, data: bdstTable[index] });
} else if (
headId === "chBuildingYn" &&
index !== undefined &&
bdstTable !== undefined
) {
if (
bdstTable[index] &&
bdstTable[index]?.chgBuilding[0]?.bdstChgNm !== null
) {
setBdstChgProps({
isOpen: true,
chBuilding: bdstTable[index].chgBuilding,
});
if (headId !== 'chBuildingYn' && index !== undefined && bdstTable !== undefined) {
setDetailModal({ isOpen: true, data: bdstTable[index] })
} else if (headId === 'chBuildingYn' && index !== undefined && bdstTable !== undefined) {
if (bdstTable[index] && bdstTable[index]?.chgBuilding[0]?.bdstChgNm !== null) {
setBdstChgProps({ isOpen: true, chBuilding: bdstTable[index].chgBuilding });
}
}
};
const getSelCity = (val: string | number) => {
const cityValue = String(val);
if (cityValue === "0") {
setSelectSgg([]);
setObjSearch({ city: cityValue, sgg: "" });
} else {
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? "");
setObjSearch({ city: cityValue, sgg: firstSggVal });
}
};
const getSgg = (val: string | number) => {
setObjSearch({ ...objSearch, sgg: String(val) })
};
const getConValue = () => {
if (!objSearch.city || objSearch.city === "0") {
return "0";
}
if (objSearch.sgg && objSearch.sgg !== "0") {
return objSearch.sgg;
}
return objSearch.city;
};
useEffect(() => {
if (!rainSttn.isInit) {
rainSttnInit(codeData ?? "0");
rainSttnInit(1, "20", "0", fcltNm, code.cityCd, code.sggCd);
}
}, [rainSttn.isInit, rainSttnInit]);
useEffect(() => {
if (rainSttnRes.length > 0 && code.cityCd.length > 0) {
const data = tableData(rainSttnRes, { code });
const chData = data
.map((item: any) => ({
const data = tableData(rainSttnRes, { code });
const chData = data
.map((item: any) => {
return {
...item,
...item.reuseBuilding[0],
...item.reuseBuilding[0].rainyObj,
dtfcltCpct: item.reuseBuilding[0].rainyObj.dtfcltCpct === -1
? "확인불가"
: item.reuseBuilding[0].rainyObj.dtfcltCpct,
instlCst: item.reuseBuilding[0].instlCst === -1
? "확인불가"
: item.reuseBuilding[0].instlCst,
instlCstSprtAmt: item.reuseBuilding[0].instlCstSprtAmt === -1
? "확인불가"
: item.reuseBuilding[0].instlCstSprtAmt,
chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N",
}))
.filter((item: any) => item.rainyObj !== undefined);
setBdstTable(chData);
}
pntfColArea: item.reuseBuilding[0].rainyObj.pntfColArea === -1
? "확인불가"
: item.reuseBuilding[0].rainyObj.pntfColArea,
filtrTrtmfctCpct: item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct === -1
? "확인불가"
: item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct,
archArea: Math.floor(item.archArea) === -1 ? "확인불가" : item.archArea,
};
})
.filter((item: any) => item.rainyObj !== undefined);
setBdstTable(chData);
}, [code, rainSttnRes, rainSttn.rainSttHeadList]);
useEffect(() => {
if (codeData != null) rainSttnSearch(pageNation, "20", codeData, code.cityCd, code.sggCd);
rainSttnSearch(pageNation, "20", String(getConValue()), fcltNm, code.cityCd, code.sggCd);
}, [pageNation]);
useEffect(() => {
@@ -111,8 +148,10 @@ export default function RainSttn() {
}, [rainSttn.excelList]);
const excelClick = useCallback(() => {
if (codeData) rainSttnSearch(1, "10000", codeData, code.cityCd, code.sggCd);
}, [rainSttnSearch, codeData, code.cityCd, code.sggCd]);
// 최신 검색 조건 반영
const con = getConValue();
rainSttnSearch(1, "10000", String(con), fcltNm, code.cityCd, code.sggCd);
}, [rainSttnSearch, objSearch, code.cityCd, code.sggCd]);
return (
<>
@@ -120,47 +159,75 @@ export default function RainSttn() {
<div className="grid grid-cols-4 gap-4 ">
<div className="col-span-4">
<Title title={"빗물재이용 시설 현황"} />
<SearchBaseForm
selectSidoData={code.cityCd}
selectSigunData={code.sggCd}
selectRegionData={code.rvbsnCd}
selectUnitWaterShedData={code.lgbsnCd}
selectMiddleRegionData={code.mdmbsnCd}
selectEnvPartData={code.lclgvrnmntCd}
rightChildren={
<Button
className={btnClass}
onClick={() => {
setIsChHead(true);
}}
>
조회항목설정
</Button>
}
leftChildren={undefined}
queryClick={function (returnType: ReturnType): void {
// '{partValue: 'A', sidoValue: '0', sigunValue: '0', regionValue: '0', unitWaterShedValue: '0', …}'
let con;
if (returnType.sigunValue != "0") {
con = returnType.sigunValue;
} else {
con = returnType.sidoValue;
}
rainSttnSearch(1, "20", con, code.cityCd, code.sggCd);
}}
excelClick={excelClick}
initClick={function (): void { }}
btnClass={btnClass}
ref={ref}
isAdmin={false}
/>
<SearchDiv>
<SearchFormLeft>
<div className="col-span-10 flex flex-row">
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl"
label="시도"
selectClass={""}
selectArray={code.cityCd}
onSelectValue={getSelCity}
/>
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl ml-4"
label="시군구"
selectClass={""}
selectArray={selectSgg || []}
value={objSearch.sgg} // value props 추가
onSelectValue={getSgg}
/>
<div className="col-span-2 flex flex-row space-x-2">
<Label className="w-28 mt-2 text-xl text-gray-600">
{"시설명"}
</Label>
<SearchInput
className={"w-full h-full text-2xl"}
label={""}
textValue={fcltNm || ""}
setText={setFcltNm}
/>
</div>
</div>
</SearchFormLeft>
<SearchFormRight
className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="flex flex-row space-x-2 items-center">
<Button
size={"sm"}
onClick={() => {
const con = getConValue();
rainSttnSearch(pageNation, "20", String(con), fcltNm, code.cityCd, code.sggCd);
}}
>
조회
</Button>
<Button
size={"sm"}
className={`${btnClass} green`}
onClick={excelClick}
>
다운로드
</Button>
<Button
size={"sm"}
className={btnClass}
onClick={() => {
setIsChHead(true);
}}
>
조회항목설정
</Button>
</div>
</SearchFormRight>
</SearchDiv>
</div>
</div>
<div className="grid grid-cols-6 gap-4 ">
<div className="col-span-6">
<ListDiv>
<UiTable
headName={[]}
headName={undefined}
tableData={bdstTable || []}
children={
<HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} />
@@ -168,9 +235,7 @@ export default function RainSttn() {
headlist={rainSttn.rainSttHeadList}
pageSize={20}
total={100000}
cellClick={(index: number | undefined, headId: string) => {
cellClick(index ?? undefined, headId);
}}
cellClick={(index: number | undefined, headId: string) => { cellClick(index ?? undefined, headId) }}
onPageChange={setPageNation}
></UiTable>
</ListDiv>
@@ -5,23 +5,31 @@ import type { chgBuilding } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg";
import {
excelDownload,
getCity,
getCityOrSgg,
tableData,
} from "@/components/lib/func/commom";
import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import HeadChModal from "@/components/ui/modal/sttn/headChModal/headChModal";
import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal";
import {
ContentsDiv,
SearchBaseForm,
SearchDiv,
SearchFormLeft,
SearchFormRight,
SearchInput,
SelectBox,
Title,
UiModal,
UiTable,
type ReturnType,
type SelectDataType,
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
import { Button } from "@common/components/ui";
import { Button, Label } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
export default function RswtSttn() {
@@ -34,13 +42,14 @@ export default function RswtSttn() {
const [isChHead, setIsChHead] = useState(false);
const ref = useRef<ReturnType>({} as ReturnType);
const [bdstTable, setBdstTable] = useState<customRswtBdst[]>();
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>()
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false })
const [bdstChgProps, setBdstChgProps] = useState<{
isOpen: boolean;
chBuilding: chgBuilding[];
}>({ isOpen: false, chBuilding: [] });
const codeCityOrSgg = getCityOrSgg(ref);
const [fcltNm, setFcltNm] = useState<string>("");
const cellClick = (index: number | undefined, headId: string) => {
@@ -50,37 +59,67 @@ export default function RswtSttn() {
if (
bdstTable[index] &&
bdstTable[index]?.chBuildingYn === 'Y'
) {
setBdstChgProps({ isOpen: true, chBuilding: bdstTable[index].chgBuilding });
}
}
}
const getSelCity = (val: string | number) => {
const cityValue = String(val);
if (cityValue === "0") {
setSelectSgg([]);
setObjSearch({ city: cityValue, sgg: "" });
} else {
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data);
const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? "");
setObjSearch({ city: cityValue, sgg: firstSggVal });
}
};
const getSgg = (val: string | number) => {
setObjSearch({ ...objSearch, sgg: String(val) })
};
const getConValue = () => {
if (!objSearch.city || objSearch.city === "0") {
return "0";
}
if (objSearch.sgg && objSearch.sgg !== "0") {
return objSearch.sgg;
}
return objSearch.city;
};
useEffect(() => {
if (!rswtSttn.isInit) {
rswtSttnInit("0");
rswtSttnInit(1, "20", "0", fcltNm, code.cityCd, code.sggCd);
}
}, [rswtSttn.isInit, rswtSttnInit]);
useEffect(() => {
if (rswtSttnRes.length > 0 && code.cityCd.length > 0) {
const data = tableData(rswtSttnRes, { code });
const chData = data
.map((item: customRswtBdst) => ({
const data = tableData(rswtSttnRes, { code });
const chData = data
.map((item: customRswtBdst) => {
return {
...item,
...item.reuseBuilding[0],
...item.reuseBuilding[0]?.rswtrObj,
chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N'
}))
.filter(
(item: customRswtBdst) =>
item.reuseBuilding[0]?.rswtrObj !== undefined
);
chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N',
instlCst: item.reuseBuilding[0]?.instlCst === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCst,
instlCstSprtAmt: item.reuseBuilding[0]?.instlCstSprtAmt === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCstSprtAmt,
archArea : item?.archArea.split('.')[0]!=='-1'?item?.archArea.split('.')[0]:'확인불가'
}
})
.filter(
(item: customRswtBdst) =>
item.reuseBuilding[0]?.rswtrObj !== undefined
);
setBdstTable(chData);
}
setBdstTable(chData);
}, [code, rswtSttnRes]);
useEffect(() => {
@@ -95,16 +134,14 @@ export default function RswtSttn() {
}
}, [rswtSttn.excelList]);
const excelClick = useCallback(
(returnType: ReturnType) => {
if (codeCityOrSgg)
rswtSttnSearch(1, "10000", codeCityOrSgg, code.cityCd, code.sggCd);
},
[rswtSttnSearch, code.cityCd, code.sggCd, codeCityOrSgg]
);
const excelClick = useCallback(() => {
// 최신 검색 조건 반영
const con = getConValue();
rswtSttnSearch(1, "10000", String(con), fcltNm, code.cityCd, code.sggCd);
}, [rswtSttnSearch, objSearch, code.cityCd, code.sggCd]);
useEffect(() => {
if (codeCityOrSgg != null) rswtSttnSearch(pageNation, "20", codeCityOrSgg, code.cityCd, code.sggCd);
rswtSttnSearch(pageNation, "20", String(getConValue()), fcltNm, code.cityCd, code.sggCd);
}, [pageNation]);
return (
@@ -113,38 +150,75 @@ export default function RswtSttn() {
<div className="grid grid-cols-4 gap-4 ">
<div className="col-span-4">
<Title title={"중수도시설 현황"} />
<SearchBaseForm
selectSidoData={code.cityCd}
selectSigunData={code.sggCd}
selectRegionData={code.rvbsnCd}
selectUnitWaterShedData={code.lgbsnCd}
selectMiddleRegionData={code.mdmbsnCd}
selectEnvPartData={code.lclgvrnmntCd}
rightChildren={<Button
className={btnClass}
onClick={() => {
setIsChHead(true);
} }
>
조회항목설정
</Button>}
leftChildren={undefined}
queryClick={function (returnType: ReturnType): void {
// '{partValue: 'A', sidoValue: '0', sigunValue: '0', regionValue: '0', unitWaterShedValue: '0', …}'
if (codeCityOrSgg)
rswtSttnSearch(1, "20", codeCityOrSgg, code.cityCd, code.sggCd);
} }
excelClick={excelClick}
initClick={function (): void { } }
btnClass={btnClass}
ref={ref} isAdmin={false} />
<SearchDiv>
<SearchFormLeft>
<div className="col-span-10 flex flex-row">
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl"
label="시도"
selectClass={""}
selectArray={code.cityCd}
onSelectValue={getSelCity}
/>
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl ml-4"
label="시군구"
selectClass={""}
selectArray={selectSgg || []}
value={objSearch.sgg} // value props 추가
onSelectValue={getSgg}
/>
<div className="col-span-2 flex flex-row space-x-2">
<Label className="w-28 mt-2 text-xl text-gray-600">
{"시설명"}
</Label>
<SearchInput
className={"w-full h-full text-2xl"}
label={""}
textValue={fcltNm || ""}
setText={setFcltNm}
/>
</div>
</div>
</SearchFormLeft>
<SearchFormRight
className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="flex flex-row space-x-2 items-center">
<Button
size={"sm"}
onClick={() => {
const con = getConValue();
rswtSttnSearch(pageNation, "20", String(con), fcltNm, code.cityCd, code.sggCd);
}}
>
조회
</Button>
<Button
size={"sm"}
className={`${btnClass} green`}
onClick={excelClick}
>
다운로드
</Button>
<Button
size={"sm"}
className={btnClass}
onClick={() => {
setIsChHead(true);
}}
>
조회항목설정
</Button>
</div>
</SearchFormRight>
</SearchDiv>
</div>
</div>
<div className="grid grid-cols-6 gap-4 ">
<div className="col-span-6">
<ListDiv>
<UiTable
headName={[]}
headName={undefined}
tableData={bdstTable || []}
children={
<HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} />
@@ -26,10 +26,10 @@ export const headMakeprfmnc = (
key={head.id}
rowSpan={1}
colSpan={
makeHeadNm.find((ch) => ch.headNm === head.upName)?.length
makeHeadNm?.find((ch) => ch.headNm === head.upName)?.length
}
>
{makeHeadNm.find((ch) => ch.headNm === head.upName)?.text}
{makeHeadNm?.find((ch) => ch.headNm === head.upName)?.text}
</TableHead>
) : null
)}
@@ -39,7 +39,7 @@ export const headMakeprfmnc = (
{upHeadList
.filter((data) => data.visible != false)
.map((head: TableUpperProps) =>
makeHeadNm.find((ch) => ch.headNm === head.upName) ? (
makeHeadNm?.find((ch) => ch.headNm === head.upName) ? (
<TableHead
className={`border border-gray-600 px-2 py-1 `}
key={head.id}
@@ -1,10 +1,14 @@
"use client";
import { useWrStore } from "@/app/store";
import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import type {
rswtChkZodType,
rswtrChkDetailsZodType,
import {
commonCd,
rainPageSize,
type rainChkZodType,
} from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import {
rswtPageSize,
type rswtrChkDetailsZodType,
} from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn";
import {
excelArrayDownload,
@@ -15,23 +19,27 @@ import ChkgdExcel from "@/components/ui/modal/chkgd/chkgdExcel/chkgdExcel";
import ChkgdModal from "@/components/ui/modal/chkgd/chkgdModal/chkgdModal";
import RainChkgdTable from "@/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable";
import RswtChkgdTable from "@/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable";
import { SearchInput } from "@common/business_components";
import {
ContentsDiv,
SearchDiv,
SearchFormLeft,
SearchFormRight,
SearchInput,
SelectBox,
Title,
UiModal,
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Label } from "@common/components/ui";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@common/components/ui/tooltip";
import { useCallback, useEffect, useState } from "react";
import { Toaster } from "react-hot-toast";
import { shallow } from "zustand/shallow";
import toast, { Toaster } from "react-hot-toast";
//-----------------------
export default function Page() {
const {
code,
@@ -42,9 +50,10 @@ export default function Page() {
rainChkDelete,
rainChkExcelDownload,
rainChkHeadChange,
rainChkNmSearch,
isRainSttn,
rswtChkgdnInit,
rswtChkNmSearch,
rswtChkPolInit,
rswtChkDetilsUpdate,
rswtChkDelete,
@@ -61,38 +70,87 @@ export default function Page() {
rswtrChkDetailsZodType[]
>([]);
const [deleteIdx, setDeleteIdx] = useState<number[]>([]);
const [rainPageNation, setRainPageNation] = useState(1);
const [rswtPageNation, setRswtPageNation] = useState(1);
const [fcltNm, setFcltNm] = useState<string>("");
const [selFcltDiv, setSelFcltDiv] = useState("RNB");
const [isModal, setIsModal] = useState({
open: false,
data: {},
});
const cellClick = () => {
if (searchYear) rainChkInit(searchYear, "0");
};
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
const cellClick = () => {
if (searchYear)
if (selFcltDiv === "RNB") {
if (fcltNm) {
rainChkNmSearch(
rainPageNation,
rainPageSize,
searchYear,
commonCd,
fcltNm
);
} else {
rainChkInit(rainPageNation, rainPageSize, searchYear, commonCd);
}
} else {
if (fcltNm) {
rswtChkNmSearch(
rainPageNation,
rainPageSize,
searchYear,
commonCd,
fcltNm
);
} else {
rswtChkgdnInit(rswtPageNation, rswtPageSize, searchYear, commonCd);
}
}
};
const rainPageChange = (pageNo: number) => {
if (pageNo > 0) {
setRainPageNation(pageNo);
rainChkInit(pageNo, rainPageSize, searchYear, commonCd);
}
};
const rswtPageChange = (pageNo: number) => {
if (pageNo > 0) {
setRswtPageNation(pageNo);
rswtChkgdnInit(pageNo, rswtPageSize, searchYear, commonCd);
}
};
useEffect(() => {
if (!rainChkgdn.isInit) {
rainChkInit(new Date().getFullYear().toString(), "0");
rainChkInit(
1,
rainPageSize,
new Date().getFullYear().toString(),
commonCd
);
isRainSttn(false);
setIsModal({ data: {}, open: false });
setIsRegExcel(false);
}
}, [rainChkgdn.isInit, rainChkInit, rainPageSize]);
useEffect(() => {
if (!rswtChkgdn.isInit) {
rswtChkgdnInit(new Date().getFullYear().toString(), "0");
rswtChkgdnInit(
1,
rswtPageSize,
new Date().getFullYear().toString(),
commonCd
);
setIsModal({ data: {}, open: false });
setIsRegExcel(false);
}
}, [rainChkgdn.isInit, rainChkInit]);
}, [rswtChkgdn.isInit, rswtChkgdnInit, rswtPageSize]);
useEffect(() => {
if (rswtChkgdn.excelList[0]&&rswtChkgdn.excelList[0]?.data.length > 1) {
if (rswtChkgdn.excelList[0] && rswtChkgdn.excelList[0]?.data.length > 1) {
excelArrayDownload(
"중수도시설 지도점검",
"sample_rs_chk.xlsx",
@@ -152,9 +210,17 @@ export default function Page() {
};
const chkUpdateTable = (selFcltDiv: string) => {
if (selFcltDiv === "RNB") {
if (updateRainDetail && updateRainDetail.length > 0) {
rainChkDetilUpdate(updateRainDetail);
} else {
toast.error("수정을 원하는 행을 선택하고 저장버튼을 눌러주세요 ");
}
} else {
if (updateRswtDetail && updateRswtDetail.length > 0) {
rswtChkDetilsUpdate(updateRswtDetail);
} else {
toast.error("수정을 원하는 행을 선택하고 저장버튼을 눌러주세요 ");
}
}
};
@@ -175,9 +241,9 @@ export default function Page() {
selectClass={""}
selectArray={yearOptions}
value={searchYear}
onSelectValue={(val: string | number) =>
setSearchYear(String(val))
}
onSelectValue={(val: string | number) => {
setSearchYear(String(val));
}}
/>
</div>
<div className="col-span-2 flex flex-row space-x-2">
@@ -189,14 +255,24 @@ export default function Page() {
label={""}
selectClass={""}
selectArray={rainChkgdn.fcltDiv}
onSelectValue={(val: string | number) =>{
if(val === "RST") rswtChkPolInit();
setSelFcltDiv(String(val))
}
}
onSelectValue={(val: string | number) => {
if (val === "RST") rswtChkPolInit();
setSelFcltDiv(String(val));
}}
value={selFcltDiv ?? rainChkgdn.fcltDiv[0]?.val}
/>
</div>
<div className="col-span-2 flex flex-row space-x-2">
<Label className="w-28 mt-2 text-xl text-gray-600">
{"시설명"}
</Label>
<SearchInput
className={"w-full h-full text-2xl"}
label={""}
textValue={fcltNm || ""}
setText={setFcltNm}
/>
</div>
</SearchFormLeft>
<SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="space-y">
@@ -217,6 +293,10 @@ export default function Page() {
>
삭제
</Button>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size={"sm"}
onClick={() => {
@@ -225,6 +305,16 @@ export default function Page() {
>
저장
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={5}>
<h2 className="text-xl">
아래 테이블에 선택을 체크하면 입력셀이 활성화 됩니다.
</h2>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Button
size={"sm"}
onClick={() => {
@@ -251,13 +341,18 @@ export default function Page() {
detailUp={setUpdateRainDetail}
handleClick={handleClick}
rainChkData={rainChkgdn.rainChk}
/>
pageNation={rainPageNation}
onPageChange={rainPageChange}
pageCount={rainChkgdn.rainChkCount} />
) : (
<RswtChkgdTable
detaiRswtlUpdate={setUpdateRSwtDetail}
deleteRswtData={setDeleteIdx}
handleRswtClick={handleClick}
rswtChkData={rswtChkgdn.rswtChkRes}
pageNation={rswtPageNation}
onPageChange={rswtPageChange}
pageCount={rswtChkgdn.rswtChkCount}
/>
)}
</ListDiv>
@@ -265,17 +360,28 @@ export default function Page() {
</div>
<UiModal
open={isModal.open}
onOpenChange={() => {
setIsModal({ open: !open, data: {} });
onOpenChange={(open: boolean) => {
setIsModal((prev) => ({ ...prev, open }));
}}
bodyClass={"w-[80vw] max-w-none"}
body={<ChkgdModal type={selFcltDiv} obj={isModal.data} />}
body={
<ChkgdModal
type={selFcltDiv}
obj={isModal.data}
setIsModal={(open) => setIsModal((prev) => ({ ...prev, open }))}
/>
}
></UiModal>
<UiModal
open={isRegExcel}
onOpenChange={setIsRegExcel}
bodyClass={"w-[30vw] max-w-none"}
body={<ChkgdExcel type={selFcltDiv}></ChkgdExcel>}
body={
<ChkgdExcel
type={selFcltDiv}
setIsRegExcel={setIsRegExcel}
></ChkgdExcel>
}
/>
<Toaster position="top-center" />
</ContentsDiv>
@@ -25,7 +25,7 @@ import {
} from "@common/components/ui";
import { useState, useEffect, useCallback } from "react";
import { set } from "react-hook-form";
import { Toaster } from "react-hot-toast";
import toast, { Toaster } from "react-hot-toast";
import { headMakeprfmnc } from "../chkgdnReg/TableHead";
import { InputTableBody } from "@/components/lib/func/TableData";
import {
@@ -37,6 +37,7 @@ import {
type PrfType,
} from "@/app/store/prfmncReg/pbnstPrfmncReg/type";
import { excelToPbnPrfData } from "@/app/store/prfmncReg/pbnstPrfmncReg/excel";
import data from "@common/components/ui/icon-finder/data";
export default function prfmncPrfmncReg() {
const { code, prfmnc, prfmncHasuInit, prfmnRegData } = useWrStore(
@@ -51,11 +52,12 @@ export default function prfmncPrfmncReg() {
}>({ isModal: false, data: {} as FcPrfType });
const [inputData, setInputData] = useState<any[]>();
const [pbnstRuseFcltPk, setPbnstRuseFcltPk] = useState<string>();
const [pageNation , setPageNation] = useState<number>(1);
const [pageNation, setPageNation] = useState<number>(1);
const [cellNum, setCellNum] = useState<number>(1);
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
const excelClick = useCallback(() => {
excelDownload(
[[]],
@@ -65,33 +67,43 @@ export default function prfmncPrfmncReg() {
);
}, [prfmnc.excelList]);
const onSave = useCallback(() => {
const onSave = useCallback(
async() => {
if (pbnstRuseFcltPk) {
prfmnRegData(
await prfmnRegData(pageNation,'41',
excelToPbnPrfData(
inputData as unknown as JSON[],
searchYear,
pbnstRuseFcltPk
) as unknown as PrfType
);
prfmncHasuInit(String(pageNation), "41");
// cellClick(cellNum)
}
}, [inputData, searchYear, pbnstRuseFcltPk,prfmncHasuInit,pageNation]);
}, [inputData, searchYear, pbnstRuseFcltPk, pageNation,cellNum]);
const cellClick = useCallback(
(index: number | undefined, headId: string) => {
(index: number | undefined) => {
if (index !== undefined) {
setIsExcelOPen({
isModal: false,
data: prfmncRes[index] || ({} as FcPrfType),
});
const inUsageData = structuredClone(initValInUsage);
const outUsageData = structuredClone(initValOutUsage);
const inSubtotal = structuredClone(subTotalObj);
const outSubtotal = structuredClone(subTotalObj);
const inUsageData = structuredClone(initValInUsage).map((row) => ({
...row,
구분: "장내용수",
}));
const outUsageData = structuredClone(initValOutUsage).map((row) => ({
...row,
구분: "장외용수",
}));
const inSubtotal = {
...structuredClone(subTotalObj),
구분: "장내용수",
};
const outSubtotal = {
...structuredClone(subTotalObj),
구분: "장외용수",
};
if (prfmncRes[index]?.prfList) {
prfmncRes[index].prfList?.forEach((prfItem) => {
const month = Number(prfItem?.ym?.slice(4)); // "MM"
@@ -127,7 +139,11 @@ export default function prfmncPrfmncReg() {
});
inUsageData.push(inSubtotal);
outUsageData.push(outSubtotal);
const usageTotalObj: { [key: string]: any } = structuredClone(totalObj);
const usageTotalObj: { [key: string]: any } = {
...structuredClone(totalObj),
구분: "총합계",
용수명: "",
};
for (const key in usageTotalObj) {
if (["usage", "gubun", "edit"].includes(key)) continue;
const val =
@@ -138,7 +154,6 @@ export default function prfmncPrfmncReg() {
}
const tableData = [...inUsageData, ...outUsageData, usageTotalObj];
setPbnstRuseFcltPk(prfmncRes[index].pbnstRuseFcltPk);
setInputData(tableData);
} else {
setInputData([]);
@@ -150,19 +165,24 @@ export default function prfmncPrfmncReg() {
useEffect(() => {
if (!prfmnc.isInit) {
prfmncHasuInit('1', "41");
prfmncHasuInit(1, "41");
}
}, [prfmnc.isInit, prfmncHasuInit]);
useEffect(() => {
setbdstTable(prfmncRes);
}, [prfmncRes]);
// cellNum이 0일 경우(첫 번째 행)도 유효한 인덱스이므로 undefined 여부만 체크합니다.
if (cellNum !== undefined) {
cellClick(cellNum);
}
}, [prfmncRes, cellNum, cellClick]);
return (
<>
<ContentsDiv>
<div className="grid grid-cols-4 gap-4">
<div className="col-span-4">
<Title title={"하수재이용 시설 등록"} />
<Title title={"하수재이용 실적 등록"} />
<SearchDiv>
<SearchFormLeft>
<div className="col-span-2 flex flex-row space-x-2">
@@ -211,7 +231,12 @@ export default function prfmncPrfmncReg() {
<Button
size={"sm"}
onClick={() => {
setIsExcelOPen({ ...isExcelOpen, isModal: true });
if (cellNum === undefined) {
toast.error("하수처리재이용시설을 선택해주세요.");
return;
} else {
setIsExcelOPen({ ...isExcelOpen, isModal: true });
}
}}
>
파일등록
@@ -231,13 +256,15 @@ export default function prfmncPrfmncReg() {
<ListDiv className="min-h-0">
<ScrollArea className="h-[60vh]">
<UiTable
key={'leftTable'}
tableData={bdstTable || []}
headlist={pbnstPrfmncHeadList}
pageSize={10}
total={10000}
headName={"하수재이용시설 목록"}
cellClick={(index: number | undefined, headId: string) => {
cellClick(index, headId);
cellClick(index);
setCellNum(index ?? 0);
}}
onPageChange={setPageNation}
children={undefined}
@@ -257,7 +284,7 @@ export default function prfmncPrfmncReg() {
<TableHeader className="font-bold">
{headMakeprfmnc(monthHeadList, [])}
</TableHeader>
<TableBody>
<TableBody key={prfmnc.pageKey+'pageKey'}>
{InputTableBody(inputData ?? [], monthHeadList, setInputData)}
</TableBody>
</Table>
@@ -266,8 +293,23 @@ export default function prfmncPrfmncReg() {
</div>
</ContentsDiv>
<UiModal
bodyClass={""}
body={<PbnPrfExcelModal data={isExcelOpen.data}></PbnPrfExcelModal>}
bodyClass={"w-[30vw] max-w-none"}
body={
<PbnPrfExcelModal
data={isExcelOpen.data}
setIsExcelOPen={(open) => {
setIsExcelOPen({ ...isExcelOpen, isModal: open })
}}
setModalExcelData={ async(excelData ) =>{
await prfmnRegData(pageNation,'41',
excelData,
);
// 데이터 저장 후 UI 업데이트는 useEffect가 감지하여 처리하므로 여기서는 호출하지 않습니다.
}
}
></PbnPrfExcelModal>
}
open={isExcelOpen.isModal}
onOpenChange={() => {
setIsExcelOPen({ isModal: false, data: {} as FcPrfType });
@@ -1,14 +1,12 @@
"use client";
import { useWrStore } from "@/app/store";
import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn";
import { commonCd, rainPageSize } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import { rswtPageSize } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn";
import {
excelArrayDownload,
excelDownload,
yearOptions,
yearOptions
} from "@/components/lib/func/commom";
import ChkgdExcel from "@/components/ui/modal/chkgd/chkgdExcel/chkgdExcel";
import ChkgdModal from "@/components/ui/modal/chkgd/chkgdModal/chkgdModal";
import RainChkgdTable from "@/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable";
import RswtChkgdTable from "@/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable";
@@ -20,56 +18,99 @@ import {
SearchInput,
SelectBox,
Title,
UiModal,
UiModal
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Label } from "@common/components/ui";
import { useCallback, useEffect, useState } from "react";
import { Toaster } from "react-hot-toast";
//-----------------------
export default function Page() {
const {
code,
rainChkSttn,
rswtChkSttn,
rainChkSttnInit,
rainChkSttnSearch,
rainChkSttnNmSearch,
rswtChkSttnInit,
rswtChkNmSttnSearch,
isRainSttn,
isRswtSttn
isRswtSttn,
rswtChkPolInit,
} = useWrStore((state) => {
return state;
});
const [selFcltDiv, setSelFcltDiv] = useState("RNB");
const [rainPageNation, setRainPageNation] = useState(1);
const [rswtPageNation, setRswtPageNation] = useState(1);
const [fcltNm, setFcltNm] = useState<string>("");
const [isModal, setIsModal] = useState({
open: false,
data: {},
});
const [searchYear, setSearchYear] = useState<string>(String(new Date().getFullYear()));
const cellClick = () => {
if (searchYear) rainChkSttnSearch(searchYear, "0");
if (searchYear) {
if (selFcltDiv === "RNB") {
if (fcltNm) {
rainChkSttnNmSearch(
rainPageNation,
rainPageSize,
searchYear,
commonCd,
fcltNm
);
} else {
rainChkSttnInit(rainPageNation, rainPageSize, searchYear, commonCd);
}
} else {
if (fcltNm) {
rswtChkNmSttnSearch(
rswtPageNation,
rswtPageSize,
searchYear,
commonCd,
fcltNm
);
} else {
rswtChkSttnInit(rswtPageNation, rswtPageSize, searchYear, commonCd);
}
}
}
};
const rainPageChange = (pageNo: number) => {
if (pageNo > 0) {
setRainPageNation(pageNo);
rainChkSttnInit(pageNo, rainPageSize, searchYear, commonCd);
}
};
const rswtPageChange = (pageNo: number) => {
if (pageNo > 0) {
setRswtPageNation(pageNo);
rswtChkSttnInit(pageNo, rswtPageSize, searchYear, commonCd);
}
};
useEffect(() => {
if (!rainChkSttn.isInit) {
isRainSttn(true)
rainChkSttnInit( "0");
console.log("여기", rainPageSize)
rainChkSttnInit(1, rainPageSize, new Date().getFullYear().toString(), commonCd);
setIsModal({ data: {}, open: false });
}
}, [rainChkSttn.isInit, rainChkSttnInit]);
useEffect(() => {
}, [rainChkSttn.isInit, rainChkSttnInit, rainPageSize]);
useEffect(() => {
if (!rswtChkSttn.isInit) {
setIsModal({ data: {}, open: false });
rswtChkSttnInit( "0");
rswtChkSttnInit(1, rswtPageSize, new Date().getFullYear().toString(), commonCd);
isRswtSttn(true)
}
},[rswtChkSttn.isInit, rswtChkSttnInit])
}, [rswtChkSttn.isInit, rswtChkSttnInit, rswtPageSize])
const handleClick = useCallback(
@@ -78,25 +119,25 @@ export default function Page() {
},
[isModal]
);
const excelDown=() =>{
if(rainChkSttn.sttnExcelList && selFcltDiv === "RNB"){
excelDownload(
rainChkSttn.sttnExcelList,
"빗물이용시설 지도점검 현황",
"sample_rainChkSttn.xlsx",
"A1:Q2",
3
);
}else if(rswtChkSttn.sttnExcelList && selFcltDiv === "RST"){
excelDownload(
rswtChkSttn.sttnExcelList,
"중수도시설 지도점검 현황",
"sample_rswtChkSttn.xlsx",
"A1:X2",
3
);
const excelDown = () => {
if (rainChkSttn.sttnExcelList && selFcltDiv === "RNB") {
excelDownload(
rainChkSttn.sttnExcelList,
"빗물이용시설 지도점검 현황",
"sample_rainChkSttn.xlsx",
"A1:Q2",
3
);
} else if (rswtChkSttn.sttnExcelList && selFcltDiv === "RST") {
excelDownload(
rswtChkSttn.sttnExcelList,
"중수도시설 지도점검 현황",
"sample_rswtChkSttn.xlsx",
"A1:X2",
3
);
}
}
}
return (
<ContentsDiv>
@@ -125,10 +166,23 @@ const excelDown=() =>{
label={""}
selectClass={""}
selectArray={rainChkSttn.fcltDiv}
onSelectValue={(val: string | number) =>
setSelFcltDiv(String(val))
}
value={selFcltDiv ?? rainChkSttn.fcltDiv[0]?.val}
onSelectValue={(val: string | number) => {
if (val === "RST") rswtChkPolInit();
setSelFcltDiv(String(val))
}
}
value={selFcltDiv ?? rainChkSttn.fcltDiv[0]?.val}
/>
</div>
<div className="col-span-2 flex flex-row space-x-2">
<Label className="w-28 mt-2 text-xl text-gray-600">
{"시설명"}
</Label>
<SearchInput
className={"w-full text-2xl"}
label={""}
textValue={fcltNm || ""}
setText={setFcltNm}
/>
</div>
</SearchFormLeft>
@@ -143,7 +197,7 @@ const excelDown=() =>{
>
조회
</Button>
<Button color="green" size={"sm"} onClick={(e) =>{excelDown()}}>
<Button color="green" size={"sm"} onClick={(e) => { excelDown() }}>
다운로드
</Button>
</div>
@@ -157,17 +211,23 @@ const excelDown=() =>{
<ListDiv>
{selFcltDiv === "RNB" ? (
<RainChkgdTable
deleteData={() => {}}
detailUp={() => {}}
deleteData={() => { }}
detailUp={() => { }}
handleClick={handleClick}
rainChkData={rainChkSttn.rainChkRes}
pageNation={rainPageNation}
onPageChange={rainPageChange}
pageCount={rainChkSttn.rainChkCount}
/>
) : (
<RswtChkgdTable
detaiRswtlUpdate={() => {}}
deleteRswtData={() => {}}
detaiRswtlUpdate={() => { }}
deleteRswtData={() => { }}
handleRswtClick={handleClick}
rswtChkData={rswtChkSttn.rswtChkRes}
pageNation={rswtPageNation}
onPageChange={rswtPageChange}
pageCount={rswtChkSttn.rswtChkCount}
/>
)}
</ListDiv>
@@ -179,7 +239,7 @@ const excelDown=() =>{
setIsModal({ open: !open, data: {} });
}}
bodyClass={"w-[80vw] max-w-none"}
body={<ChkgdModal type={selFcltDiv} obj={isModal.data} />}
body={<ChkgdModal type={selFcltDiv} obj={isModal.data} setIsModal={() => { }} />}
></UiModal>
<Toaster position="top-center" />
</ContentsDiv>
@@ -8,9 +8,11 @@ import { excelDownload, yearOptions } from "@/components/lib/func/commom";
import SearchInputText from "@/components/ui/searchInput/searchInput";
import { CHART_FORM, ContentsDiv, MainChart, ScrollArea, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ChartConfig, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Label } from "@common/components/ui";
import { Button, Label, Table, TableBody, TableHeader } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import toast, { Toaster } from "react-hot-toast";
import { headMakeprfmnc } from "../../prfmncReg/chkgdnReg/TableHead";
import { InputTableBody } from "@/components/lib/func/TableData";
export default function PbnstPrfmncSttn() {
const {
@@ -34,9 +36,9 @@ export default function PbnstPrfmncSttn() {
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>();
const [pbnRuseNm, setPbnRuseNm] = useState<string>("");
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
const [excelData, setExcelData] = useState<any[]>([]);
const excelClick = useCallback(() => {
excelDownload(
@@ -66,8 +68,6 @@ export default function PbnstPrfmncSttn() {
chartData5: lineChartDataProps[];
chartData6: lineChartDataProps[];
chartData7: lineChartDataProps[];
chartData8: lineChartDataProps[];
chartData9: lineChartDataProps[];
}>();
const [outLineData, setOutLineData] = useState<{
chartData1: lineChartDataProps[];
@@ -76,6 +76,8 @@ export default function PbnstPrfmncSttn() {
chartData4: lineChartDataProps[];
chartData5: lineChartDataProps[];
chartData6: lineChartDataProps[];
chartData7: lineChartDataProps[];
chartData8: lineChartDataProps[];
}>();
type dnChartForm = { label: string, val: number, fill?: string };
@@ -83,15 +85,20 @@ export default function PbnstPrfmncSttn() {
type lineChartDataProps = { label: string, val: number, color: string };
const prfTableData = (prfList: PrfType[], year: string) => {
const inUsageData = structuredClone(initValInUsage);
const outUsageData = structuredClone(initValOutUsage);
const inSubtotal = structuredClone(subTotalObj);
const outSubtotal = structuredClone(subTotalObj);
const inUsageData = structuredClone(initValInUsage).map(row => ({
...row,
구분: "장내용수",
}));
const outUsageData = structuredClone(initValOutUsage).map(row => ({
...row,
구분: "장외용수",
}));
const inSubtotal = { ...structuredClone(subTotalObj), 구분: "장내용수" };
const outSubtotal = { ...structuredClone(subTotalObj), 구분: "장외용수" };
const prfFilter = prfList?.filter((e) => e.ym.slice(0, 4) === year)
if (prfList && prfFilter.length > 0) {
prfList?.forEach((prfItem) => {
const month = Number(prfItem?.ym?.slice(4)); // "MM"
const monthKey = `${month}월`;
@@ -142,7 +149,7 @@ export default function PbnstPrfmncSttn() {
setPrfData([]);
}
}
const [chartId, setChartId] = useState<string>();
useEffect(() => {
// 누적 변수 초기화
@@ -198,8 +205,8 @@ export default function PbnstPrfmncSttn() {
prfTableData([], year)
rawData.forEach((item) => {
// 실적 테이블 데이터
if (chartId){
if(item.prfList?.[0]?.ym.slice(0, 4) === year && item.prfList?.[0]?.pbnstRuseFcltFk === chartId){
if (chartId) {
if (item.prfList?.[0]?.ym.slice(0, 4) === year && item.prfList?.[0]?.pbnstRuseFcltFk === chartId) {
prfTableData(item.prfList, year);
}
item.prfList.filter((e) => e.ym.slice(0, 4) === year && e.pbnstRuseFcltFk === chartId).forEach((element) => {
@@ -221,16 +228,18 @@ export default function PbnstPrfmncSttn() {
indstUswtr += element.indstUswtr;
// 월별 합계
inUsageSum = element.wshwtr
inUsageSum =
element.wshwtr
+ element.coolUswtr
+ element.cleanUswtr
+ element.drwtUswtr
+ element.dltnUswtr
+ element.ithlRswtr
+ element.ithlEtcUswtr
+ element.cleanToilUswtr
+ element.resn;
outUsageSum = element.lndwtr
outUsageSum =
element.cleanToilUswtr
+ element.resn
+ element.lndwtr
+ element.hydUswtr
+ element.ugwtChr
+ element.farmUswtr
@@ -251,9 +260,10 @@ export default function PbnstPrfmncSttn() {
dltnUswtrData.push({ label: `${month}월`, val: element.dltnUswtr, color: "purple" });
ithlRswtrData.push({ label: `${month}월`, val: element.ithlRswtr, color: "green" });
ithlEtcUswtrData.push({ label: `${month}월`, val: element.ithlEtcUswtr, color: "blue" });
cleanToilUswtrData.push({ label: `${month}월`, val: element.cleanToilUswtr, color: "magenta" });
resnData.push({ label: `${month}월`, val: element.resn, color: "skyblue" });
lndwtrData.push({ label: `${month}월`, val: element.lndwtr, color: "red" });
hydUswtrData.push({ label: `${month}월`, val: element.hydUswtr, color: "orange" });
ugwtChrData.push({ label: `${month}월`, val: element.ugwtChr, color: "yellow" });
@@ -264,41 +274,37 @@ export default function PbnstPrfmncSttn() {
}
});
let dnTotalIn = 0;
let dnTotalOut = 0;
inUsageArr.forEach((item) => {
dnTotalIn += item
})
outUsageArr.forEach((item) => {
dnTotalOut += item
})
// 도넛차트 데이터
dnData.push({ label: "세척수", val: wshwtr, fill: "red" });
dnData.push({ label: "냉각수", val: coolUswtr, fill: "orange" });
dnData.push({ label: "청소수", val: cleanUswtr, fill: "yellow" });
dnData.push({ label: "식수대", val: drwtUswtr, fill: "green" });
dnData.push({ label: "희석용수", val: dltnUswtr, fill: "skyblue" });
dnData.push({ label: "중수도", val: ithlRswtr, fill: "blue" });
dnData.push({ label: "기타", val: ithlEtcUswtr, fill: "purple" });
dnData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "red" });
dnData.push({ label: "세척살수용수", val: resn, fill: "orange" });
dnData.push({ label: "조경용수", val: lndwtr, fill: "yellow" });
dnData.push({ label: "친수용수", val: hydUswtr, fill: "green" });
dnData.push({ label: "지하수충전", val: ugwtChr, fill: "skyblue" });
dnData.push({ label: "농업용수", val: farmUswtr, fill: "blue" });
dnData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "purple" });
dnData.push({ label: "공업용수", val: indstUswtr, fill: "black" });
dnData.push({ label: "장내용수", val: dnTotalIn, fill: "#8DD8FF" });
dnData.push({ label: "장외용수", val: dnTotalOut, fill: "#FFF085" });
// 장내용수 도넛
dnInData.push({ label: "세척수", val: wshwtr, fill: "yellow" });
dnInData.push({ label: "냉각수", val: coolUswtr, fill: "green" });
dnInData.push({ label: "청소수", val: cleanUswtr, fill: "yellow" });
dnInData.push({ label: "식수대", val: drwtUswtr, fill: "green" });
dnInData.push({ label: "희석용수", val: dltnUswtr, fill: "yellow" });
dnInData.push({ label: "중수도", val: ithlRswtr, fill: "green" });
dnInData.push({ label: "기타", val: ithlEtcUswtr, fill: "yellow" });
dnInData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "green" });
dnInData.push({ label: "세척살수용수", val: resn, fill: "yellow" });
dnInData.push({ label: "세척수", val: wshwtr, fill: "#F7CFD8" });
dnInData.push({ label: "냉각수", val: coolUswtr, fill: "#F4F8D3" });
dnInData.push({ label: "청소수", val: cleanUswtr, fill: "#A6D6D6" });
dnInData.push({ label: "식수대", val: drwtUswtr, fill: "#8E7DBE" });
dnInData.push({ label: "희석용수", val: dltnUswtr, fill: "#FF8383" });
dnInData.push({ label: "중수도", val: ithlRswtr, fill: "#FFF574" });
dnInData.push({ label: "기타", val: ithlEtcUswtr, fill: "#A1D6CB" });
// 장외용수 도넛
dnOutData.push({ label: "조경용수", val: lndwtr, fill: "yellow" });
dnOutData.push({ label: "친수용수", val: hydUswtr, fill: "black" });
dnOutData.push({ label: "지하수충전", val: ugwtChr, fill: "yellow" });
dnOutData.push({ label: "농업용수", val: farmUswtr, fill: "black" });
dnOutData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "yellow" });
dnOutData.push({ label: "공업용수", val: indstUswtr, fill: "black" });
dnOutData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" });
dnOutData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" });
dnOutData.push({ label: "조경용수", val: lndwtr, fill: "#81BFDA" });
dnOutData.push({ label: "친수용수", val: hydUswtr, fill: "#F5F0CD" });
dnOutData.push({ label: "지하수충전", val: ugwtChr, fill: "#FADA7A" });
dnOutData.push({ label: "농업용수", val: farmUswtr, fill: "#A6AEBF" });
dnOutData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "#C5D3E8" });
dnOutData.push({ label: "공업용수", val: indstUswtr, fill: "#D0E8C5" });
// 라인차트 데이터
setInLineData({
@@ -308,17 +314,17 @@ export default function PbnstPrfmncSttn() {
chartData4: drwtUswtrData,
chartData5: dltnUswtrData,
chartData6: ithlRswtrData,
chartData7: ithlEtcUswtrData,
chartData8: cleanToilUswtrData,
chartData9: resnData
chartData7: ithlEtcUswtrData
});
setOutLineData({
chartData1: lndwtrData,
chartData2: hydUswtrData,
chartData3: ugwtChrData,
chartData4: farmUswtrData,
chartData5: rvrKeepUswtrData,
chartData6: indstUswtrData,
chartData1: cleanToilUswtrData,
chartData2: resnData,
chartData3: lndwtrData,
chartData4: hydUswtrData,
chartData5: ugwtChrData,
chartData6: farmUswtrData,
chartData7: rvrKeepUswtrData,
chartData8: indstUswtrData,
});
setBarData(barData);
setChartData(dnData);
@@ -401,15 +407,7 @@ export default function PbnstPrfmncSttn() {
chartData7: {
label: '기타',
color: "blue",
},
chartData8: {
label: '청소화장실용수',
color: "magenta",
},
chartData9: {
label: '세척살수용수',
color: "skyblue",
},
}
} satisfies ChartConfig;
setLnInChartConfig(newChartConfig);
}
@@ -420,26 +418,34 @@ export default function PbnstPrfmncSttn() {
if (outLineData?.chartData1) {
const newChartConfig = {
chartData1: {
label: '조경용수',
label: '청소화장실용수',
color: "red",
},
chartData2: {
label: '세척살수용수',
color: "pink",
},
chartData3: {
label: '조경용수',
color: "gray",
},
chartData4: {
label: '친수용수',
color: "orange",
},
chartData3: {
chartData5: {
label: '지하수충전',
color: "yellow",
},
chartData4: {
chartData6: {
label: '농업용수',
color: "green",
},
chartData5: {
chartData7: {
label: '하천등유지용수',
color: "blue",
},
chartData6: {
chartData8: {
label: '공업용수',
color: "purple",
},
@@ -466,7 +472,7 @@ export default function PbnstPrfmncSttn() {
useEffect(() => {
setbdstTable(prfmncRes);
if(prfmncRes?.[0]?.pbnstRuseFcltPk) setChartId(prfmncRes[0].pbnstRuseFcltPk)
if (prfmncRes?.[0]?.pbnstRuseFcltPk) setChartId(prfmncRes[0].pbnstRuseFcltPk)
}, [prfmncRes]);
@@ -608,7 +614,7 @@ export default function PbnstPrfmncSttn() {
<h1 className="">장내·외 월별 하수재이용실적 현황</h1>
<div className="flex">
<div className="w-1/3">
<MainChart chartConfig={barChartConfig} chartData={barData} chartTitle={"월별 장내·외 용수 총 실적"} chartForm={CHART_FORM.DOUBLEBAR} />
<MainChart chartConfig={barChartConfig} chartData={barData} chartTitle={"월별 장내·외 용수 총 실적"} chartForm={CHART_FORM.DOUBLEBAR} showLegend={true} />
</div>
<div className="w-1/3">
<MainChart chartConfig={lnInChartConfig} chartData={inLineData} chartTitle={"월별 장내 용수 실적"} chartForm={CHART_FORM.LINE} />
@@ -619,25 +625,44 @@ export default function PbnstPrfmncSttn() {
</div>
<h1 className="">하수재이용실적 현황</h1>
<ListDiv className="col-span-8 pr-6 ">
<ScrollArea className="h-[60vh]">
<div className="card-header">
{/* <ScrollArea className="h-[60vh]"> */}
{/* <div className="card-header">
<h3 className="text-2xl font-medium text-label">
{"하수처리수재이용 실적(m3) 등록결과"}
</h3>
</div>
</div> */}
<UiTable
headName={undefined}
tableData={prfData || []}
headlist={monthHeadList}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}}>
</UiTable>
</ScrollArea>
<UiTable
headName={undefined}
tableData={prfData || []}
headlist={monthHeadList}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={function (page: number): void {
throw new Error("Function not implemented.");
}}>
</UiTable>
{/* <ListDiv className="col-span-8 pr-6 ">
<ScrollArea className="h-[60vh]">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">
{"하수처리수재이용 실적(m3) 등록결과"}
</h3>
</div>
<Table variant="secondary">
<TableHeader className="font-bold">
{headMakeprfmnc(monthHeadList, [])}
</TableHeader>
<TableBody>
{InputTableBody(prfData ?? [], monthHeadList, setPrfData)}
</TableBody>
</Table>
</ScrollArea>
</ListDiv> */}
{/* </ScrollArea> */}
</ListDiv>
</ListDiv>
+2 -1
View File
@@ -210,8 +210,9 @@ export default function RainStat() {
...item,
ctpvCd: item.cityText
};
})
}).filter((item: RainStatProps) => item.cityText !== '없음')
: [];
setBdstTable(tbData);
}, [code, rainStatList]);
+1 -1
View File
@@ -46,7 +46,7 @@ export default function RswtStat() {
...item,
ctpvCd: item.cityText
};
})
}).filter((item: RswtStatProps) => item.cityText !== '없음')
: [];
setRswtTable(tbData);
}, [code, rswtStatList])
+204
View File
@@ -0,0 +1,204 @@
import type { SelectDataType } from "@common/business_components";
export const yn: SelectDataType[] = [
{ val: "Y", text: "Y" },
{ val: "N", text: "N" },
];
export const bdstFilterYn: SelectDataType[] = [
{ val: "Y", text: "Y" },
{ val: "N", text: "N" },
{ val: "D", text: "확인불가" },
{ val: "F", text: "미가동" },
];
// 최우수(그린1)/우수(그린2), 우량(그린3), 일반(그린4), 확인불가 체크
export const bdstGrntchClass: SelectDataType[] = [
{ val: "최우수(그린1)", text: "최우수(그린1)" },
{ val: "우수(그린2)", text: "우수(그린2)" },
{ val: "우량(그린3)", text: "우량(그린3)" },
{ val: "일반(그린4)", text: "일반(그린4)" },
{ val: "확인불가", text: "확인불가" },
];
export const bdstIncenYn: SelectDataType[] = [
{ val: "Y", text: "Y" },
{ val: "N", text: "N" },
{ val: "D", text: "확인불가" },
];
//처리장연계/자체+연계/자체처리
export const erctUswtrPrcsMeth: SelectDataType[] = [
{ val: "처리장연계", text: "처리장연계" },
{ val: "자체+연계", text: "자체+연계" },
{ val: "자체처리", text: "자체처리" },
];
export const rusePrcsMthdCd: SelectDataType[] = [
{ val: "물리학적처리", text: "물리학적처리" },
{ val: "생물학적처리", text: "생물학적처리" },
];
export const ruseSeCd: SelectDataType[] = [
{ val: "신규", text: "신규" },
{ val: "증설", text: "증설" },
{ val: "관로", text: "관로" },
{ val: "감량", text: "감량" },
{ val: "개량", text: "개량" },
];
export const ruseBizMthdCd: SelectDataType[] = [
{ val: "재정", text: "재정" },
{ val: "민간투자", text: "민간투자" },
{ val: "자체", text: "자체" },
];
//공통 건축물 용도
export const bdstUsgCd: SelectDataType[] = [
{ val: "-1", text: "확인불가" },
{ val: "1", text: "운동장" },
{ val: "2", text: "체육관" },
{ val: "3", text: "공공업무시설" },
{ val: "4", text: "공공기관의청사" },
{ val: "5", text: "아파트" },
{ val: "6", text: "연립주택" },
{ val: "7", text: "다세대주택" },
{ val: "8", text: "기숙사" },
{ val: "9", text: "초등학교" },
{ val: "10", text: "중학교" },
{ val: "11", text: "고등학교" },
{ val: "12", text: "전문대학" },
{ val: "13", text: "대학및대학교" },
{ val: "14", text: "골프장" },
{ val: "15", text: "대규모점포" },
{ val: "16", text: "기타" },
];
export const rswtBdstType: SelectDataType[] = [
{ text: "숙박업", val: "17" },
{ text: "목욕장업", val: "18" },
{ text: "공장", val: "19" },
{ text: "발전시설", val: "20" },
{ text: "관광단지개발사업", val: "21" },
{ text: "도시개발사업", val: "22" },
{ text: "산업단지개발사업", val: "23" },
{ text: "택지개발사업", val: "24" },
{ text: "대규모점포(대형마트)", val: "25" },
{ text: "대규모점포(전문점)", val: "26" },
{ text: "대규모점포(백화점)", val: "27" },
{ text: "대규모점포(쇼핑센터)", val: "28" },
{ text: "대규모점포(복합쇼핑몰)", val: "29" },
{ text: "물류시설", val: "30" },
{ text: "운수시설(여객자동차터미널)", val: "31" },
{ text: "운수시설(철도시설)", val: "32" },
{ text: "운수시설(공항시설)", val: "33" },
{ text: "운수시설(항만시설)", val: "34" },
{ text: "업무시설(공공업무시설)", val: "35" },
{ text: "업무시설(금융업소)", val: "36" },
{ text: "업무시설(사무소)", val: "37" },
{ text: "업무시설(결혼상담소 등 소개업소)", val: "38" },
{ text: "업무시설(출판사)", val: "39" },
{ text: "업무시설(신문사)", val: "40" },
{ text: "업무시설(오피스텔)", val: "41" },
{ text: "교정시설", val: "42" },
{ text: "방송국", val: "43" },
{ text: "전신전화국", val: "44" },
{ text: "기타", val: "45" },
];
export const orgwtKndCd = [
{ text: "오수(변기 오수 제외)", val: "1" },
{ text: "오수", val: "2" },
{ text: "폐수", val: "3" },
{ text: "오수 및 폐수", val: "4" },
{ text: "기타", val: "5" },
];
export const chBuCd = [
{ val: "증축", text: "증축" },
{ val: "개축", text: "개축" },
{ val: "재축", text: "재축" },
];
export const dmdUszCd = [
{ val: "조경용수", text: "조경용수" },
{ val: "청소화장실용수", text: "청소화장실용수" },
{ val: "농업용수", text: "농업용수" },
{ val: "공업용수", text: "공업용수" },
{ val: "친수용수", text: "친수용수" },
{ val: "기타", text: "기타" },
];
export const ruseMngMtd = [
{ val: "자체운영", text: "자체운영" },
{ val: "공기업", text: "공기업" },
{ val: "민간(대행)", text: "민간(대행)" },
];
export const pplQulity = [
{ val: "도복장강관", text: "도복장강관" },
{ val: "PE관", text: "PE관" },
{ val: "스테인레스관", text: "스테인레스관" },
{ val: "덕타일주철관", text: "덕타일주철관" },
{ val: "유리섬유복합관", text: "유리섬유복합관" },
{ val: "기타", text: "기타" },
];
export const pplLenght = [
{ val: "D100", text: "D100" },
{ val: "D200", text: "D200" },
{ val: "D250", text: "D250" },
{ val: "D300", text: "D300" },
{ val: "D350", text: "D350" },
{ val: "D400", text: "D400" },
{ val: "D450", text: "D450" },
{ val: "D500", text: "D500" },
{ val: "D700", text: "D700" },
{ val: "D900", text: "D900" },
{ val: "D1000​", text: "D1000​" },
];
export const rainFcltCd = [
{ val: "지붕", text: "지붕" },
{ val: "건물옥상", text: "건물옥상" },
{ val: "골프장 연못", text: "골프장 연못" },
{ val: "운동장 바닥", text: "운동장 바닥" },
{ val: "운동장 지붕", text: "운동장 지붕" },
{ val: "주차장", text: "주차장" },
{ val: "태양광패널", text: "태양광패널" },
{ val: "연못", text: "연못" },
{ val: "기타", text: "기타" },
{ val: "확인불가", text: "확인불가" },
];
//물리화학적처리(스크린, 응집, 침전)/생물학적처리(활성슬러지법 등)/소독(염소, UV 등)/기타
export const rswtPrscCdMthdCd = [
{
val: "물리화학적처리(스크린,응집,침전)",
text: "물리화학적처리(스크린,응집,침전)",
},
{
val: "생물학적처리(활성슬러지법 등)",
text: "생물학적처리(활성슬러지법 등)",
},
{ val: "소독(염소, UV 등)", text: "소독(염소, UV 등)" },
{ val: "기타", text: "기타" },
];
export const htdPowerMthdCd= [
{
val: "석탄화력발전",
text: "석탄화력발전",
},
];
//(공업용수/양식장/농업용수)
export const htdUtztnUsgCd= [
{
val: "공업용수",
text: "공업용수",
},
{
val: "양식장",
text: "양식장",
},
{
val: "농업용수",
text: "농업용수",
},
];
+53 -176
View File
@@ -1,184 +1,13 @@
import { getFetch } from "@/components/lib/api";
import type { EnvPartSelectDataType, SelectDataType } from "@common/business_components/ui";
import { bdstFilterYn, bdstGrntchClass, bdstIncenYn, bdstUsgCd, chBuCd, dmdUszCd, erctUswtrPrcsMeth, htdPowerMthdCd, htdUtztnUsgCd, orgwtKndCd, pplLenght, pplQulity, rainFcltCd, rswtPrscCdMthdCd, ruseBizMthdCd, ruseMngMtd, rusePrcsMthdCd, ruseSeCd, yn } from "./basicCode";
export const btnClass = "p-4 m-2 text-white rounded-sm text-lg font-medium "
const yn: SelectDataType[] = [
{ val: 'Y', text: 'Y' },
{ val: 'N', text: 'N' },
]
//공통 건축물 용도
const bdstUsgCd: SelectDataType[] = [
{ val: '-1', text: "확인불가" },
{ val: '1', text: "운동장" },
{ val: '2', text: "체육관" },
{ val: '3', text: "공공업무시설" },
{ val: '4', text: "공공기관의청사" },
{ val: '5', text: "아파트" },
{ val: '6', text: "연립주택" },
{ val: '7', text: "다세대주택" },
{ val: '8', text: "기숙사" },
{ val: '9', text: "초등학교" },
{ val: '10', text: "중학교" },
{ val: '11', text: "고등학교" },
{ val: '12', text: "전문대학" },
{ val: '13', text: "대학및대학교" },
{ val: '14', text: "골프장" },
{ val: '15', text: "대규모점포" },
{ val: '16', text: "기타" },
];
//빗물 집수면 종류 용도
// const rainFcltCd = [
// { val: "1", text: "지붕" },
// { val: "2", text: "건물옥상" },
// { val: "3", text: "골프장 연못" },
// { val: "4", text: "운동장 바닥" },
// { val: "5", text: "운동장 지붕" },
// { val: "6", text: "주차장" },
// { val: "7", text: "태양광패널" },
// { val: "8", text: "연못" },
// { val: "9", text: "기타" },
// { val: "10", text: "확인불가" },
// ];
// 중수도 필수 원수종류
const rswtBdstType: SelectDataType[] = [
{ text: "숙박업", val: "17" },
{ text: "목욕장업", val: "18" },
{ text: "공장", val: "19" },
{ text: "발전시설", val: "20" },
{ text: "관광단지개발사업", val: "21" },
{ text: "도시개발사업", val: "22" },
{ text: "산업단지개발사업", val: "23" },
{ text: "택지개발사업", val: "24" },
{ text: "대규모점포(대형마트)", val: "25" },
{ text: "대규모점포(전문점)", val: "26" },
{ text: "대규모점포(백화점)", val: "27" },
{ text: "대규모점포(쇼핑센터)", val: "28" },
{ text: "대규모점포(복합쇼핑몰)", val: "29" },
{ text: "물류시설", val: "30" },
{ text: "운수시설(여객자동차터미널)", val: "31" },
{ text: "운수시설(철도시설)", val: "32" },
{ text: "운수시설(공항시설)", val: "33" },
{ text: "운수시설(항만시설)", val: "34" },
{ text: "업무시설(공공업무시설)", val: "35" },
{ text: "업무시설(금융업소)", val: "36" },
{ text: "업무시설(사무소)", val: "37" },
{ text: "업무시설(결혼상담소 등 소개업소)", val: "38" },
{ text: "업무시설(출판사)", val: "39" },
{ text: "업무시설(신문사)", val: "40" },
{ text: "업무시설(오피스텔)", val: "41" },
{ text: "교정시설", val: "42" },
{ text: "방송국", val: "43" },
{ text: "전신전화국", val: "44" },
{ text: "기타", val: "45" }
];
const orgwtKndCd = [
{ text: "오수(변기 오수 제외)", val: "1" },
{ text: "오수", val: "2" },
{ text: "폐수", val: "3" },
{ text: "오수 및 폐수", val: "4" },
{ text: "기타", val: "5" },
];
const chBuCd = [
{ val: "증축", text: "증축" },
{ val: "개축", text: "개축" },
{ val: "재축", text: "재축" },
]
const dmdUszCd = [
{ val: "조경용수", text: "조경용수" },
{ val: "청소화장실용수", text: "청소화장실용수" },
{ val: "농업용수", text: "농업용수" },
{ val: "공업용수", text: "공업용수" },
{ val: "친수용수", text: "친수용수" },
{ val: "기타", text: "기타" },
]
const ruseMngMtd = [
{ val: "자체운영", text: "자체운영" },
{ val: "공기업", text: "공기업" },
{ val: "민간(대행)", text: "민간(대행)" },
]
const pplQulity = [
{ val: "도복장강관", text: "도복장강관" },
{ val: "PE관", text: "PE관" },
{ val: "스테인레스관", text: "스테인레스관" },
{ val: "덕타일주철관", text: "덕타일주철관" },
{ val: "유리섬유복합관", text: "유리섬유복합관" },
{ val: "기타", text: "기타" },
]
const pplLenght = [
{ val: "D100", text: "D100" },
{ val: "D200", text: "D200" },
{ val: "D250", text: "D250" },
{ val: "D300", text: "D300" },
{ val: "D350", text: "D350" },
{ val: "D400", text: "D400" },
{ val: "D450", text: "D450" },
{ val: "D500", text: "D500" },
{ val: "D700", text: "D700" },
{ val: "D900", text: "D900" },
{ val: "D1000​", text: "D1000​" },
]
export const buildingType = {
RST:"RST",
RNB:"RNB",
HTB:"HTB"
}
const rainFcltCd = [
{ val: "지붕", text: "지붕" },
{ val: "건물옥상", text: "건물옥상" },
{ val: "골프장 연못", text: "골프장 연못" },
{ val: "운동장 바닥", text: "운동장 바닥" },
{ val: "운동장 지붕", text: "운동장 지붕" },
{ val: "주차장", text: "주차장" },
{ val: "태양광패널", text: "태양광패널" },
{ val: "연못", text: "연못" },
{ val: "기타", text: "기타" },
{ val: "확인불가", text: "확인불가" },
];
export type comCodeState = {
code: {
isInit: boolean;
chBuCd: SelectDataType[]
rainFcltCd: SelectDataType[];
bdstUsgCd: SelectDataType[];
cityCd: SelectDataType[];
sggCd: SelectDataType[];
rvbsnCd: SelectDataType[];
mdmbsnCd: SelectDataType[];
lgbsnCd: SelectDataType[];
lclgvrnmntCd: EnvPartSelectDataType[];
orgwtKndCd: SelectDataType[];
yn: SelectDataType[];
pplQulity: SelectDataType[];
pplLenght: SelectDataType[];
dmdUszCd:SelectDataType[]
buildingType: {
RST:string,
RNB:string,
HTB:string
}
ruseMngMtd:SelectDataType[]
};
};
export type comCodeType = comCodeState & comCodeActions;
export const getSggCd = async (set: any) => {
const sgg = await getFetch("common", "/addr/sgg");
/* const sggCd = sgg.map((item: any) => ({
@@ -187,7 +16,7 @@ export const getSggCd = async (set: any) => {
})); */
const sggCd = [
{ text: "전체", val: '0' },
// { text: "전체", val: '0' },
...sgg
.map((item: any) => ({
text: item.gugun || '',
@@ -304,6 +133,47 @@ export const getEnvPartCd = async (set: any) => {
export type comCodeState = {
code: {
isInit: boolean;
chBuCd: SelectDataType[]
rainFcltCd: SelectDataType[];
bdstUsgCd: SelectDataType[];
cityCd: SelectDataType[];
sggCd: SelectDataType[];
rvbsnCd: SelectDataType[];
mdmbsnCd: SelectDataType[];
lgbsnCd: SelectDataType[];
lclgvrnmntCd: EnvPartSelectDataType[];
orgwtKndCd: SelectDataType[];
yn: SelectDataType[];
pplQulity: SelectDataType[];
pplLenght: SelectDataType[];
dmdUszCd:SelectDataType[]
buildingType: {
RST:string,
RNB:string,
HTB:string
}
ruseMngMtd:SelectDataType[]
ruseSeCd: SelectDataType[];
ruseBizMthdCd: SelectDataType[];
rusePrcsMthdCd: SelectDataType[];
erctUswtrPrcsMeth: SelectDataType[];
bdstFilterYn: SelectDataType[];
bdstIncenYn: SelectDataType[];
bdstGrntchClass:SelectDataType[];
rswtPrscCdMthdCd:SelectDataType[];
htdPowerMthdCd:SelectDataType[]
htdUtztnUsgCd:SelectDataType[]
};
};
export type comCodeType = comCodeState & comCodeActions;
export const comCodeStateExport: comCodeState = {
code: {
isInit: false,
@@ -323,6 +193,16 @@ export const comCodeStateExport: comCodeState = {
dmdUszCd:dmdUszCd,
buildingType:buildingType,
ruseMngMtd:ruseMngMtd,
ruseSeCd:ruseSeCd,
ruseBizMthdCd: ruseBizMthdCd,
rusePrcsMthdCd:rusePrcsMthdCd,
erctUswtrPrcsMeth:erctUswtrPrcsMeth,
bdstFilterYn:bdstFilterYn,
bdstIncenYn:bdstIncenYn,
bdstGrntchClass:bdstGrntchClass,
rswtPrscCdMthdCd:rswtPrscCdMthdCd,
htdPowerMthdCd:htdPowerMthdCd,
htdUtztnUsgCd:htdUtztnUsgCd,
},
};
@@ -330,9 +210,6 @@ export type comCodeActions = {
codeInit: () => void;
};
export const codeActionExport: (set: any) => comCodeActions = (set: any) => {
return {
codeInit: async () => {
+58 -11
View File
@@ -7,8 +7,50 @@ import {
import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type";
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
import type { conCostFeeType, rateFeeType } from "./type";
export const comFcltStateExport: comFcltState = {
searchFclts: [] as pubHasuType[],
demandCompList: [],
dmdIsSucees: '',
checkDemand: {
index: 0,
dmdInfoPk: "",
dmdInfoFk: "",
bzmnNo: "",
dmdCoNm: "",
daddr: "",
telno: "",
rgtr: "",
useUsgCd: "",
ctpvCd: "",
sggCd: "",
dmdAplyPswd: "",
wntUntprc: 0,
dmndTmng: "",
dmndQty: 0,
grssClfg: 0,
tn: 0,
combResdch: 0,
tph: 0,
trbt: 0,
ph: 0,
blgcOxygDmndQnt: 0,
clIonQnt: 0,
smll: 0,
cdtnv: 0,
crmty: 0,
rmrk: "",
reqDt: ""
},
constCostRes:[],
rateFeeRes:[],
};
export type comFcltType = comFcltState & comFcltActions;
export type comFcltState = {
searchFclts: pubHasuType;
searchFclts: pubHasuType[];
demandCompList: DemandCompType[];
dmdIsSucees: string;
checkDemand: DemandCompType;
@@ -16,7 +58,7 @@ export type comFcltState = {
rateFeeRes:rateFeeType[],
};
export type comFcltActions = {
type comFcltActions = {
getSwtrmtFcltNm: (searchNm: string) => void;
getDemandCompList: (searchNm: string, cnt: number) => void;
regDemandCompany: (data: DemandCompType) => void;
@@ -25,9 +67,10 @@ export type comFcltActions = {
getRateFee: (year: string) => void;
};
export const searchActions: (set: any) => comFcltActions = (set: any) => {
export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => {
return {
getSwtrmtFcltNm: async (searchNm: string) => {
const fcltNm = await getFetch(
"common",
`/prcsFclt/info/1/11/${searchNm}`
@@ -40,6 +83,7 @@ export const searchActions: (set: any) => comFcltActions = (set: any) => {
addr: item.addr,
fctCpc: item.fctCpc,
}));
set((state: comFcltState) => ({
...state,
searchFclts: searchFclts,
@@ -51,10 +95,13 @@ export const searchActions: (set: any) => comFcltActions = (set: any) => {
`/reuseFc/request/${searchNm}/${cnt}`
);
console.log(coList);
set((state: comFcltState) => {
return (state.demandCompList = [...coList]);
});
if(coList&&coList.length>0){
set((state: comFcltState) => {
return (state.demandCompList = [...coList]);
});
}
},
regDemandCompany: async (data: DemandCompType) => {
try {
@@ -69,9 +116,10 @@ export const searchActions: (set: any) => comFcltActions = (set: any) => {
} else {
message = "fail";
}
set((state: comFcltState) => {
state.dmdIsSucees = message;
return state;
return state.dmdIsSucees = message;
});
} catch (err) {
console.log(err);
@@ -89,8 +137,7 @@ export const searchActions: (set: any) => comFcltActions = (set: any) => {
}
set((state: comFcltState) => {
state.dmdIsSucees = message;
return state;
return state.dmdIsSucees = message;
});
} catch (err) {
console.log(err);
+185
View File
@@ -0,0 +1,185 @@
import { getFetch } from "@/components/lib/api";
export type wrMapType = wrMapState & wrMapActions;
export type wrMapState = {
wrMap: {
buildingSearchRes: any[]
} // 건물 검색 결과 데이터
};
type wrMapActions = {
buildingNmSearch: (
flg: string,
sigunCode: string,
searchNm: string | undefined
) => void;
buildingNmSearchMultiple: (
checkedLayers: string[],
sigunCode: string,
searchNm: string | undefined
) => void; // 여러 레이어 동시 검색 함수 추가
};
export const wrMapStateExport: wrMapState = {
wrMap: {
buildingSearchRes: []
}
};
// 레이어 타입과 플래그 매핑
const LAYER_FLAG_MAP: { [key: string]: string } = {
// '46': '???', // 하수처리시설
// '47': '???', // 하수재이용시설
'48': 'RNB', // 빗물이용시설
'49': 'RST', // 중수도시설
'50': 'HTB', // 온배수시설
};
const findBdst = async (flg: string, sggCd: string, searchTxt: string | undefined) => {
return await getFetch('wr', `/reuseFc/building/${flg}/${sggCd}` + (searchTxt ? `/${searchTxt}` : ''))
}
export const wrMapActionsExport: (set: any) => wrMapActions = (
set: any
) => {
return {
buildingNmSearch: async (
flg: string,
sigunCode: string,
searchNm: string | undefined
) => {
try {
// if (!searchNm || searchNm.length < 2) {
// set((state: wrMapState) => ({
// ...state,
// buildingSearchData: []
// }));
// return;
// }
console.log("건축물 코드, 시군구 코드, 검색어: ", flg, sigunCode, searchNm);
const response = await findBdst(flg, sigunCode, searchNm);
console.log("response: ", response);
// API 응답을 SearchInputText 형식으로 변환
const searchListFormatted = (response || []).map((building: any) => ({
searchNm: building.bdstNm,
searchPk: String(building.bdstInfoPk),
dmdCoNm: building.bdstNm,
layerType: flg,
...building
}));
// findBdst와 유사한 API 호출
// findBdst(flg, sigunCode || "", searchNm).then(() => {
// console.log("건물 검색 API 호출: ", sigunCode, searchNm, flg);
// // bdst.bdstBase를 SearchInputText 형식으로 변환
// const searchListFormatted = (state.bdst.bdstBase || []).map((building: any) => ({
// searchNm: building.bdstNm,
// searchPk: String(building.bdstInfoPk),
// dmdCoNm: building.bdstNm,
// layerType: flg,
// ...building
// }));
// set((state: any) => ({
// ...state,
// wrMap: {
// ...state.wrMap,
// buildingSearchData: searchListFormatted
// }
// }));
// });
set((state: wrMapState) => ({
...state,
wrMap: {
...state.wrMap,
buildingSearchRes: searchListFormatted
}
}));
} catch (error) {
console.error("건물 검색 중 오류:", error);
}
},
buildingNmSearchMultiple: async (
checkedLayers: string[],
sigunCode: string,
searchNm: string | undefined
) => {
try {
if (!searchNm || searchNm.length < 2) {
set((state: any) => ({
...state.wrMap,
buildingSearchData: []
}));
return;
}
// 체크된 레이어들에 대해 병렬로 API 호출
const searchPromises = checkedLayers
.filter(layer => LAYER_FLAG_MAP[layer]) // 매핑된 레이어만 필터링
.map(layer => {
const flg = LAYER_FLAG_MAP[layer];
return findBdst(flg, sigunCode, searchNm)
.then(response => {
console.log(`레이어 ${layer}(${flg}) API 응답:`, response);
const searchListFormatted = (state.bdst.bdstBase || []).map((building: any) => ({
searchNm: building.bdstNm,
searchPk: String(building.bdstInfoPk),
dmdCoNm: building.bdstNm,
layerType: flg,
...building
}));
return {
layerType: layer,
flg: flg,
data: bdstNames
};
})
.catch(error => {
console.error(`레이어 ${layer}(${flg}) 검색 중 오류:`, error);
return { layerType: layer, flg: flg, data: [] };
});
});
const results = await Promise.all(searchPromises);
console.log("다중 레이어 검색 결과:", results);
// 모든 결과를 하나의 배열로 합치기
const allSearchResults = results.reduce((acc, result) => {
const formatted = result.data.map((building: any) => ({
searchNm: building.bdstNm,
searchPk: String(building.bdstInfoPk),
dmdCoNm: building.bdstNm,
layerType: result.layerType,
flg: result.flg,
...building.originalData
}));
return [...acc, ...formatted];
}, [] as any[]);
console.log("최종 합쳐진 검색 결과:", {
totalCount: allSearchResults.length,
sampleData: allSearchResults.slice(0, 3)
});
set((state: any) => ({
...state.wrMap,
buildingSearchData: allSearchResults
}));
} catch (error) {
console.error("다중 레이어 검색 중 오류:", error);
set((state: any) => ({
...state,
buildingSearchData: []
}));
}
}
};
};
@@ -29,12 +29,12 @@ export const coRvwHeadListExport: CommonTableProps[]= [
visible: true,
},
{
id: "regDt", //바꾸기
id: "dmndTmng",
title: "신청일자",
visible: true,
},
{
id: "state", //바꾸기
id: "splyFcltFlg",
title: "진행상태",
visible: true,
},
+61 -39
View File
@@ -1,13 +1,18 @@
import { constCostHeadListExport, coRvwHeadListExport, coRvwNodeListExport, rateFeeHeadListExport } from "./headList";
import {
constCostHeadListExport,
coRvwHeadListExport,
coRvwNodeListExport,
rateFeeHeadListExport,
} from "./headList";
import type { CommonTableProps } from "../../common/type/header";
import type { DedMatUpdateType, DmdCoReqType } from "./type";
import { fetchType, getFetch, putFectch } from "@/components/lib/api";
import toast from "react-hot-toast/headless";
import { fetchType, getFetch, getFetchWithCount, putFectch } from "@/components/lib/api";
import toast from "react-hot-toast";
const coRvwHeadList = coRvwHeadListExport;
const coRvwNodeList = coRvwNodeListExport;
const rateFeeHeadList =rateFeeHeadListExport;
const constCostHeadList=constCostHeadListExport
const rateFeeHeadList = rateFeeHeadListExport;
const constCostHeadList = constCostHeadListExport;
export type coRvwState = {
coRvw: {
isInit: boolean;
@@ -16,6 +21,7 @@ export type coRvwState = {
coRvwNodeList: CommonTableProps[];
rateFeeHeadList: CommonTableProps[];
constCostHeadList: CommonTableProps[];
coMatRvwCount:number;
};
};
@@ -25,8 +31,9 @@ export const coRvwStateExport: coRvwState = {
coMatRvwRes: [],
coRvwHeadList: coRvwHeadList,
coRvwNodeList: coRvwNodeList,
rateFeeHeadList:rateFeeHeadList,
constCostHeadList:constCostHeadList
rateFeeHeadList: rateFeeHeadList,
constCostHeadList: constCostHeadList,
coMatRvwCount:1
},
};
@@ -37,7 +44,7 @@ type coRvwActions = {
pageNo: number,
pageCnt: number,
startYear: string,
endYear: string,
endYear: string
) => void;
coMatRvwUpdate: (
splyCd: string,
@@ -50,7 +57,7 @@ type coRvwActions = {
startYear: string,
endYear: string,
sggCd: string,
dmdCoNm: string,
dmdCoNm: string
) => void;
coMatRvwInqua: (
pageNo: number,
@@ -65,10 +72,10 @@ const coMatInit = async (
pageNo: number,
pageCnt: number,
startYear: string,
endYear: string,
endYear: string
) => {
const url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
const coMatRvwRes = await getFetch(fetchType.WR, url);
const url = `/reuseFc/request/infoList/Sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
const coMatRvwRes = await getFetchWithCount(fetchType.WR, url);
return coMatRvwRes;
};
@@ -79,7 +86,7 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
pageNo: number,
pageCnt: number,
startYear: string,
endYear: string,
endYear: string
) => {
const coMatRvwRes = await coMatInit(pageNo, pageCnt, startYear, endYear);
@@ -88,7 +95,8 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
coRvw: {
...state.coRvw,
init: false,
coMatRvwRes: coMatRvwRes,
coMatRvwRes: coMatRvwRes.data,
coMatRvwCount:coMatRvwRes.count
},
}));
},
@@ -98,25 +106,37 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
data: DedMatUpdateType
) => {
try {
const isUpdate = await putFectch(
const response = await putFectch(
`/reuseFc/request/cst/${splyCd}/${dmdCd}`,
data
);
if (response.status === 200) {
const coMatRvwRes = coMatInit(
1,
10,
new Date().getFullYear().toString(),
new Date().getFullYear().toString(),
);
return set((state: coRvwState) => ({
...state,
coRvw: {
...state.coRvw,
init: false,
coMatRvwRes: coMatRvwRes,
},
}));
if(data.flg ==='Sub'){
toast.success("매칭 검토로 저장되었습니다.");
}else{
toast.success("매칭 검토완료로 저장되었습니다.");
}
const coMatRvwRes = await coMatInit(
1,
10,
new Date().getFullYear().toString(),
new Date().getFullYear().toString()
);
return set((state: coRvwState) => ({
...state,
coRvw: {
...state.coRvw,
coMatRvwRes: coMatRvwRes.data,
coMatRvwCount:coMatRvwRes.count,
init: false,
},
}));
} else {
toast.error("매칭 검토 저장에 실패했습니다.");
}
} catch (e) {
toast.error("매칭 검토 저장 오류입니다.");
}
@@ -127,9 +147,9 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
startYear: string,
endYear: string,
sggCd: string,
dmdCoNm: string,
dmdCoNm: string
) => {
let url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
let url = `/reuseFc/request/infoList/Sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
if (sggCd.length > 0) {
url += `/${sggCd}`;
@@ -142,14 +162,15 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
url += `/0`;
}
console.log(' url ', url)
const coMatRvwRes = await getFetch(fetchType.WR, url);
console.log(" url ", url);
const coMatRvwRes = await getFetchWithCount(fetchType.WR, url);
return set((state: coRvwState) => ({
...state,
coRvw: {
...state.coRvw,
coMatRvwRes: coMatRvwRes,
coMatRvwRes: coMatRvwRes.data,
coMatRvwCount:coMatRvwRes.count,
},
}));
},
@@ -161,23 +182,24 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
sggCd: string
) => {
try {
let url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
let url = `/reuseFc/request/infoList/Sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
if (sggCd.length > 0) {
url += `/${sggCd}/0`;
}else if(sggCd ==='0'){
} else if (sggCd === "0") {
url += `/0/0`;
}
const coMatRvwRes = await getFetch(fetchType.WR, url);
const coMatRvwRes = await getFetchWithCount(fetchType.WR, url);
return set((state: coRvwState) => ({
...state,
coRvw: {
...state.coRvw,
coMatRvwRes: coMatRvwRes,
coMatRvwRes: coMatRvwRes.data,
coMatRvwCount:coMatRvwRes.count,
},
}));
} catch (e) {
console.log(" error ", e);
}
}
},
};
};
+130 -155
View File
@@ -1,173 +1,148 @@
import { z } from "zod";
const DemandInfoSchema = z.object({
dmdInfoSn: z.number(),
dmdInfoFk: z.string(),
dmndTmng: z.coerce.date(),
dmndQty: z.number(),
wntUntprc: z.number(),
grssClfg: z.number(),
combResdch: z.number(),
trbt: z.number(),
blgcOxygDmndQnt: z.number(),
smll: z.number(),
crmty: z.number().nullish(),
tn: z.number(),
tph: z.number(),
ph: z.number(),
clIonQnt: z.number(),
cdtnv: z.number(),
rmrk: z.string(),
useUsgCd: z.string(),
mtchnCmptnYmd: z.string().nullish(),
rvwCmptnYmd: z.string().nullish()
});
const DemandMatchingSchema = z.object({
dmdInfoFk: z.string().nullish(),
splyFcltFk: z.string().nullish(),
splyFcltFlg: z.string().nullish(),
pbnstRuseFcltBizFk: z.number().nullish(),
splyBgngDay: z.coerce.date().nullish(),
splyEndDay: z.coerce.date().nullish(),
splyCpct: z.number().nullish(),
splyFmYn: z.string().nullish(),
useUsgCd: z.string().nullish(),
dmdInfoSn: z.number().nullish(),
splyCndtList:z.array(z.string().nullish() ).nullish()
});
const DemandCoSchema = z.object({
dmdInfoPk: z.string(),
dmdCoNm: z.string(),
ctpvCd: z.string(),
sggCd: z.string(),
daddr: z.string(),
splyBgngDay: z.coerce.date(),
splyEndDay: z.coerce.date(),
splyCpct: z.number(),
splyFmYn: z.string().nullish(),
telno: z.string(),
useUsgCd: z.string().nullish(),
regDt: z.coerce.date().nullish(),
rgtr: z.string().nullish(),
mdfcnDt: z.coerce.date().nullish(),
mdfr: z.string().nullish(),
dmdAplyPswd: z.string(),
demandInfo: z.array(DemandInfoSchema.nullish()).nullish(), // 안쓰는 거임
demandMatchings: z.array(DemandMatchingSchema.nullish()).nullish()// 안쓰는 거임
});
const DemandCstSchema = z.object({
dmdInfoSn: z.number(),
usgPrcsProcs: z.string().nullish(),
cstrnCst: z.number(),
fcltSbsdCstrnCst: z.number(),
prcsFcltCpctCstrnCst: z.number()
});
export const DmdCoReqSchema = z.object({
cityText:z.string().nullish(),
sggText:z.string().nullish(),
dmdInfoSn: z.number(),
dmdInfoFk: z.string(),
dmndTmng: z.coerce.date(),
dmndQty: z.number(),
wntUntprc: z.number().nullish(),
grssClfg: z.number().nullish(),
combResdch: z.number().nullish(),
trbt: z.number().nullish(),
blgcOxygDmndQnt: z.number(),
smll: z.number().nullish(),
crmty: z.number().nullish(),
tn: z.number().nullish(),
tph: z.number().nullish(),
ph: z.number().nullish(),
clIonQnt: z.number().nullish(),
cdtnv: z.number(),
rmrk: z.string().nullish(),
useUsgCd: z.string().nullish(),
mtchnCmptnYmd: z.string().nullish(),
rvwCmptnYmd: z.string().nullish(),
demandCo: DemandCoSchema.nullish(),
demandMatching: DemandMatchingSchema.nullish(),
demandCst: DemandCstSchema,
ruseUtztnFcltNm:z.string().nullish(),
totalConstCost:z.number().nullish(),
const DemandInfoSchema = z.object({
dmdInfoSn: z.number(),
dmdInfoFk: z.string().nullish(),
dmndTmng: z.coerce.date().nullish(),
dmndQty: z.number().nullish(),
wntUntprc: z.number().nullish(),
grssClfg: z.number().nullish(),
combResdch: z.number().nullish(),
trbt: z.number().nullish(),
blgcOxygDmndQnt: z.number(),
smll: z.number().nullish(),
crmty: z.number().nullish(),
tn: z.number().nullish(),
tph: z.number().nullish(),
ph: z.number(),
clIonQnt: z.number().nullish(),
cdtnv: z.number().nullish(),
rmrk: z.string().nullish(),
useUsgCd: z.string().nullish(),
});
});
const DemandMatchingSchema = z.object({
dmdInfoFk: z.string().nullish(),
splyFcltFk: z.string().nullish(),
splyFcltFlg: z.string().nullish(),
pbnstRuseFcltBizFk: z.number().nullish(),
splyBgngDay: z.coerce.date().nullish(),
splyEndDay: z.coerce.date().nullish(),
splyCpct: z.number().nullish(),
splyFmYn: z.string().nullish(),
useUsgCd: z.string().nullish(),
dmdInfoSn: z.number().nullish(),
splyCndtList: z.array(z.string().nullish()).nullish(),
mtchnCmptnYmd: z.string().nullish(),
rvwCmptnYmd: z.string().nullish(),
});
const DemandCoSchema = z.object({
dmdInfoPk: z.string().nullish(),
dmdCoNm: z.string().nullish(),
ctpvCd: z.string().nullish(),
sggCd: z.string().nullish(),
daddr: z.string().nullish(),
splyBgngDay: z.coerce.date().nullish(),
splyEndDay: z.coerce.date().nullish(),
splyCpct: z.number().nullish(),
splyFmYn: z.string().nullish(),
telno: z.string().nullish(),
useUsgCd: z.string().nullish(),
regDt: z.coerce.date().nullish(),
rgtr: z.string().nullish(),
mdfcnDt: z.coerce.date().nullish(),
mdfr: z.string().nullish(),
dmdAplyPswd: z.string().nullish(),
demandInfo: z.array(DemandInfoSchema.nullish()).nullish(), // 안쓰는 거임
demandMatchings: z.array(DemandMatchingSchema.nullish()).nullish(), // 안쓰는 거임
});
const DemandCstSchema = z.object({
dmdInfoSn: z.number().nullish(),
usgPrcsProcs: z.string().nullish(),
cstrnCst: z.number().default(0),
fcltSbsdCstrnCst: z.number().default(0),
prcsFcltCpctCstrnCst: z.number().default(0),
});
export const DmdCoReqSchema = z.object({
cityText: z.string().nullish(),
sggText: z.string().nullish(),
dmdInfoSn: z.number().default(0),
dmdInfoFk: z.string().default(''),
dmndTmng: z.coerce.date().nullish(),
dmndQty: z.number().nullish(), // null 또는 undefined 허용
blgcOxygDmndQnt: z.number().nullish(),
wntUntprc: z.number().nullish(),
grssClfg: z.number().nullish(),
combResdch: z.number().nullish(),
trbt: z.number().nullish(),
smll: z.number().nullish(),
crmty: z.number().nullish(),
tn: z.number().nullish(),
tph: z.number().nullish(),
ph: z.number().nullish(),
clIonQnt: z.number().nullish(),
cdtnv: z.number().nullish(),
rmrk: z.string().nullish(),
useUsgCd: z.string().nullish(),
mtchnCmptnYmd: z.string().nullish(),
rvwCmptnYmd: z.string().nullish(),
demandCo: DemandCoSchema.nullish(),
demandMatching: DemandMatchingSchema.nullish(),
demandCst: DemandCstSchema.nullish(),
ruseUtztnFcltNm: z.string().nullish(),
totalConstCost: z.number().nullish(),
});
export type DmdCoReqType = z.infer<typeof DmdCoReqSchema>;
export type DmdMatTableType = {
cityText:string,
sggText:string,
dmdCoNm:string,
daddr:string,
telno:string,
regDt:string,
splyFcltFlg:string
}
cityText: string;
sggText: string;
dmdCoNm: string;
daddr: string;
telno: string;
regDt: string;
splyFcltFlg: string;
};
export type DedMatUpdateType =
{
export type DedMatUpdateType = {
dmdInfoSn: number;
usgPrcsProcs: string;
cstrnCst: number;
fcltSbsdCstrnCst: number;
prcsFcltCpctCstrnCst: number;
rgtr: string;
}
flg?:string;
};
export type RvwHistTableType = {
cityText:string,
sggText:string,
dmdCoNm:string,
daddr:string,
telno:string,
regDt:string,
rvwCmptnYmd:string,
mtchnCmptnYmd:string,
ruseUtztnFcltNm:string,
dmndQty:string,
useUsgCd:string,
splyFcltFlg:string
completeYn:boolean
dmdInfoSn :string
cityText: string;
sggText: string;
dmdCoNm: string;
daddr: string;
telno: string;
regDt: string;
rvwCmptnYmd: string;
mtchnCmptnYmd: string;
ruseUtztnFcltNm: string;
dmndQty: string;
useUsgCd: string;
splyFcltFlg: string;
completeYn: boolean;
dmdInfoSn: string;
};
}
export type RvwHistTableType = {
cityText:string,
sggText:string,
dmdCoNm:string,
daddr:string,
telno:string,
regDt:string,
rvwCmptnYmd:string,
mtchnCmptnYmd:string,
ruseUtztnFcltNm:string,
dmndQty:string,
useUsgCd:string,
splyFcltFlg:string
completeYn:boolean
dmdInfoSn :string
}
export type conCostFeeType= {
yr: string,
fctCpc: string,
fctCd: string,
stdCost: string
}
export type conCostFeeType = {
yr: string;
fctCpc: string;
fctCd: string;
stdCost: string;
};
export type rateFeeType = {
yr: string,
incExpCd: string,
stdCost: number,
rt: number
}
yr: string;
incExpCd: string;
stdCost: number;
rt: number;
};
@@ -44,6 +44,11 @@ export const coSttHeadListExport: z.infer<typeof sttnHeadListType> = [
title: "전화번호",
visible: true,
},
{
id: "regDt",
title: "등록일자",
visible: true,
},
];
export const coSttNodeListExport: CommonTableProps[] = [
+1 -1
View File
@@ -2,7 +2,7 @@ import type { CommonTableProps } from "@/app/store/common/type/header";
import { fetchType, getFetch, getFetchWithCount } from "@/components/lib/api";
import { coSttHeadListExport, coSttNodeListExport } from "./headList";
import { excelCoSttnParsing } from "./excel";
import type { DemandCompType } from "@common/business_components/ui";
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
// 테이블 헤드 리스트
const coSttNodeList = coSttNodeListExport;
+16 -17
View File
@@ -1,5 +1,19 @@
import { z } from "zod";
import type { CommonTableProps } from "../../common/type/header";
export const drgtZod = z.object({
ctpvCd: z.string().default('').nullish(),
sggNm: z.string().default('').nullish(),
normal: z.string().default(''),
attention: z.string().default(''),
caution: z.string().default(''),
warning: z.string().default(''),
ranking: z.string().default(''),
year: z.string().default(''),
month: z.string().default(''),
drgtState: z.string().default('')
})
export const drgtHeadListExport: CommonTableProps[] = [
{
id: "ctpvCd",
@@ -10,21 +24,13 @@ export const drgtHeadListExport: CommonTableProps[] = [
colSpan: 1,
},
{
id: "sggCd",
id: "sggNm",
title: "시군구",
visible: true,
row: 1,
rowSpan: 1,
colSpan: 1,
},
{
id: "haengjung",
title: "행정동",
visible: true,
row: 1,
rowSpan: 1,
colSpan: 1,
},
{
id: "normal",
title: "정상(횟수)",
@@ -83,16 +89,9 @@ export const drgtDetailHeadListExport: CommonTableProps[] = [
row: 1,
rowSpan: 1,
colSpan: 1,
},{
id: "day",
title: "일",
visible: true,
row: 1,
rowSpan: 1,
colSpan: 1,
},
{
id: "state",
id: "drgtState",
title: "가뭄상태",
visible: true,
row: 1,
+153 -9
View File
@@ -1,27 +1,171 @@
import { fetchType, getFetch, postFectch } from "@/components/lib/api";
import type { CommonTableProps } from "../../common/type/header";
import { drgtHeadListExport } from "./headList";
import { drgtHeadListExport, drgtZod } from "./headList";
import type { z } from "zod";
const drgtHeadList = drgtHeadListExport;
export type customDrgt = z.infer<typeof drgtZod>;
const drgtRes: customDrgt[] = [];
export type drgtState = {
drgt:{
drgtHeadList: CommonTableProps[];
}
drgt: {
drgtHeadList: CommonTableProps[];
drgtRes: customDrgt[];
drgtMoRes: customDrgt[];
isInit: boolean;
}
}
type drgtActions = {
drgtInit: () => void
drgtSearch: (fromDate: string, toDate: string, sido: string[]) => void
drgtModalSearch: (fromDate: string, toDate: string, sgg: string) => void
};
export type drgtType = drgtState & drgtActions;
export const drgtStateExport: drgtState = {
drgt: {
drgtHeadList: drgtHeadList,
}
drgt: {
drgtHeadList: drgtHeadList,
drgtRes: drgtRes,
drgtMoRes: drgtRes,
isInit: false,
}
}
const initFetch = async (fromDate: string, toDate: string, sido:string[]) => {
console.log("sido",sido)
const drgtData = await postFectch(
`/il/getDataCount/${fromDate}/${toDate}`,
sido,
fetchType.WR
);
//주의, 관심, 정상 추출
const statusData = Array.from(
new Set(drgtData.data.map((e) => e.lvngMfgUswtrDrgtInfo))
);
const statusMap = drgtData.data.reduce((acc: any, { sggNm, lvngMfgUswtrDrgtInfo: status, countDrgt }: { sggNm: string; lvngMfgUswtrDrgtInfo: string; countDrgt: number }) => {
if (!acc[sggNm]) {
acc[sggNm] = { sggNm, ...Object.fromEntries(statusData.map((e) => [e, 0])) };
}
acc[sggNm][status] = Number(countDrgt);
return acc;
}, {});
const data = Object.values(statusMap);
const formatStatus = data.map((item) => {
const attention = item['관심'] || 0;
const caution = item['주의'] || 0;
return {
sggNm: item.sggNm,
normal: String(item['정상'] || 0),
caution: String(caution),
attention: String(attention),
warning: String(attention + caution),
};
});
const ranking = Array.from(
new Set(formatStatus.map(e => Number(e.warning)))
).sort((a, b) => b - a);
const rankData = ranking.reduce((acc, val, idx) => {
acc[val] = idx + 1;
return acc;
}, {});
const drgtRes = formatStatus.map(item => ({
...item,
warning: String(item.warning),
ranking: rankData[Number(item.warning)],
}));
return drgtRes;
}
export const drgtActionsExport: (set: any) => drgtActions = (
set: any
) => {
return {};
};
return {
drgtInit: async () => {
try {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const formatted = `${year}${month}`;
const fromDate = formatted;
const toDate = formatted;
//FIXME: 페이지 처음 로딩될때 데이터 보려고 6월로 박아놓음 이거 fromDate로 고치기
const drgtRes = await initFetch("202506", toDate, []);
set((state: drgtState) => {
return {
...state,
drgt: {
...state.drgt,
drgtRes: drgtRes,
isInit: false,
},
}
});
} catch (e) {
console.log(e)
}
},
drgtSearch: async (fromDate: string, toDate: string, sido: string[]) => {
try {
const drgtRes = await initFetch(fromDate, toDate, sido);
set((state: drgtState) => {
return {
...state,
drgt: {
...state.drgt,
drgtRes: drgtRes,
},
}
});
} catch (e) {
console.log(e)
}
},
drgtModalSearch: async (fromDate: string, toDate: string, sgg: string) => {
try {
const drgtMoData = await getFetch(
fetchType.WR,
`/il/getData/${fromDate}/${toDate}/${sgg}/${sgg}`
);
const drgtMoRes = drgtMoData.map((item) => {
const year = item.anlsYmd.slice(0, 4);
const month = item.anlsYmd.slice(4, 6);
const drgtState = item.lvngMfgUswtrDrgtInfo;
const sggNm = item.sggNm;
return {
year: year,
month: month,
drgtState: drgtState,
sggNm: sggNm
}
});
set((state: drgtState) => {
return {
...state,
drgt: {
...state.drgt,
drgtMoRes: drgtMoRes,
},
}
});
} catch (e) {
console.log(e)
}
},
}
}
@@ -46,29 +46,17 @@ export const names: nameMap = {
}
export const excelPbnstSttnParsing = (data: any[]): string[][] => {
export const excelLdrmntParsing = (data: any[]): string[][] => {
const excelExportData: string[][] = [];
data.forEach((item) => {
excelExportData.push([
item.rvbsn,
String(item.lgbsn),
item.mdmbsn ?? "",
item.rgnNm ?? "",
String(item.sido ?? ""),
String(item.gugun ?? ""),
String(item.bscFctNm ?? ""),
String(item.efflWqltStndrdNm ?? ""),
String(item.fctCpc ?? ""),
String(item.tel ?? ""),
String(item.ruseUtztnFcltNm ?? ""),
String(item.fcltCpct ?? ""),
String(item.splyExtraCpct ?? ""),
String(item.ruseCpct ?? ""),
String(item.usageTotal ?? ""),
String(item.inUsage ?? ""),
String(item.outUsage ?? ""),
item.fctryAddr?.split(" ")[0] || "",
item.fctryAddr?.split(" ")[1] || "",
item.dmdCoNm,
item.fctryAddr,
item.fctryMngNo,
item.telno,
])
})
return excelExportData;
+40 -27
View File
@@ -2,10 +2,8 @@ import type { CommonTableProps } from "@/app/store/common/type/header";
import { deleteFectch, fetchType, getFetch, postFectch, putFectch } from "@/components/lib/api";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import { z } from "zod";
import { ldrmntHeadListExport, ldrmntNodeListExport, ldrmntZod } from "./headList";
import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg";
import { ldrmntHeadListExport, ldrmntNodeListExport, ldrmntZod } from "./headList";
// 테이블 헤드 리스트
const ldrmntHeadList = ldrmntHeadListExport;
@@ -34,7 +32,7 @@ const excelDownList: ExcelType[] = [];
type ldrmntActions = {
ldrmntRegData: (data: any[]) => void,
ldrmntInit: (con: string) => void
ldrmntSearch: (page: number, sggCd: string, coNm: string) => void;
ldrmntSearch: (page: number, sggCd: string, total: string, coNm: string) => void;
ldrmntUpdate: (ldrmntRes: customLdrmnt) => void;
ldrmntDelete: () => void;
};
@@ -61,6 +59,7 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
fetchType.WR,
`/reuseFc/request/temp/1/20/${con}`
);
set((state: ldrmntState) => {
return {
...state,
@@ -68,6 +67,7 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
...state.ldrmnt,
ldrmntRes: ldrmntRes,
isInit: false,
excelList: excelDownList,
},
}
});
@@ -101,44 +101,57 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
}
},
ldrmntSearch: async (page: number, sggCd: string, coNm: string) => {
ldrmntSearch: async (page: number, total: string, sggCd: string, coNm: string) => {
let searchUrl = "";
if (sggCd !== "") {
if (coNm !== "") {
searchUrl = `/reuseFc/request/temp/${page}/20/${sggCd}/${coNm}`;
searchUrl = `/reuseFc/request/temp/${page}/${total}/${sggCd}/${coNm}`;
} else {
searchUrl = `/reuseFc/request/temp/${page}/20/${sggCd}`;
searchUrl = `/reuseFc/request/temp/${page}/${total}/${sggCd}`;
}
}
try {
const ldrmntRes = await getFetch(
fetchType.WR,
searchUrl
);
ldrmntRes.forEach((item: customLdrmnt) => {
excelDownList.push({
ctpvCd: item.ctpvCd,
sggCd: item.sggCd,
dmdCoNm: item.dmdCoNm,
fctryAddr: item.fctryAddr,
fctryMngNo: item.fctryMngNo,
telno: item.telno,
if (Number(total) > 5000) {
let excelDownList: ExcelType[] = [];
ldrmntRes.forEach((item: customLdrmnt) => {
excelDownList.push({
ctpvCd: item.ctpvCd,
sggCd: item.sggCd,
dmdCoNm: item.dmdCoNm,
fctryAddr: item.fctryAddr,
fctryMngNo: item.fctryMngNo,
telno: item.telno,
})
})
})
set((state: ldrmntState) => {
return {
...state,
ldrmnt: {
...state.ldrmnt,
excelList: excelDownList,
set((state: ldrmntState) => {
return {
...state,
ldrmnt: {
...state.ldrmnt,
ldrmntRes: ldrmntRes,
excelList: excelDownList,
},
}
})
},
};
});
return excelDownList;
} else
set((state: ldrmntState) => {
return {
...state,
ldrmnt: {
...state.ldrmnt,
ldrmntRes: ldrmntRes,
isInit: false,
},
};
});
return ldrmntRes;
} catch (e) {
console.log(e)
}
@@ -0,0 +1,109 @@
import { z } from "zod";
export const reUseCaseExcel = z.object({
file: z.any()
});
type nameMap = {
[key: string]: string;
}
export const reUseCaseNames: nameMap = {
"사업명": "ruseBizNm",
"사업시작일": "bizBgngDate",
"사업종료일": "bizEndDate",
"공급처_시설종류": "splyFcltKnd",
"공급처명": "splyFcltNm",
"공급처_시설용량(㎥/일)": "splyFcltCpct",
"수요처명": "dmdFcltNm",
"수요처_주소": "dmdFcltAddr",
"재이용시설_시설용량(㎥/일)": "ruseFcltCpct",
"재이용시설_관로(km)": "ruseFcltPplLen",
"재이용시설_용도": "ruseFcltUsg",
"재이용시설_공급량(㎥/일)": "ruseFcltSplyCpct",
"재이용시설_처리공법": "ruseFcltPrcsMsrc",
"보증수질_용도별": "grnteWqUsg",
"보증수질_총대장균군수": "grnteWqGrssClfg",
"보증수질_결합잔류염소": "grnteWqCombResdch",
"보증수질_탁도": "grnteWqTrbt",
"보증수질_생물학적산소요구량(BOD)(mg/L)": "grnteWqBod",
"보증수질_냄새": "grnteWqSmll",
"보증수질_색도(도)": "grnteWqCrmty",
"보증수질_총질소(T-N)(mg/L)": "grnteWqTn",
"보증수질_총인(T-P)(mg/L)": "grnteWqTp",
"보증수질_수소이온농도(pH)": "grnteWqPh",
"보증수질_염화물": "grnteWqClIonQnt",
"보증수질_전기전도도": "grnteWqCndctStat",
"보증수질_비고": "grnteWqRmrk",
"소요사업비_총(백만원)": "reqBizGrssCst",
"소요사업비_토목_재이용시설(백만원)": "reqBizReuseFcltCst",
"소요사업비_토목_관로(백만원)": "reqBizPplCst",
"소요사업비_건축(백만원)": "reqBizArchCst",
"소요사업비_기계(백만원)": "reqBizMchnCst",
"소요사업비_전기.계측(백만원)": "reqBizMtrngCst",
"소요사업비_GIS DB(백만원)": "reqBizGisCst",
"소요사업비_CCTV(백만원)": "reqBizCctvCst",
"소요사업비_시공 및 준공(백만원)": "reqBizCmcnCst",
"소요사업비_폐기물(백만원)": "reqBizWstCst",
"소요사업비_설계비(백만원)": "reqBizDesignCst",
"소요사업비_감리비(백만원)": "reqBizSpvsCst",
"소요사업비_시설부대비(백만원)": "reqBizSbsdCst",
"소요사업비_기타(백만원)": "reqBizEtc",
"재원조달계획_총(백만원)": "prcmPlanGrssCst",
"재원조달계획_국비(백만원)": "prcmPlanNlep",
"재원조달계획_지방비(백만원)": "prcmPlanLcep",
"재원조달계획_원인자부담(백만원)": "prcmPlanBrdnCst",
"재원조달계획_국고비율(백만원)": "prcmPlanEcqrRt",
"사업비 정산_총(백만원)": "clclnGrssCst",
"사업비 정산_국비(백만원)": "clclnNlep",
"사업비 정산_지방비(백만원)": "clclnLcep",
"사업비 정산_원인자부담(백만원)": "clclnBrdnCst",
"사업비 정산_국고비율(백만원)": "clclnEcqrRt"
}
interface PbnstSttnItem {
rvbsn: string;
lgbsn: number;
mdmbsn?: string;
rgnNm?: string;
sido?: number;
gugun?: number;
bscFctNm?: string;
efflWqltStndrdNm?: string;
fctCpc?: number;
tel?: string;
ruseUtztnFcltNm?: string;
fcltCpct?: number;
splyExtraCpct?: number;
ruseCpct?: number;
usageTotal?: number;
inUsage?: number;
outUsage?: number;
}
export const excelPbnstSttnParsing = (data: PbnstSttnItem[]): string[][] => {
const excelExportData: string[][] = [];
data.forEach((item) => {
excelExportData.push([
item.rvbsn,
String(item.lgbsn),
item.mdmbsn ?? "",
item.rgnNm ?? "",
String(item.sido ?? ""),
String(item.gugun ?? ""),
String(item.bscFctNm ?? ""),
String(item.efflWqltStndrdNm ?? ""),
String(item.fctCpc ?? ""),
String(item.tel ?? ""),
String(item.ruseUtztnFcltNm ?? ""),
String(item.fcltCpct ?? ""),
String(item.splyExtraCpct ?? ""),
String(item.ruseCpct ?? ""),
String(item.usageTotal ?? ""),
String(item.inUsage ?? ""),
String(item.outUsage ?? ""),
])
})
return excelExportData;
};
@@ -4,15 +4,22 @@ import type { TableUpperProps } from "../../common/type/header";
//★☆★☆ 여기서부터 수정하기 ★☆★☆
export const ruseCaseHeadListExport: TableUpperProps[] = [
{
id: "a",
id: "ruseBizNm",
title: "사업명",
visible: true,
row: 0,
rowSpan: 2,
},
{
id: "b",
title: "사업기간",
id: "bizBgngDate",
title: "사업시작일",
visible: true,
row: 0,
rowSpan: 2,
},
{
id: "bizEndDate",
title: "사업종료일",
visible: true,
row: 0,
rowSpan: 2,
@@ -25,19 +32,19 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 3,
},
{
id: "d",
id: "splyFcltKnd",
title: "시설종류",
visible: true,
row: 1,
},
{
id: "e",
id: "splyFcltNm",
title: "공급처명",
visible: true,
row: 1,
},
{
id: "f",
id: "splyFcltCpct",
title: "시설용량(m³/일)",
visible: true,
row: 1,
@@ -50,13 +57,13 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 2,
},
{
id: "h",
id: "dmdFcltNm",
title: "수요처명",
visible: true,
row: 1,
},
{
id: "i",
id: "dmdFcltAddr",
title: "주소",
visible: true,
row: 1,
@@ -69,31 +76,31 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 5,
},
{
id: "q2",
id: "ruseFcltCpct",
title: "시설용량(m³/일)",
visible: true,
row: 1,
},
{
id: "q3",
id: "ruseFcltPplLen",
title: "관로(km)",
visible: true,
row: 1,
},
{
id: "q4",
id: "ruseFcltUsg",
title: "용도",
visible: true,
row: 1,
},
{
id: "q5",
id: "ruseFcltSplyCpct",
title: "공급량(m³/일)",
visible: true,
row: 1,
},
{
id: "q6",
id: "ruseFcltPrcsMsrc",
title: "처리공법",
visible: true,
row: 1,
@@ -106,79 +113,79 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 13,
},
{
id: "k1",
id: "grnteWqUsg",
title: "용도별",
visible: true,
row: 1,
},
{
id: "k2",
id: "grnteWqGrssClfg",
title: "총대장균군수​(개/100ml)",
visible: true,
row: 1,
},
{
id: "k3",
id: "grnteWqCombResdch",
title: "결합잔류염소(mg/L)",
visible: true,
row: 1,
},
{
id: "k4",
id: "grnteWqTrbt",
title: "탁도(NTU)",
visible: true,
row: 1,
},
{
id: "k5",
id: "grnteWqBod",
title: "생물학적​산소요구량(BOD)(mg/L)​",
visible: true,
row: 1,
},
{
id: "k6",
id: "grnteWqSmll",
title: "냄새​",
visible: true,
row: 1,
},
{
id: "k7",
id: "grnteWqCrmty",
title: "색도​",
visible: true,
row: 1,
},
{
id: "k8",
id: "grnteWqTn",
title: "총질소(T-N)(mg/L)​",
visible: true,
row: 1,
},
{
id: "k9",
id: "grnteWqTp",
title: "총인(T-P)(mg/L)",
visible: true,
row: 1,
},
{
id: "k10",
id: "grnteWqPh",
title: "수소이온농도(PH)​",
visible: true,
row: 1,
},
{
id: "k11",
title: "수소이온농도(PH)​",
id: "grnteWqClIonQnt",
title: "염화물",
visible: true,
row: 1,
},
{
id: "k12",
title: "전기전도도​(㎲/cm",
id: "grnteWqCndctStat",
title: "전기전도도​(㎲/cm)",
visible: true,
row: 1,
},
{
id: "k13",
id: "grnteWqRmrk",
title: "비고​",
visible: true,
row: 1,
@@ -191,85 +198,85 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 14,
},
{
id: "h1",
id: "reqBizGrssCst",
title: "총",
visible: true,
row: 1,
},
{
id: "h2",
title: "토목재이용시설",
id: "reqBizReuseFcltCst",
title: "토목재이용시설",
visible: true,
row: 1,
},
{
id: "h3",
id: "reqBizPplCst",
title: "토목관로",
visible: true,
row: 1,
},
{
id: "h4",
id: "reqBizArchCst",
title: "건축",
visible: true,
row: 1,
},
{
id: "h5",
id: "reqBizMchnCst",
title: "기계",
visible: true,
row: 1,
},
{
id: "h6",
id: "reqBizMtrngCst",
title: "전기・계측",
visible: true,
row: 1,
},
{
id: "h7",
id: "reqBizGisCst",
title: "GIS・DB",
visible: true,
row: 1,
},
{
id: "h8",
id: "reqBizCctvCst",
title: "CCTV",
visible: true,
row: 1,
},
{
id: "h9",
id: "reqBizCmcnCst",
title: "시공및준공",
visible: true,
row: 1,
},
{
id: "h10",
id: "reqBizWstCst",
title: "페기물",
visible: true,
row: 1,
},
{
id: "h11",
title: "설계비",
id: "reqBizDesignCst",
title: "설계비",
visible: true,
row: 1,
},
{
id: "h12",
id: "reqBizSpvsCst",
title: "감리비",
visible: true,
row: 1,
},
{
id: "h13",
id: "reqBizSbsdCst",
title: "시설부대비",
visible: true,
row: 1,
},
{
id: "h14",
id: "reqBizEtc",
title: "기타",
visible: true,
row: 1,
@@ -279,32 +286,38 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
title: "자원조달계획(백만원)",
visible: true,
row: 0,
colSpan: 4,
colSpan: 5,
},
{
id: "i1",
id: "prcmPlanGrssCst",
title: "총",
visible: true,
row: 1,
},
{
id: "i2",
id: "prcmPlanNlep",
title: "국비",
visible: true,
row: 1,
},
{
id: "i3",
id: "prcmPlanLcep",
title: "지방비",
visible: true,
row: 1,
},
{
id: "i4",
id: "prcmPlanBrdnCst",
title: "원인자부담",
visible: true,
row: 1,
},
{
id: "prcmPlanEcqrRt",
title: "국고비율",
visible: true,
row: 1,
},
{
id: "j",
title: "사업비정산(백만원)",
@@ -313,31 +326,31 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [
colSpan: 5,
},
{
id: "j1",
id: "clclnGrssCst",
title: "총",
visible: true,
row: 1,
},
{
id: "j2",
id: "clclnNlep",
title: "국비",
visible: true,
row: 1,
},
{
id: "j3",
id: "clclnLcep",
title: "지방비",
visible: true,
row: 1,
},
{
id: "j4",
id: "clclnBrdnCst",
title: "원인자부담",
visible: true,
row: 1,
},
{
id: "j5",
id: "clclnEcqrRt",
title: "국고비율",
visible: true,
row: 1,
+110 -10
View File
@@ -1,24 +1,124 @@
import { fetchType, getFetch, getFetchWithCount, postFectch } from "@/components/lib/api";
import type { TableUpperProps } from "../../common/type/header";
import { ruseCaseHeadListExport } from "./headList";
import { toast } from "react-hot-toast";
export type ruseCaseState = {
ruseCase: {
ruseHeadList:TableUpperProps[];
};
ruseCaseMng: {
ruseHeadList:TableUpperProps[];
ruseCaseMngList:ruseCaseMngExcelProps[];
status: number;
count: number;
};
type ruseCaseActions = {
};
export interface ruseCaseMngExcelProps {
ruseBizNm: string,
bizBgngDate: string,
bizEndDate: string,
splyFcltKnd: string,
splyFcltNm: string,
splyFcltCpct: number | 0 |'',
dmdFcltNm: string,
dmdFcltAddr: string,
ruseFcltCpct: number | 0 |'',
ruseFcltPplLen: string,
ruseFcltUsg: string,
ruseFcltSplyCpct: number | 0 |'',
ruseFcltPrcsMsrc: string,
grnteWqUsg: string,
grnteWqGrssClfg: number | 0 |'',
grnteWqCombResdch: number | 0 |'',
grnteWqTrbt: string,
grnteWqBod: string,
grnteWqSmll: string,
grnteWqCrmty: string,
grnteWqTn: string,
grnteWqTp: string,
grnteWqPh: string,
grnteWqClIonQnt: string,
grnteWqCndctStat: string,
grnteWqRmrk: string,
reqBizReuseFcltCst: number | 0 |'',
reqBizPplCst: number | 0 |'',
reqBizArchCst: number | 0 |'',
reqBizMchnCst: number | 0 |'',
reqBizMtrngCst: number | 0 |'',
reqBizGisCst: number | 0 |'',
reqBizCctvCst: number | 0 |'',
reqBizCmcnCst: number | 0 |'',
reqBizWstCst: number | 0 |'',
reqBizDesignCst: number | 0 |'',
reqBizSpvsCst: number | 0 |'',
reqBizSbsdCst: number | 0 |'',
reqBizEtc: string,
reqBizGrssCst: number | 0 |'',
prcmPlanNlep: number | 0 |'',
prcmPlanLcep: number | 0 |'',
prcmPlanBrdnCst: number | 0 |'',
prcmPlanEcqrRt: number | 0 |'',
prcmPlanGrssCst: number | 0 |'',
clclnNlep: number | 0 |'',
clclnLcep: number | 0 |'',
clclnBrdnCst: number | 0 |'',
clclnEcqrRt: number | 0 |'',
clclnGrssCst: number | 0 |'',
}
type ruseCaseActions = {
selectRuseCaseMng: (pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd : string, name: string) => Promise<void>;
insertRuseCaseMng: (data: any) => Promise<void>;
};
export const fetchSearchRuseCaseMng = async (pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd : string, name: string) => {
const tailUrl = '/'+pageNo+'/'+pageSize+ ((sigunguCd !== '전체' && sigunguCd !=='') ? `/${sigunguCd}` : (sidoCd != '전체' && sidoCd != '') ? `/${sidoCd}` : `/0`) + (usageCd? `/${usageCd}` : '/0') + (name? `/${name}` : '');
const resp = await getFetchWithCount(fetchType.WR,`/reuseFc/pubFcRuseCase${tailUrl}`);
return {data:resp.data, count:resp.count};
};
export const fetchInsertRuseCaseMng = async (data: any) => {
const response = await postFectch('/reuseFc/pubFcRuseCase', data, fetchType.WR);
return {data:response.data, status:response.status};
};
export type ruseCaseType = ruseCaseState & ruseCaseActions;
export const ruseCaseStateExport: ruseCaseState = {
ruseCase: {
ruseHeadList:ruseCaseHeadListExport,
ruseCaseMng: {
ruseHeadList:ruseCaseHeadListExport,
ruseCaseMngList:[],
status: 0,
count: 0,
},
};
export const ruseCaseActionsExport: (set: any) => ruseCaseActions = (
export const ruseCaseActionsExport: (set: any) => ruseCaseActions = (
set: any
) => {
return {
selectRuseCaseMng: async (pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd : string, name: string) => {
const res = await fetchSearchRuseCaseMng(pageNo, pageSize, sidoCd, sigunguCd, usageCd, name);
set((state: ruseCaseState) => ({
ruseCaseMng: {
...state.ruseCaseMng,
ruseCaseMngList: res.data,
count: res.count,
},
}));
},
insertRuseCaseMng: async (data: any) => {
const response = await fetchInsertRuseCaseMng(data);
if(response.status === 200){
toast.success("물재이용사례 등록이 완료되었습니다. ");
}else{
toast.error("물재이용사례 등록에 실패하였습니다. ");
}
set((state: ruseCaseState) => ({
ruseCaseMng: {
...state.ruseCaseMng,
status: response.status,
},
}));
},
};
};
};
@@ -28,7 +28,7 @@ export const ruseHstryHeadListExport:TableUpperProps[]= [
visible: true,
},
{
id: "regDt", //바꾸기
id: "dmndTmng", //바꾸기
title: "신청일자",
visible: true,
},
+26 -13
View File
@@ -5,7 +5,7 @@ import type {
TableUpperProps,
} from "../../common/type/header";
import { ruseHstryHeadListExport } from "./headList";
import { fetchType, getFetch, postFectch, putFectch } from "@/components/lib/api";
import { fetchType, getFetch, getFetchWithCount, postFectch, putFectch } from "@/components/lib/api";
import type { DmdCoReqType } from "../coMatRvw/type";
import toast from "react-hot-toast";
import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg";
@@ -16,6 +16,7 @@ export type ruseHstryState = {
ruseHist: {
isInit: boolean;
rvwHistRes: DmdCoReqType[];
rvwHistCount:number;
ruseHistHeadList: TableUpperProps[];
};
};
@@ -25,6 +26,7 @@ export const ruseHstryStateExport: ruseHstryState = {
isInit: false,
rvwHistRes: [],
ruseHistHeadList: ruseHistHeadList,
rvwHistCount:1,
},
};
@@ -79,14 +81,14 @@ const irvwHistInit = async (
startYear: string,
endYear: string,
) => {
let url = `/reuseFc/request/infoList/matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
let url = `/reuseFc/request/infoList/Matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
// console.log("sggCd ", sggCd);
// if (sggCd) {
// url += `/${sggCd}`;
// }
const rvwHistRes = await getFetch(fetchType.WR, url);
const rvwHistRes = await getFetchWithCount(fetchType.WR, url);
return rvwHistRes;
};
@@ -115,7 +117,8 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = (
ruseHist: {
...state.ruseHist,
init: false,
rvwHistRes: rvwHistRes,
rvwHistRes: rvwHistRes.data,
rvwHistCount:rvwHistRes.count
},
}));
},
@@ -140,8 +143,8 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = (
ruseHist: {
...state.ruseHist,
init: false,
rvwHistRes: rvwHistRes,
},
rvwHistRes: rvwHistRes.data,
rvwHistCount:rvwHistRes.count },
}));
}
} catch (e) {
@@ -158,7 +161,7 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = (
dmdCoNm: string,
ruseUtztnFcltNm: string
) => {
let url = `/reuseFc/request/infoList/matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
let url = `/reuseFc/request/infoList/Matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
if (sggCd.length > 0) {
url += `/${sggCd}`;
@@ -175,14 +178,14 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = (
} else {
url += `/0`;
}
const rvwHistRes = await getFetch(fetchType.WR, url);
const rvwHistRes = await getFetchWithCount(fetchType.WR, url);
return set((state: ruseHstryState) => ({
...state,
ruseHist: {
...state.ruseHist,
rvwHistRes: rvwHistRes,
},
rvwHistRes: rvwHistRes.data,
rvwHistCount:rvwHistRes.count },
}));
},
ruseRvwInqua: async (
@@ -193,16 +196,17 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = (
sggCd: string
) => {
try {
let url = `/reuseFc/request/infoList/matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
let url = `/reuseFc/request/infoList/Matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`;
if (sggCd.length > 0) {
url += `/${sggCd}/0`;
}
const rvwHistRes = await getFetch(fetchType.WR, url);
const rvwHistRes = await getFetchWithCount(fetchType.WR, url);
return set((state: ruseHstryState) => ({
...state,
ruseHist: {
...state.ruseHist,
rvwHistRes: rvwHistRes,
rvwHistRes: rvwHistRes.data,
rvwHistCount:rvwHistRes.count
},
}));
} catch (e) {
@@ -224,6 +228,15 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = (
new Date().getFullYear().toString(),
new Date().getFullYear().toString(),
);
return set((state: ruseHstryState) => ({
...state,
ruseHist: {
...state.ruseHist,
rvwHistRes: rvwHistRes.data,
rvwHistCount:rvwHistRes.count
},
}));
}
} catch (e) {
console.log(" error ", e);
@@ -94,15 +94,15 @@ export const excelToRainChkData = (excelData: any, year: string) => {
}
const makeGuidDetail = Array.from({ length: 12 }, (_, i) => {
let month = `${i + 1}`;
const splyData = item[`보급_${month}월`];
const splyData = item[`대체_${month}월`];
const ruseData = item[`빗물_${month}월`];
if (month.length < 2) month = "0" + month;
return {
utztnUsgCd: item["이용용도"],
ym: year + month,
chckYmd: checkYmd,
splyUswtrUtztnQnt: Number(splyData ?? 0),
ruseFcltUtztnQnt: Number(ruseData ?? 0),
splyUswtrUtztnQnt: Number(splyData) || 0,
ruseFcltUtztnQnt: Number(ruseData) || 0,
flg: "RNB",
};
});
@@ -120,13 +120,13 @@ export const excelToRainChkData = (excelData: any, year: string) => {
rainyDetails: [
{
utztnUsgCd: item["이용용도"] ?? "",
ph: Number(item["PH"] ?? 0),
trbt: Number(item["탁도"] ?? 0),
grssClfg: Number(item["총대장균군수"] ?? 0),
etc: item["기타"] ?? "", // 필드 누락 방지
vltnDt: vltnDt ?? "",
vltnMttr: item["위반사항"] ?? "",
levyAmt: Number(item["과태료부과금액(천원)"] ?? 0),
ph: Number(item["PH"] || 0),
trbt: Number(item["탁도"] || 0),
grssClfg: Number(item["총대장균군수"] || 0),
etc: item["기타"] || "", // 필드 누락 방지
vltnDt: vltnDt || "-",
vltnMttr: item["위반사항"] || "-",
levyAmt: Number(item["과태료부과금액(천원)"] || 0),
rgtr: TEST_UUID,
},
],
@@ -17,7 +17,7 @@ export const chkgdRegExcel = z.object({
},
{ message: "현재 년도보다 높은 수를 입력할 수 없습니다." }
),
file: z.instanceof(File).optional(),
file: z.instanceof(File).nullish(),
});
export const guideDetail = z.object({
@@ -114,10 +114,22 @@ export type splRuseWaterType = {
ruse12: number;
};
export const rainHeadNmExport: chHeadType[] = [
export const rainMainHeadNmExport: chHeadType[] = [
{ headNm: "useQuilty", text: "이용수질(필요시)", length: 3 },
{ headNm: "feeHist", text: "과태료 부과내역", length: 3 },
];
export const rainRuseHeadNmExport: chHeadType[] = [
{ headNm: "useQuilty", text: "이용수질(필요시)", length: 3 },
{ headNm: "feeHist", text: "과태료 부과내역", length: 3 },
{ headNm: "ruseWater", text: "연간대체수 이용량(m³)", length: 13 },
{ headNm: "detailQulity", text: "분기", length: 11 },
];
export const rainSplHeadNmExport: chHeadType[] = [
{ headNm: "useQuilty", text: "이용수질(필요시)", length: 3 },
{ headNm: "feeHist", text: "과태료 부과내역", length: 3 },
{ headNm: "ruseWater", text: "연간보급수 이용량(m³)", length: 13 },
{ headNm: "detailQulity", text: "분기", length: 11 },
{ headNm: "splWater", text: "연간빗물 이용량(m³)", length: 13 },
];
@@ -134,7 +146,7 @@ export const rainChkgdnHeadListExport: TableUpperProps[] = [
{ id: "dtfcltCpct", title: "빗물저류조용량(m³)", mergeCell: true },
{ id: "chckYmd", title: "점검일", mergeCell: true },
{ id: "utztnUsgCd", title: "이용용도" },
{ id: "totalSplyUswtrUtztnQnt", title: "연간보급수이용량(m³)" },
{ id: "totalSplyUswtrUtztnQnt", title: "연간대체수이용량(m³)" },
{ id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)" },
{
id: "ph",
@@ -1,27 +1,31 @@
import { buildingType } from "@/app/store/common/code";
import {
TEST_UUID,
} from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import type { TableUpperProps } from "@/app/store/common/type/header";
import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import {
deleteFectch,
fetchType,
getFetch,
getFetchWithCount,
postFectch,
putFectch,
} from "@/components/lib/api";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import type { SelectDataType } from "@common/business_components/ui";
import toast from "react-hot-toast";
import { z } from "zod";
import { buildingToExcelType } from "./excel";
import {
rainChkgdnHeadListExport,
rainChkZod,
rainHeadNmExport,
rainMainHeadNmExport,
rainRuseHeadNmExport,
rainSplHeadNmExport,
ruseHeadListExport,
splHeadListExport,
type chHeadType,
} from "./headList";
import type { TableUpperProps } from "@/app/store/common/type/header";
export const rainPageSize = 5;
export const commonCd = "41";
export type rainChkZodType = z.infer<typeof rainChkZod>;
const fcltDiv = [
@@ -29,30 +33,41 @@ const fcltDiv = [
{ val: "RNB", text: "빗물이용시설" },
];
const rainHeadNm = rainHeadNmExport;
const rainRuseHeadNm = rainRuseHeadNmExport;
const rainSplHeadNm = rainSplHeadNmExport;
const rainChkgdnHeadList = rainChkgdnHeadListExport;
const ruseHeadList = ruseHeadListExport;
const splHeadList = splHeadListExport;
const rainMainHeadNm = rainMainHeadNmExport
const rainChk: rainChkZodType[] = [];
export type rainChkgdnState = {
rainChkgdn: {
rainChkgdnHeadList: TableUpperProps[];
rainSplHeadNm: chHeadType[]
rainRuseHeadNm: chHeadType[]
rainChk: rainChkZodType[];
rainHeadNm: chHeadType[];
ruseHeadList: TableUpperProps[];
splHeadList: TableUpperProps[];
isInit: boolean;
isModalClose: boolean;
fcltDiv: SelectDataType[];
rainMainHeadNm: chHeadType[]
excelList: [];
isSttn: boolean;
rainChkCount: number;
isLoading: boolean;
};
};
type rainChkgdnActions = {
rainChkInit: (sggCd: string, searchTxt: string) => void;
rainChkInit: (
pageNo: number,
cnt: number,
sggCd: string,
searchTxt: string
) => void;
rainChkDetilUpdate(rainChk: rainChkZodType[]): Promise<void>;
rainChkGuidUpdate(rainChk: rainChkZodType): Promise<void>;
rainChkExcelDownload(
@@ -61,7 +76,14 @@ type rainChkgdnActions = {
): Promise<void>;
rainChkExcelDataUPload(excelData: rainChkZodType[]): Promise<void>;
rainChkDelete(chkgdnPks: number[]): Promise<void>;
rainChkHeadChange: (rainChkgdn: rainChkgdnState, isEdit :boolean) => void;
rainChkHeadChange: (rainChkgdn: rainChkgdnState, isEdit: boolean) => void;
rainChkNmSearch: (
pageNo: number,
cnt: number,
year: string,
sggCd: string,
txt: string
) => void;
};
export type rainChkgdnType = rainChkgdnState & rainChkgdnActions;
@@ -69,7 +91,9 @@ export type rainChkgdnType = rainChkgdnState & rainChkgdnActions;
export const rainChkgdnStateExport: rainChkgdnState = {
rainChkgdn: {
rainChkgdnHeadList: rainChkgdnHeadList,
rainHeadNm: rainHeadNm,
rainRuseHeadNm: rainRuseHeadNm,
rainSplHeadNm: rainSplHeadNm,
rainMainHeadNm: rainMainHeadNm,
rainChk: rainChk,
splHeadList: splHeadList,
ruseHeadList: ruseHeadList,
@@ -78,6 +102,8 @@ export const rainChkgdnStateExport: rainChkgdnState = {
excelList: [],
fcltDiv: fcltDiv,
isSttn: false,
rainChkCount: 1,
isLoading: false,
},
};
@@ -98,11 +124,23 @@ const makeMdfrChk = (data: rainChkZodType[], type: string, toDo: string) => {
return chData;
};
const initFetch = async (txt: string | undefined, sgg: string | undefined) => {
const rainRes = await getFetch(
const initFetch = async (
pageNo: number,
cnt: number,
year: string | undefined,
sgg: string | undefined
) => {
const currentYear = new Date().getFullYear().toString();
const rainRes = await getFetchWithCount(
"wr",
`/reuseChk/rainy` + (txt ? `/${txt}` : "2024") + (sgg ? `/${sgg}` : "/0")
`/reuseChk/rainy` +
(pageNo ? `/${pageNo}` : "/1") +
(cnt ? `/${cnt}` : "/10") +
(year ? `/${year}` : `/${currentYear}`) +
(sgg ? `/${sgg}` : "/0")
);
return rainRes;
};
@@ -128,39 +166,89 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
}));
},
rainChkExcelDataUPload: async (excelData: rainChkZodType[]) => {
set((state: rainChkgdnType) => (
{ rainChkgdn: { ...state.rainChkgdn, isLoading: true } }));
try {
const res = await postFectch("/reuseChk/rainy", excelData);
if (res.ok) {
ToastFormUi({
body: "지도점검 등록이 완료되었습니다. ",
title: "지도점검 등록",
});
if (res.status === 200) {
const currentYear = new Date().getFullYear().toString();
const rainChkRes = await initFetch(
1,
rainPageSize,
currentYear,
commonCd
);
set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
...state.rainChkgdn,
rainChk: rainChkRes.data,
rainChkCount: rainChkRes.count,
},
}));
toast.success("지도점검 등록이 완료되었습니다.");
} else {
toast.error("지도점검 등록이 실패되었습니다.");
}
const rainChkRes = await initFetch("2024", "0");
return set((state: rainChkgdnState) => ({
} catch (error) {
console.log(error);
toast.error("지도점검 등록이 실패되었습니다.");
} finally {
set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } }));
}
},
rainChkInit: async (
pageNo: number,
cnt: number,
year: string,
sggCd: string
) => {
// set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: true } }));
try {
const rainChkRes = await initFetch(pageNo, cnt, year, sggCd);
set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
...state.rainChkgdn,
rainChk: rainChkRes,
rainChk: rainChkRes.data,
rainChkCount: rainChkRes.count,
isInit: false, // 무한 루프 방지를 위해 isInit을 true로 설정
},
}));
} catch (error) {
console.log(error);
ToastFormUi({
body: "지도점검 등록이 실패되었습니다.",
title: "지도점검 등록",
});
// 에러가 발생하더라도 무한 루프에 빠지지 않도록 isInit을 true로 설정합니다.
// set((state: rainChkgdnType) => ({
// rainChkgdn: { ...state.rainChkgdn, isInit: true },
// }));
} finally {
// set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } }));
}
},
rainChkInit: async (searchTxt: string, sggCd: string) => {
rainChkNmSearch: async (
pageNo: number,
cnt: number,
year: string,
sggCd: string,
txt: string
) => {
try {
const rainChkRes = await initFetch(searchTxt, sggCd);
const currentYear = new Date().getFullYear();
const rainChkRes = await getFetchWithCount(
"wr",
`/reuseChk/rainy` +
(pageNo ? `/${pageNo}` : "/1") +
(cnt ? `/${cnt}` : "/10") +
(year ? `/${year}` : `/${currentYear}`) +
(sggCd ? `/${sggCd}` : "/0") +
(txt ? `/${txt}` : "")
);
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
...state.rainChkgdn,
rainChk: rainChkRes,
rainChk: rainChkRes.data,
rainChkCount: rainChkRes.count,
},
}));
} catch (error) {
@@ -171,46 +259,62 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
try {
const chData = await makeMdfrChk(rainChk, "rain", "rainDetil");
const res = await postFectch("/reuseChk/rainy", chData);
if (res.ok) {
ToastFormUi({
body: "지도점검 보급수 및 밋물이용량 수정이 완료되었습니다. ",
title: "지도점검 수정",
});
const res = await putFectch("/reuseChk/rainy/list", chData);
if (res) {
toast.success(
"지도점검 대체수 및 밋물이용량 수정이 완료되었습니다. "
);
const currentYear = new Date().getFullYear().toString();
const rainChkRes = await initFetch(
1,
rainPageSize,
currentYear,
"41"
);
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
...state.rainChkgdn,
rainChk: rainChkRes.data,
rainChkCount: rainChkRes.count,
},
}));
} else {
toast.error("지도점검 대체수 및 밋물이용량 수정 오류입니다.");
}
const rainChkRes = await initFetch("2024", "0");
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
...state.rainChkgdn,
rainChk: rainChkRes,
},
}));
} catch (error) {
console.log(error);
alert("수정에 실패하였습니다.");
toast.error("지도점검 대체수 및 밋물이용량 수정 오류입니다.");
}
},
rainChkGuidUpdate: async (rainChk: rainChkZodType) => {
try {
//const chData = await makeMdfrChk(rainChk, "guideDetails", "rainDetil");
const res = await putFectch("/reuseChk/rainy", rainChk);
if (res.ok) {
ToastFormUi({
body: "지도점검 수질 및 과태료 수정이 완료되었습니다. ",
title: "지도점검 수정",
});
if (res.status === 200) {
toast.success("지도점검 수질 및 과태료 수정이 완료되었습니다. ");
const currentYear = new Date().getFullYear().toString();
const rainChkRes = await initFetch(
1,
rainPageSize,
currentYear,
"41"
);
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
...state.rainChkgdn,
rainChk: rainChkRes.data,
rainChkCount: rainChkRes.count,
},
}));
} else {
toast.error("지도점검 수질 및 과태료 수정 오류입니다.");
}
const rainChkRes = await initFetch("2024", "0");
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
...state.rainChkgdn,
rainChk: rainChkRes,
},
}));
} catch (error) {
console.log(error);
toast.error("지도점검 수질 및 과태료 수정 오류입니다.");
}
},
rainChkDelete: async (chkgdnPks: number[]) => {
@@ -218,40 +322,45 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
const res = await deleteFectch(`/reuseChk/rainy`, chkgdnPks);
if (res) {
ToastFormUi({
body: "삭제가 완료되었습니다. ",
title: "지도점검 삭제",
});
toast.success("삭제가 완료되었습니다. ");
const currentYear = new Date().getFullYear().toString();
const rainChkRes = await initFetch(
1,
rainPageSize,
currentYear,
"41"
);
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
...state.rainChkgdn,
rainChk: rainChkRes.data,
rainChkCount: rainChkRes.count,
},
}));
} else {
toast.error("삭제시 오류가 발생하였습니다.. ");
}
const rainChkRes = await initFetch("2024", "0");
return set((state: rainChkgdnState) => ({
...state,
rainChkgdn: {
...state.rainChkgdn,
rainChk: rainChkRes,
},
}));
} catch (error) {
ToastFormUi({ body: "삭제를 실패했습니다.. ", title: "지도점검 삭제" });
toast.error("삭제시 오류가 발생하였습니다.. ");
}
},
rainChkHeadChange: async (rainChkgdn: rainChkgdnState , isEdit :boolean) => {
rainChkHeadChange: async (rainChkgdn: rainChkgdnState, isEdit: boolean) => {
try {
const rainChkgdnHeadList = rainChkgdn.rainChkgdn.rainChkgdnHeadList;
const splHeadList = rainChkgdn.rainChkgdn.splHeadList;
const ruseHeadList = rainChkgdn.rainChkgdn.ruseHeadList;
rainChkgdnHeadList.forEach(( head) => {
if(head.edit!== undefined)
head.edit=isEdit;
rainChkgdnHeadList.forEach((head) => {
if (head.edit !== undefined) head.edit = isEdit;
});
splHeadList.forEach((head) => {
if(head.edit!== undefined)
head.edit=isEdit;
if (head.edit !== undefined) head.edit = isEdit;
});
ruseHeadList.forEach((head) => {
if(head.edit!== undefined)
head.edit=isEdit;
if (head.edit !== undefined) head.edit = isEdit;
});
return set((state: rainChkgdnState) => ({
@@ -259,8 +368,8 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = (
rainChkgdn: {
...state.rainChkgdn,
rainChkgdnHeadList: rainChkgdnHeadList,
splHeadList:splHeadList,
ruseHeadList:ruseHeadList
splHeadList: splHeadList,
ruseHeadList: ruseHeadList,
},
}));
} catch (error) {
@@ -1,6 +1,6 @@
import type { TableUpperProps } from "@/app/store/common/type/header";
import { z } from "zod";
import { guideDetail, type chHeadType } from "../rainChkgdn/headList";
import type { TableUpperProps } from "@/app/store/common/type/header";
export type polutionType = {
chkgdnDtlFk: number;
@@ -109,6 +109,24 @@ export const rswtChkgdZod = z.object({
rswtrPollutions: z.array(rswtrPolutionsZode),
});
export const rswtRuseHeadNmExport: chHeadType[] = [
{ headNm: "useQuilty", text: "이용수질(필요시)", length: 3 },
{ headNm: "feeHist", text: "과태료 부과내역", length: 3 },
{ headNm: "ruseWater", text: "연간중수 이용량(m³)", length: 13 },
{ headNm: "detailQulity", text: "분기", length: 11 },
];
export const rswtSplHeadNmExport: chHeadType[] = [
{ headNm: "useQuilty", text: "이용수질(필요시)", length: 3 },
{ headNm: "feeHist", text: "과태료 부과내역", length: 3 },
{ headNm: "detailQulity", text: "분기", length: 11 },
{ headNm: "splWater", text: "연간대체수 이용량(m³)", length: 13 },
];
export const rswtHeadNmExport: chHeadType[] = [
{ headNm: "useQuilty", text: "연간 평균 이용수질", length: 10 },
{ headNm: "feeHist", text: "과태료 부과내역", length: 3 },
@@ -126,7 +144,7 @@ export const rswtChkHeadListExport: TableUpperProps[] = [
{ id: "spwtPositn", title: "채수지점" ,mergeCell: true},
{ id: "chckYmd", title: "점검일" ,mergeCell: true},
{ id: "utztnUsgCd", title: "이용용도" },
{ id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)" ,type: "number"},
{ id: "splyUswtrUtztnQnt", title: "연간대체수이용량(m³)" ,type: "number"},
{ id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)",type: "number"},
{
@@ -1,18 +1,24 @@
import { buildingType } from "@/app/store/common/code";
import type { TableUpperProps } from "@/app/store/common/type/header";
import {
TEST_UUID,
} from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import type {
SelectDataType,
SheetParameter,
} from "@common/business_components";
import {
deleteFectch,
fetchType,
getFetch,
getFetchWithCount,
postFectch,
putFectch,
} from "@/components/lib/api";
import { buildingType } from "@/app/store/common/code";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import type {
SelectDataType,
SheetParameter,
} from "@common/business_components";
import toast from "react-hot-toast";
import { z } from "zod";
import { type chHeadType } from "../rainChkgdn/headList";
import { rswtMakeExcelSample } from "./excel";
import {
rswtChkgdZod,
@@ -20,15 +26,19 @@ import {
rswtDetailHeadListExport,
rswtHeadNmExport,
rswtrDetailsZod,
rswtRuseHeadNmExport,
rswtSplHeadNmExport,
} from "./headList";
import { z } from "zod";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import { type chHeadType } from "../rainChkgdn/headList";
import type { TableUpperProps } from "@/app/store/common/type/header";
export const rswtPageSize = 5;
const rswtChkHeadList = rswtChkHeadListExport;
const rswtDetailHeadList = rswtDetailHeadListExport;
const rswtHeadNm = rswtHeadNmExport;
const rswtRuseHeadNm =rswtRuseHeadNmExport
const rswtSplHeadNm =rswtSplHeadNmExport
export type rswtChkZodType = z.infer<typeof rswtChkgdZod>;
export type rswtrChkDetailsZodType = z.infer<typeof rswtrDetailsZod>;
export type rswtChkgdnState = {
@@ -40,13 +50,29 @@ export type rswtChkgdnState = {
excelList: SheetParameter[];
rswtChkRes: rswtChkZodType[];
rswtChkPolGuide: rswtChkZodType[];
rswtSplHeadNm:chHeadType[]
rswtRuseHeadNm:chHeadType[]
isInit: boolean;
isSttn:boolean
isSttn: boolean;
isLoading:boolean;
rswtChkCount: number;
};
};
type rswtChkgdnActions = {
rswtChkgdnInit: (year: string, sggCd: string) => void;
rswtChkgdnInit: (
pageNo: number,
cnt: number,
year: string,
sggCd: string
) => void;
rswtChkNmSearch: (
pageNo: number,
cnt: number,
year: string,
sggCd: string,
txt: string
) => void;
rswtrChkExcelDownload(
sidoCode: SelectDataType[],
sggCode: SelectDataType[]
@@ -57,7 +83,7 @@ type rswtChkgdnActions = {
rswtChkPolAndGuide: (chkGdnPk: number) => void;
rswtChkPolInit: () => void;
rwstPolAndGuideUpdate: (rswtChk: rswtChkZodType[]) => void;
rswtChkHeadChange: (rswtChkgdn: rswtChkgdnState, isEdit: boolean) => void;
rswtChkHeadChange: (rswtChkgdn: rswtChkgdnState, isEdit: boolean) => void;
};
export type rswtChkgdnType = rswtChkgdnState & rswtChkgdnActions;
@@ -67,24 +93,41 @@ export const rswtChkgdnStateExport: rswtChkgdnState = {
rswtChkHeadList: rswtChkHeadList,
rswtDetailHeadList: rswtDetailHeadList,
rswtHeadNm: rswtHeadNm,
excelList: [{
data: [[]], headerNum: 0, headerRange: "A1:V3",
rswtRuseHeadNm:rswtRuseHeadNm,
rswtSplHeadNm:rswtSplHeadNm,
excelList: [
{
data: [[]],
headerNum: 0,
headerRange: "A1:V3",
sheetName: "",
startCol: '1'
}],
startCol: "1",
},
],
rswtChkRes: [],
rswtChkPolGuide: [],
isInit: false,
isSttn:false,
isSttn: false,
isLoading:false,
rswtChkCount: 1,
},
};
const initFetch = async (year: string | undefined, sgg: string | undefined) => {
const rswtChkRes = await getFetch(
const initFetch = async (
pageNo: number,
cnt: number,
year: string | undefined,
sgg: string | undefined
) => {
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await getFetchWithCount(
"wr",
`/reuseChk/reuseWater/` +
(year ? `${year}` : "2024") +
(sgg ? `/${sgg}` : "/0")
`/reuseChk/reuseWater` +
(pageNo ? `/${pageNo}` : "/1") +
(cnt ? `/${cnt}` : "/20") +
(year ? `/${year}` : `/${currentYear}`) +
(sgg ? `/${sgg}` : "/0")
);
return rswtChkRes;
};
@@ -121,12 +164,12 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
excelList.push({
data: exceldata.guideDetail,
headerNum: 0,
headerRange: "A1:AI1",
headerRange: "A1:AJ1",
});
excelList.push({
data: exceldata.polutions,
headerNum: 0,
headerRange: "A1:AZ1",
headerRange: "A1:BB1",
});
return set((state: rswtChkgdnState) => ({
@@ -136,16 +179,63 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
},
}));
},
rswtChkgdnInit: async (year: string, sggCd: string) => {
rswtChkgdnInit: async (
pageNo: number,
cnt: number,
year: string,
sggCd: string
) => {
// set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } }));
try {
const rswtChkRes = await initFetch("2024", sggCd);
const rswtChkRes = await initFetch(pageNo, cnt, year, sggCd);
console.log("rswtChkRes", rswtChkRes)
set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
...state.rswtChkgdn,
rswtChkRes: rswtChkRes.data,
rswtChkCount: rswtChkRes.count,
isInit: true, // 무한 루프를 방지하기 위해 isInit을 true로 설정
},
}));
} catch (error) {
// console.log(error);
// set((state: rswtChkgdnType) => ({
// rswtChkgdn: { ...state.rswtChkgdn, isInit: true },
// }));
} finally {
// set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: false } }));
}
},
rswtChkNmSearch: async (
pageNo: number,
cnt: number,
year: string,
sggCd: string,
txt: string
) => {
try {
console.log("여기")
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await getFetchWithCount(
"wr",
`/reuseChk/reuseWater` +
(pageNo ? `/${pageNo}` : "/1") +
(cnt ? `/${cnt}` : "/20") +
(year ? `/${year}` : `/${currentYear}`) +
(sggCd ? `/${sggCd}` : "/0") +
(txt ? `/${txt}` : "")
);
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
...state.rswtChkgdn,
rswtChkRes: rswtChkRes,
isInit: false,
rswtChkRes: rswtChkRes.data,
rswtChkCount: rswtChkRes.count,
// isInit: false,
},
}));
} catch (error) {
@@ -154,6 +244,8 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
},
rswtChkExceDataUpload: async (excelData: rswtChkZodType[]) => {
try {
set((state: rswtChkgdnType) => (
{ rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } }));
const res = await postFectch("/reuseChk/reuseWater", excelData);
if (res.ok) {
ToastFormUi({
@@ -161,62 +253,67 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
title: "지도점검 등록",
});
}
// const rainChkRes = await initFetch('2024', '0');
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
...state.rswtChkgdn,
rswtChkRes: rswtChkRes.data,
rswtChkCount: rswtChkRes.count,
},
}));
} catch (error) {
console.log(error);
}finally{
set((state: rswtChkgdnType) => (
{ rswtChkgdn: { ...state.rswtChkgdn, isLoading: false } }));
}
},
rswtChkDetilsUpdate: async (rswtChk: rswtrChkDetailsZodType[]) => {
try {
const chData = await makeMdfrChk(rswtChk);
const res = await putFectch("/reuseChk/reuseWater", chData);
if (res.ok) {
ToastFormUi({
body: "지도점검 과태료 수정 수정이 완료되었습니다. ",
title: "지도점검 수정",
});
toast.success("지도점검 과태료 수정이 완료되었습니다. ",)
}
const rswtChkRes = await initFetch("2024", "0");
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
...state.rswtChkgdn,
rswtChkRes: rswtChkRes,
rswtChkRes: rswtChkRes.data,
rswtChkCount: rswtChkRes.count,
},
}));
} catch (error) {
console.log(error);
alert("수정에 실패하였습니다.");
toast.error("지도점검 과태료 수정 오류입니다. ",)
}
},
rswtChkDelete: async (chkgdnPks: number[]) => {
try {
const res = await deleteFectch(`/reuseChk/reuseWater`, chkgdnPks);
if (res.ok) {
ToastFormUi({
body: "삭제가 완료되었습니다. ",
title: "지도점검 삭제",
});
if (res) {
toast.success("삭제가 완료되었습니다.")
}
const rswtChkRes = await initFetch("2024", "0");
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
...state.rswtChkgdn,
rswtChkRes: rswtChkRes,
rswtChkRes: rswtChkRes.data,
rswtChkCount: rswtChkRes.count,
},
}));
} catch (error) {
ToastFormUi({ body: "삭제를 실패했습니다.. ", title: "지도점검 삭제" });
toast.error("삭제 오류입니다.")
}
},
rswtChkPolAndGuide: async (chkGdnPk: number) => {
@@ -233,7 +330,11 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
rswtChkPolGuide: rswtChkPolGuide,
},
}));
} catch (error) {}
} catch (error) {
}
},
rswtChkPolInit: () => {
try {
@@ -254,12 +355,10 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
chData[0]
);
if (res.ok) {
ToastFormUi({
body: "지도점검 연간 수질 및 보급수/중수도 수정이 완료되었습니다. ",
title: "지도점검 수정",
});
toast.success("지도점검 연간 수질 및 대체수/중수도 수정이 완료되었습니다.")
}
const rswtChkRes = await initFetch("2024", "0");
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0");
return set((state: rswtChkgdnState) => ({
...state,
rswtChkgdn: {
@@ -269,10 +368,9 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = (
}));
} catch (error) {
console.log(error);
ToastFormUi({
body: "지도점검 연간 수질 및 보급수/중수도 수정이 실패하였습니다.. ",
title: "지도점검 수정",
});
toast.error("지도점검 연간 수질 및 대체수/중수도 수정 오류입니다.")
}
},
rswtChkHeadChange: async (rswtChkgdn: rswtChkgdnState, isEdit: boolean) => {
@@ -4,7 +4,7 @@ import type { TableUpperProps } from "../../common/type/header";
export const pbnPrfRegExcelZod = z.object({
ym: z.string().default(""),
bscFctNm: z.string().default(""),
pbnstRuseFcltFk:z.string(),
pbnstRuseFcltFk: z.string(),
pbnstRuseFcltNm: z.string().default(""),
file: z.instanceof(File).optional(),
});
@@ -18,20 +18,21 @@ export const pbnstPrfmncHeadListExport: TableUpperProps[] = [
];
export const monthHeadListExport: TableUpperProps[] = [
{ id: "구분", title: "구분", mergeCell: true },
{ id: "용수명", title: "용수명" },
{ id: "total", title: "계" },
{ id: "1월", title: "1월" ,edit: true },
{ id: "2월", title: "2월" ,edit: true },
{ id: "3월", title: "3월" ,edit: true },
{ id: "4월", title: "4월" ,edit: true },
{ id: "5월", title: "5월" ,edit: true },
{ id: "6월", title: "6월" ,edit: true },
{ id: "7월", title: "7월" ,edit: true },
{ id: "8월", title: "8월" ,edit: true },
{ id: "9월", title: "9월" ,edit: true },
{ id: "10월", title: "10월" ,edit: true },
{ id: "11월", title: "11월" ,edit: true },
{ id: "12월", title: "12월" ,edit: true },
{ id: "1월", title: "1월", edit: true },
{ id: "2월", title: "2월", edit: true },
{ id: "3월", title: "3월", edit: true },
{ id: "4월", title: "4월", edit: true },
{ id: "5월", title: "5월", edit: true },
{ id: "6월", title: "6월", edit: true },
{ id: "7월", title: "7월", edit: true },
{ id: "8월", title: "8월", edit: true },
{ id: "9월", title: "9월", edit: true },
{ id: "10월", title: "10월", edit: true },
{ id: "11월", title: "11월", edit: true },
{ id: "12월", title: "12월", edit: true },
];
@@ -16,6 +16,7 @@ export type pbnstPrfmncState = {
monthHeadList: TableUpperProps[];
prfmncRes: FcPrfType[];
excelList: Number[];
pageKey:number
};
};
@@ -26,21 +27,22 @@ export const pbnstPrfmncStateExport: pbnstPrfmncState = {
monthHeadList: monthHeadList,
prfmncRes: [],
excelList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
pageKey:1
},
};
export type pbnstPrfmncType = pbnstPrfmncState & pbnstPrfmncActions;
type pbnstPrfmncActions = {
prfmncHasuInit: (page: string, sggOrCity: string) => void;
prfmnRegData: (data: PrfType) => void;
prfmncHasuInit: (page: number, sggOrCity: string) => void;
prfmnRegData: (page:number,sggOrCity:string, data: PrfType) => void;
};
const checkWrRegList = async () => {
const wrRegList = await getFetch("wr", `/reuseFc/pubDetailList`);
return wrRegList;
};
const hasuListFetch = async (page: string, sggOrCity: string) => {
const hasuListFetch = async (page: number, sggOrCity: string) => {
const checkWrRegListRes = await checkWrRegList();
const rainRes = await postFectch(
`/prcsFclt/ruseInfo/${page ? page : 1}/10/A/${sggOrCity ? sggOrCity : "11"}/0/1/SW`,
@@ -74,10 +76,9 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = (
set: any
) => {
return {
prfmncHasuInit: async (page: string, sggOrCity: string) => {
prfmncHasuInit: async (page: number, sggOrCity: string) => {
try {
const prfmncRes = await hasuListFetch(page, sggOrCity);
console.log("prfmncRes", prfmncRes);
return set((state: pbnstPrfmncState) => ({
...state,
prfmnc: {
@@ -90,7 +91,7 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = (
console.log(errorHasu);
}
},
prfmnRegData: async (data: PrfType) => {
prfmnRegData: async (page:number,sggOrCity:string,data: PrfType) => {
try {
const ressponse = await postFectch(
"/reuseFc/pubFcPrf",
@@ -101,6 +102,17 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = (
if (ressponse.status === 200) {
toast.success("하수재이용 시설 실적 등록이 완료되었습니다.");
const prfmncRes = await hasuListFetch(page, sggOrCity);
return set((state: pbnstPrfmncState) => ({
...state,
prfmnc: {
...state.prfmnc,
prfmncRes: prfmncRes,
isInit: false,
pageKey: state.prfmnc.pageKey + 1
},
}));
}
} catch (e) {
console.log(e);
@@ -11,7 +11,7 @@ export const rainChkgdnHeadSttnExport: TableUpperProps[] = [
{ id: "dtfcltCpct", title: "빗물저류조용량(m³)", mergeCell: true },
{ id: "chckYmd", title: "점검일", mergeCell: true },
{ id: "utztnUsgCd", title: "이용용도" },
{ id: "totalSplyUswtrUtztnQnt", title: "연간보급수이용량(m³)" },
{ id: "totalSplyUswtrUtztnQnt", title: "연간대체수이용량(m³)" },
{ id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)" },
{
id: "ph",
@@ -1,11 +1,10 @@
import type { TableUpperProps } from "@/app/store/common/type/header";
import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import { type TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { getFetch } from "@/components/lib/api";
import { ToastFormUi } from "@/components/ui/toast/toastFromUi";
import { rainMainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList";
import { getFetchWithCount } from "@/components/lib/api";
import type { SelectDataType } from "@common/business_components/ui";
import { rainChkgdnHeadSttnExport, ruseSttnHeadListExport, splSttnHeadListExport } from "./headList";
import { rainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList";
import { buildingExcelRainSttn } from "./excel";
import { rainChkgdnHeadSttnExport, ruseSttnHeadListExport, splSttnHeadListExport } from "./headList";
const fcltDiv = [
{ val: "RST", text: "중수도시설" },
@@ -16,29 +15,28 @@ const fcltDiv = [
const rainChkgdnHeadList = rainChkgdnHeadSttnExport;
const ruseHeadList = ruseSttnHeadListExport;
const splHeadList = splSttnHeadListExport;
const rainHeadNm = rainHeadNmExport
const rainChk: rainChkZodType[] = [];
const rainHeadNm = rainMainHeadNmExport
export type rainChkSttnState = {
rainChkSttn: {
rainChkRes: rainChkZodType[];
fcltDiv: SelectDataType[];
rainChkgdnHeadList: TableUpperProps[];
rainHeadNm:chHeadType[];
rainHeadNm: chHeadType[];
ruseHeadList: TableUpperProps[];
splHeadList: TableUpperProps[];
sttnExcelList:[]
sttnExcelList: []
isInit: boolean;
isSttn:boolean;
isSttn: boolean;
rainChkCount: number;
};
};
type rainChkSttnActions = {
rainChkSttnInit: ( sggCd: string) => void;
rainChkSttnSearch: (searchTxt: string, sggCd: string) => void;
isRainSttn:(isSttn:boolean) => void;
getRainSttnExcel:(excelList:rainChkZodType[]) => void;
rainChkSttnInit: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void;
isRainSttn: (isSttn: boolean) => void;
getRainSttnExcel: (excelList: rainChkZodType[]) => void;
rainChkSttnNmSearch: (pageNo: number, cnt: number, year: string, sggCd: string, txt: string) => void
};
export type rainChkSttnType = rainChkSttnState & rainChkSttnActions;
@@ -50,17 +48,23 @@ export const rainChkSttnStateExport: rainChkSttnState = {
rainChkgdnHeadList: rainChkgdnHeadList,
ruseHeadList: ruseHeadList,
splHeadList: splHeadList,
rainHeadNm:rainHeadNm,
rainHeadNm: rainHeadNm,
isInit: false,
sttnExcelList:[],
isSttn:false,
sttnExcelList: [],
isSttn: false,
rainChkCount: 1,
},
};
const initFetch = async (txt: string | undefined, sgg: string | undefined) => {
const rainRes = await getFetch(
const initFetch = async (pageNo: number, cnt: number, txt: string | undefined, sgg: string | undefined) => {
const currentYear = new Date().getFullYear().toString();
const rainRes = await getFetchWithCount(
"wr",
`/reuseChk/rainy/` + (txt ? `${txt}` : "2024") + (sgg ? `/${sgg}` : "/0")
`/reuseChk/rainy` +
(pageNo ? `/${pageNo}` : "/1") +
(cnt ? `/${cnt}` : "/5") +
(txt ? `/${txt}` : `/${currentYear}`) +
(sgg ? `/${sgg}` : "/0")
);
return rainRes;
};
@@ -68,48 +72,66 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = (
set: any
) => {
return {
rainChkSttnInit: async ( sggCd: string) => {
rainChkSttnInit: async (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => {
try {
const rainChkRes = await initFetch(undefined, sggCd);
const rainChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd);
return set((state: rainChkSttnState) => ({
...state,
rainChkSttn: {
...state.rainChkSttn,
rainChkRes:rainChkRes
rainChkRes: rainChkRes.data,
rainChkCount: rainChkRes.count,
isSttn: true
},
}));
} catch (error) {
console.log(error);
}
},
rainChkSttnSearch: async (searchTxt: string, sggCd: string) => {
rainChkSttnNmSearch: async (
pageNo: number,
cnt: number,
year: string,
sggCd: string,
txt: string
) => {
try {
const rainChkRes = await initFetch(searchTxt, sggCd);
const currentYear = new Date().getFullYear();
const rainChkRes = await getFetchWithCount(
"wr",
`/reuseChk/rainy` +
(pageNo ? `/${pageNo}` : "/1") +
(cnt ? `/${cnt}` : "/5") +
(year ? `/${year}` : `/${currentYear}`) +
(sggCd ? `/${sggCd}` : "/0") +
(txt ? `/${txt}` : "")
);
return set((state: rainChkSttnState) => ({
...state,
rainChkSttn: {
...state.rainChkSttn,
rainChkRes:rainChkRes
rainChkRes: rainChkRes.data,
rainChkCount: rainChkRes.count,
},
}));
} catch (error) {
console.log(error);
}
},isRainSttn :(isSttn:boolean) =>{
},
isRainSttn: (isSttn: boolean) => {
try {
return set((state: rainChkSttnState) => ({
...state,
rainChkSttn: {
...state.rainChkSttn,
isSttn:isSttn
isSttn: isSttn
},
}));
} catch (error) {
console.log(error);
}
},getRainSttnExcel: async (excelList:rainChkZodType[]) => {
}, getRainSttnExcel: async (excelList: rainChkZodType[]) => {
try {
const excelData = buildingExcelRainSttn(excelList)
@@ -118,7 +140,7 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = (
...state,
rainChkSttn: {
...state.rainChkSttn,
sttnExcelList:excelData
sttnExcelList: excelData
},
}));
} catch (error) {
@@ -19,6 +19,7 @@ export const buildingExcelRswtSttn = (excelList: any[]) => {
String(item.trbt) ?? "",
String(item.bod) ?? "",
String(item.smll) ?? "",
String(item.crmty) ?? "",
String(item.tn) ?? "",
String(item.tph) ?? "",
String(item.ph) ?? "",
@@ -1,15 +1,15 @@
import type { TableUpperProps } from "@/app/store/common/type/header";
export const rswtChkSttnHeadListExport: TableUpperProps[] = [
{ id: "order", title: "순번", rowSpan: true },
{ id: "cityText", title: "시도", rowSpan: true },
{ id: "sggText", title: "시군구", rowSpan: true },
{ id: "bdstNm", title: "시설명", rowSpan: true },
{ id: "operMngCd", title: "운영관리형태", rowSpan: true },
{ id: "anlOperCst", title: "연간운영비(원/년)", rowSpan: true },
{ id: "prcsCpct", title: "처리용량(m³)", rowSpan: true },
{ id: "spwtPositn", title: "채수지점", rowSpan: true },
{ id: "chckYmd", title: "점검일", rowSpan: true },
{ id: "order", title: "순번", mergeCell: true },
{ id: "cityText", title: "시도", mergeCell: true },
{ id: "sggText", title: "시군구", mergeCell: true },
{ id: "bdstNm", title: "시설명", mergeCell: true },
{ id: "operMngCd", title: "운영관리형태", mergeCell: true },
{ id: "anlOperCst", title: "연간운영비(원/년)", mergeCell: true },
{ id: "prcsCpct", title: "처리용량(m³)", mergeCell: true },
{ id: "spwtPositn", title: "채수지점", mergeCell: true },
{ id: "chckYmd", title: "점검일", mergeCell: true },
{ id: "utztnUsgCd", title: "이용용도" },
{ id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)", type: "number" },
{ id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)", type: "number" },
@@ -1,10 +1,10 @@
import { rswtHeadNmExport } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList";
import { rswtChkSttnHeadListExport, rswtDetailSttnHeadListExport } from "./headList";
import type { TableUpperProps } from "@/app/store/common/type/header";
import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList";
import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn";
import { getFetch } from "@/components/lib/api";
import { rswtHeadNmExport } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList";
import { getFetch, getFetchWithCount } from "@/components/lib/api";
import { buildingExcelRswtSttn } from "./excel";
import { rswtChkSttnHeadListExport, rswtDetailSttnHeadListExport } from "./headList";
const rswtChkHeadList = rswtChkSttnHeadListExport;
const rswtDetailHeadList = rswtDetailSttnHeadListExport;
@@ -20,14 +20,15 @@ export type rswtChkSttnState = {
rswtChkRes: rswtChkZodType[];
isInit: boolean;
isSttn: boolean;
rswtChkCount: number;
};
};
type rswtChkSttnActions = {
rswtChkSttnInit: ( sggCd: string) => void;
isRswtSttn: (isSttn:boolean) => void;
getRswtSttnExcel:(excelList:rswtChkZodType[]) => void;
rswtChkSttnInit: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void;
isRswtSttn: (isSttn: boolean) => void;
getRswtSttnExcel: (excelList: rswtChkZodType[]) => void;
rswtChkNmSttnSearch: (pageNo: number, cnt: number, year: string, sggCd: string, txt: string) => void;
};
export type rswtChkSttnType = rswtChkSttnState & rswtChkSttnActions;
@@ -40,68 +41,104 @@ export const rswtChkStateExport: rswtChkSttnState = {
sttnExcelList: [],
rswtChkRes: [],
isInit: false,
isSttn:false,
isSttn: false,
rswtChkCount: 1,
},
};
const initFetch = async (year: string | undefined, sgg: string | undefined) => {
const rswtChkRes = await getFetch(
const initFetch = async (pageNo: number, cnt: number, year: string | undefined, sgg: string | undefined) => {
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await getFetchWithCount(
"wr",
`/reuseChk/reuseWater/` +
(year ? `${year}` : "2024") +
(sgg ? `/${sgg}` : "/0")
`/reuseChk/reuseWater` +
(pageNo ? `/${pageNo}` : "/1") +
(cnt ? `/${cnt}` : "/20") +
(year ? `/${year}` : `/${currentYear}`) +
(sgg ? `/${sgg}` : "/0")
);
return rswtChkRes;
};
export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = (
set: any
) => {
return {
rswtChkSttnInit: async ( sggCd: string) => {
rswtChkSttnInit: async (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => {
try {
const rswtChkRes = await initFetch("2024", sggCd);
const rswtChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd);
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
rswtChkRes: rswtChkRes,
rswtChkRes: rswtChkRes.data,
rswtChkCount: rswtChkRes.count,
isInit: false,
},
}));
} catch (error) {
console.log(error);
}
},isRswtSttn :(isSttn:boolean) =>{
try {
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
isSttn:isSttn
},
}));
} catch (error) {
console.log(error);
}
},getRswtSttnExcel: async (excelList:rswtChkZodType []) => {
try {
const excelData = buildingExcelRswtSttn(excelList)
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
sttnExcelList:excelData
},
}));
} catch (error) {
console.log(error);
}
}
},
isRswtSttn: (isSttn: boolean) => {
try {
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
isSttn: isSttn
},
}));
} catch (error) {
console.log(error);
}
},
getRswtSttnExcel: async (excelList: rswtChkZodType[]) => {
try {
const excelData = buildingExcelRswtSttn(excelList)
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
sttnExcelList: excelData
},
}));
} catch (error) {
console.log(error);
}
},
rswtChkNmSttnSearch: async (
pageNo: number,
cnt: number,
year: string,
sggCd: string,
txt: string
) => {
try {
const currentYear = new Date().getFullYear().toString();
const rswtChkRes = await getFetchWithCount(
"wr",
`/reuseChk/reuseWater` +
(pageNo ? `/${pageNo}` : "/1") +
(cnt ? `/${cnt}` : "/20") +
(year ? `/${year}` : `/${currentYear}`) +
(sggCd ? `/${sggCd}` : "/0") +
(txt ? `/${txt}` : "")
);
return set((state: rswtChkSttnState) => ({
...state,
rswtChkSttn: {
...state.rswtChkSttn,
rswtChkRes: rswtChkRes.data,
rswtChkCount: rswtChkRes.count,
isInit: false,
},
}));
} catch (error) {
console.log(error);
}
},
};
};
@@ -148,7 +148,7 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions
let usageSum = 0;
let year = "";
const yearsData = []
res.data.forEach((item) => {
if (item.prfList.length === 0) {
const year = ""; // 연도 정보가 없으면 빈 문자열, 필요시 item에서 추출
@@ -161,26 +161,26 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions
item.prfList.forEach((acc) => {
year = acc.ym?.substring(0, 4) ?? "";
inUsageSum =
(acc.wshwtr ?? 0) +
(acc.coolUswtr ?? 0) +
(acc.cleanUswtr ?? 0) +
(acc.drwtUswtr ?? 0) +
(acc.dltnUswtr ?? 0) +
(acc.ithlRswtr ?? 0) +
(acc.ithlEtcUswtr ?? 0) +
(acc.cleanToilUswtr ?? 0) +
(acc.resn ?? 0);
(acc.wshwtr ?? 0) + //조경용수
(acc.coolUswtr ?? 0) + //냉각수
(acc.cleanUswtr ?? 0) + //청소수
(acc.drwtUswtr ?? 0) + //식수대
(acc.dltnUswtr ?? 0) + //희석용수
(acc.ithlRswtr ?? 0) + //중수도
(acc.ithlEtcUswtr ?? 0) //기타
outUsageSum =
(acc.lndwtr ?? 0) +
(acc.hydUswtr ?? 0) +
(acc.ugwtChr ?? 0) +
(acc.farmUswtr ?? 0) +
(acc.rvrKeepUswtr ?? 0) +
(acc.indstUswtr ?? 0);
(acc.cleanToilUswtr ?? 0) + //청소화장실용수
(acc.resn ?? 0)+ //세척살수용도
(acc.lndwtr ?? 0) + //조경용수
(acc.hydUswtr ?? 0) + //친수용수
(acc.ugwtChr ?? 0) + //지하수충전
(acc.farmUswtr ?? 0) + //농업용수
(acc.rvrKeepUswtr ?? 0) + //하천등유지용수
(acc.indstUswtr ?? 0); //공업용수
const yearData = yearsData.find(
(yearItem) =>
yearItem.year === year &&
yearItem.pbnstRuseFcltPk == acc.pbnstRuseFcltPk
yearItem.pbnstRuseFcltPk == acc.pbnstRuseFcltFk
);
if (yearData) {
@@ -79,8 +79,9 @@ const isRswtBdst = (data: any): data is customRswtBdst =>{
data.reuseBuilding=chData;
}
if(data?.chgBuilding.length>0){
const chRgtr = data.chgBuilding.map((item) =>({...item, rgtr:TEST_UUID, mdfr: item.mdfr ?? null }))
if('chgBuilding' in data && Array.isArray(data.chgBuilding) && data.chgBuilding.length > 0){
const chRgtr = data.chgBuilding.map((item) =>({...item, rgtr:TEST_UUID, mdfr: item.mdfr ?? null }))
data.chgBuilding =chRgtr;
}
return data;
@@ -127,7 +128,7 @@ const isRswtBdst = (data: any): data is customRswtBdst =>{
data.reuseBuilding=chData;
}
if(data?.chgBuilding.length>0){
if('chgBuilding' in data && Array.isArray(data.chgBuilding) && data.chgBuilding.length > 0){
const chMdfr = data.chgBuilding.map((item) =>({...item, mdfr:TEST_UUID, archChgArea: item.archChgArea ?? '' }))
data.chgBuilding =chMdfr;
}
@@ -0,0 +1,10 @@
import type { TableUpperProps } from "@/app/store/common/type/header";
export const htdTableHeadList: TableUpperProps[] = [
{ id: "cityText", title: "시도" },
{ id: "sggText", title: "시군구" },
{ id: "bdstNm", title: "발전소명" },
{ id: "daddr", title: "위치" },
{ id: "frstArchPrmsnDt", title: "준공년도" },
{ id: "archArea", title: "건축면적" },
];
@@ -1,9 +1,67 @@
import { getFetch, postFectch, putFectch } from "@/components/lib/api";
import { upHeadList, type chgBuilding} from "../rainReg";
import { upHeadList, type chgBuilding } from "../rainReg";
import { z } from "zod";
import { makeMdfr, makeRstr } from "../bdstReg";
import {
baseReuseBuildingSchema,
chgBuildingSchema,
htdflReuseBuildingSchema,
validationMessages,
} from "../rainReg/type"; // Import baseReuseBuildingSchema, chgBuildingSchema, validationMessages
import type { TableUpperProps } from "@/app/store/common/type/header";
import { htdTableHeadList } from "./headList";
import toast from "react-hot-toast";
const hdtflObjSchema = z.object({
htdflFcltPk: z.number().nullish(),
bdstRuseFcltFk: z.number().nullish(),
etgtType: z
.string()
.min(1, { message: validationMessages.required("에너지원 종류") }),
fclCpct: z
.number()
.positive({ message: validationMessages.required("시설 용량") }),
plwtYn: z.enum(["Y", "N"]).default("Y"),
orgwtWtritSpt: z
.string()
.min(1, { message: validationMessages.required("원수 취수 지점") }),
orgwtOcrnProcs: z
.string()
.min(1, { message: validationMessages.required("원수 발생 공정") }),
wtritEntrcTp: z
.number()
.max(100, { message: "온도는 100이상이 될 수 없습니다." })
.default(0), // Changed max to 100 based on message
amtgnr: z.number().default(0),
prcsProcsYn: z.enum(["Y", "N"]).default("Y"),
prcsFcltCpct: z
.number()
.positive({ message: validationMessages.required("처리시설 용량") }),
fmYn: z.string().default(""),
utztnCpct: z
.number()
.positive({ message: validationMessages.required("이용 용량") }),
utztnUsg: z
.string()
.min(1, { message: validationMessages.required("이용 용도") }),
grssClfg: z.number().default(0), // Measurements, 0 is fine as default
combResdch: z.number().default(0), // Measurements, 0 is fine as default
trbt: z.number().default(0), // Measurements, 0 is fine as default
bod: z.number().default(0), // Measurements, 0 is fine as default
smll: z.number().default(0), // Measurements, 0 is fine as default
crmty: z.number().default(0), // Measurements, 0 is fine as default
tn: z.number().default(0), // Measurements, 0 is fine as default
tph: z.number().default(0), // Measurements, 0 is fine as default
ph: z.number().default(0), // Measurements, 0 is fine as default
clIonQnt: z.number().default(0), // Measurements, 0 is fine as default
cdtnv: z.number().default(0), // Measurements, 0 is fine as default
emsnExitTp: z.number()
.max(100, { message: "온도는 100이상이 될 수 없습니다." })
.default(0), // Changed max to 100 based on message, // Measurements, 0 is fine as default
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
});
export const hdtflZod = z.object({
bdstInfoPk: z.number().nullish(),
@@ -14,86 +72,25 @@ export const hdtflZod = z.object({
daddr: z.string().default(" "),
bdstNm: z.string().default(" "),
frstArchPrmsnDt: z.date().default(() => new Date()),
archChgPrmsnDt: z.date().default(() => new Date()).nullish() ,
archArea: z.number().default(0),
archChgPrmsnDt: z
.date()
.default(() => new Date())
.nullish(),
archArea: z.string().default(''),
archChgArea: z.number().nullish().default(0),
rgtr:z.string().nullish().default(null),
mdfr:z.string().nullish().default(null),
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
reuseBuilding: z
.array(
z.object({
bdstRuseFcltPk: z.number().nullish(),
bdstInfoFk: z.number().nullish(),
instlCmptnDt: z.coerce.date().default(() => new Date()),
instlCst: z.number().nullish().default(0),
operMngCd: z.string().nullish().default(""),
anlOperCst: z.number().default(0),
instlCstSprtYn: z.enum(["Y", "N"]).nullish(),
instlCstSprtAmt: z.number().nullish().default(0),
grntchBdstCertNo: z.string().nullish().default(""),
grntchBdstCertGrd: z.string().nullish().default(""),
ruseFcltFlg: z.string().nullish().default(""),
bdstUsgCd: z.number().default(0),
lawTrgtYn: z.enum(["Y", "N"]).nullish(),
rgtr:z.string().nullish().default(null),
mdfr:z.string().nullish().default(null),
// htdflObj 매핑
htdflObj: z
.object({
htdflFcltPk: z.number().nullish(),
bdstRuseFcltFk: z.number().nullish(),
etgtType: z.string().default(""),
fclCpct: z.number().default(0),
plwtYn: z.enum(["Y", "N"]),
orgwtWtritSpt: z.number().default(0),
orgwtOcrnProcs: z.string().default(""),
wtritEntrcTp: z.number().max(4, "온도는 100이상이 될 수 없습니다.")
.default(0),
amtgnr: z.number().default(0),
prcsProcsYn: z.enum(["Y", "N"]),
prcsFcltCpct: z.number().default(0),
fmYn: z.string().default(""),
utztnCpct: z.number().default(0),
utztnUsg: z.string().default(""),
grssClfg: z.number().default(0),
combResdch: z.number().default(0),
trbt: z.number().default(0),
bod: z.number().default(0),
smll: z.number().default(0),
crmty: z.number().default(0),
tn: z.number().default(0),
tph: z.number().default(0),
ph: z.number().default(0),
clIonQnt: z.number().default(0),
cdtnv: z.number().default(0),
emsnExitTp: z.number().default(0),
rgtr:z.string().nullish().default(null),
mdfr:z.string().nullish().default(null),
})
.default({
plwtYn: "N",
prcsProcsYn: "N",
}),
})
)
.default([]),
chgBuilding: z
.array(
z.object({
bdstInfoFk: z.number().nullish(),
archChgPrmsnDt: z.coerce.date() ,
archChgArea: z.string().default("-").nullish(),
bdstChgNm: z.string().default(""),
rgtr:z.string().nullish().default(null),
mdfr:z.string().nullish().default(null),
htdflReuseBuildingSchema.extend({
htdflObj: hdtflObjSchema,
})
)
.default([]),
//온배수는 건축물은 등록 하는것이 다름
// chgBuilding: z.array(chgBuildingSchema).nullish(), // Use imported chgBuildingSchema
});
export type customHtdflBdst = z.infer<typeof hdtflZod>
export type customHtdflBdst = z.infer<typeof hdtflZod>;
export type htdflFacState = {
htdfl: {
isInit: boolean;
@@ -102,45 +99,46 @@ export type htdflFacState = {
};
};
type htdflFacSActions = {
initHtdfl: (code: string ) => void;
htdflBdstUpdate:(data: customHtdflBdst, code:string|number) => void;
htdflBdstReg:(data: customHtdflBdst, code:string|number) => void;
getHtdflBdstData:(pageNation:number, code:string|number) =>void;
initHtdflReg: (code: string) => void;
htdflNmSearch: (code: string | number, fcltNm:string) => void;
htdflBdstUpdate: (data: customHtdflBdst, code: string | number) => void;
htdflBdstReg: (data: customHtdflBdst, code: string | number) => void;
getHtdflBdstData: (pageNation: number, code: string | number) => void;
};
const htdflRes: customHtdflBdst[] = [];
export type htdflFacType = htdflFacState & htdflFacSActions;
export const htdflFacStateExport: htdflFacState = {
htdfl: {
isInit: false,
upHeadList: upHeadList,
upHeadList: htdTableHeadList,
htdflRes: htdflRes,
},
};
const initFetch = async (code: string) => {
const initFetch = async (code:string|number) => {
const rswtRes = await getFetch(
"wr",
`/facilityList/1/20/${code}`)
console.log(' initFetch code ', code)
let url = `/drain/facilityList/1/20`
url+=`/${code}`
const rswtRes = await getFetch("wr", url);
return rswtRes;
};
export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
set: any
) => {
return {
getHtdflBdstData: async (pageNation: number, code: string | number) => {
getHtdflBdstData: async (pageNation: number, code: string | number) => {
try {
const rswtRes = await getFetch(
"wr",
`/drain/facilityList/${pageNation}/20/${code}`
);
console.log(pageNation)
// 여기서 상태 업데이트
return set((state: htdflFacState) => ({
...state,
htdfl: {
@@ -153,11 +151,9 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
console.log(e);
}
},
initHtdfl: async (code: string ) => {
initHtdflReg: async (code: string) => {
try {
const htdflRes = await initFetch(code);
const htdflRes = await initFetch( String(code));
set((state: htdflFacState) => {
return {
...state,
@@ -172,15 +168,21 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
console.log(error);
}
},
htdflBdstUpdate: async (data: customHtdflBdst, code:string|number) => {
htdflBdstUpdate: async (data: customHtdflBdst, code: string | number) => {
try {
const chData=await makeMdfr(data)
const res = await putFectch("/drain/facility", data);
if (res.ok) {
alert(" 수정이 완료되었습니다.");
const chData = await makeMdfr(data);
const res = await putFectch("/drain/facility", chData);
if (res.status) {
toast.success("온배수 건축물 수정이 완료되었습니다.");
}
const htdflRes = await initFetch(code);
const htdflRes = await getFetch(
"wr",
`/drain/facilityList/1/20/${code}`
);
set((state: htdflFacState) => ({
...state,
htdfl: {
@@ -190,44 +192,20 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
},
}));
} catch (errorUpdate) {
alert(
"업데이트 오류입니다. " +
(errorUpdate instanceof Error ? errorUpdate.message : "")
);
toast.error("온배수 건축물 수정 오류입니다..");
}
},
htdflBdstReg: async (data: customHtdflBdst , code:string|number) => {
htdflBdstReg: async (data: customHtdflBdst, code: string | number) => {
try {
const chData=await makeRstr(data)
const res = await postFectch("/drain/facility", data);
if (res.ok) {
if(data.chgBuilding.length > 0){
await res
.json()
.then((resData: any) => {
const chPK = resData?.data?.bdstInfoPk;
const makeCh = resData.data.chgBuilding.map(
(item: chgBuilding) => ({
...item,
bdstInfoFk: chPK,
})
);
const chch = postFectch("/reuseFc/chgBuilding", makeCh);
return chch;
})
.then((chch: any) => {
if (chch.ok) {
alert("등록이 완료되었습니다.");
}
});
}else{
alert("등록이 완료되었습니다.");
}
// data는 단일 객체이므로 .map을 사용할 수 없습니다.
// 등록 시에는 건축물 변경 이력이 없으므로 chgBuilding을 빈 배열로 설정하고,
// API가 배열을 기대하므로 단일 객체를 배열로 감싸줍니다.
const chData = makeRstr(data);
const res = await postFectch("/drain/facility", chData);
if (res.status ===200) {
toast.success("온배수 건축물 등록이 완료되었습니다.");
}
const htdflRes = await initFetch(code);
const htdflRes = await initFetch(String(code));
set((state: htdflFacState) => ({
...state,
htdfl: {
@@ -237,8 +215,26 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = (
},
}));
} catch (errorReg) {
alert("등록 오류입니다.");
}
toast.error("온배수 건축물 등록 오류입니다..");
}
},htdflNmSearch :async(code:string | number, fcltNm:string) =>{
try {
const htdflRes = await getFetch("wr", `/drain/facilityList/1/20/${code}/${fcltNm}`);
set((state: htdflFacState) => {
return {
...state,
htdfl: {
...state.htdfl,
htdflRes: htdflRes,
isInit: false,
},
};
});
} catch (error) {
console.log(error);
}
},
};
};
@@ -31,8 +31,8 @@ export const pbnstMngUpHeadListExport: TableUpperProps[] = [
export const pplInstlsHeadListExport: TableUpperProps[] = [
{ id: "choose", title: "선택", type: "checkbox" },
{ id: "ppMqlt", title: "관재질", type: "select", selectData: "pplQulity" },
{ id: "pdmt", title: "관경", type: "select", selectData: "pplLenght" },
{ id: "cpct", title: "연장", edit: true, type: "number" },
{ id: "pdmt", title: "관경(mm)", type: "select", selectData: "pplLenght" },
{ id: "cpct", title: "연장(m)", edit: true, type: "number" },
];
export const pbnstDemandHeadListExport: TableUpperProps[] = [
@@ -69,20 +69,21 @@ export type formDetailType = {
id: string;
lable: string;
type?: string;
selectData?: string; // select box일 경우
option?:string
};
export const wasteWaterFcltINfo: formDetailType[] = [
{ id: "wwtEmsnFcltPrmsnYn", lable: "폐수배출물신고 허가여부", type: "selectYn" },
{ id: "tmsInstlYn", lable: "Tms설치여부", type: "selectYn" },
{ id: "pbnstDrngInflwExclYn", lable: "공공하수도유입제외 허가여부", type: "selectYn" },
{ id: "wwtEmsnFcltPrmsnYn", lable: "폐수배출물신고 허가여부", type: "select" , selectData: "yn"},
{ id: "tmsInstlYn", lable: "Tms설치여부", type: "select" , selectData: "yn" },
{ id: "pbnstDrngInflwExclYn", lable: "공공하수도유입제외 허가여부", type: "select" , selectData: "yn" },
];
export const concentratedWaterInfo: formDetailType[] = [
{ id: "erctWtrOcrnCpct", lable: "눙축수 발생량(m³/일)", type: "number" },
{ id: "erctUswtrLinkSpt", lable: "눙축수연계지점" },
{ id: "erctUswtrFcltCpct", lable: "시설용량(m³/일)", type: "number" },
{ id: "erctUswtrPrcsMeth", lable: "눙축수처리방식" },
{ id: "erctUswtrPrcsMeth", lable: "눙축수처리방식", type : "select", selectData: "erctUswtrPrcsMeth" },
{ id: "erctUswtrPrcsMsrcCd", lable: "눙축수처리공법" },
];
@@ -90,8 +91,8 @@ export const ruseFcltinfo: formDetailType[] = [
{ id: "rprcsFcltCpct", lable: "시설용량(m³/일)", type: "number" },
{ id: "majorPrcsMsrcCd", lable: "주처리공법" },
{ id: "addr", lable: "위치(주소)" },
{ id: "rprcsMthdCd", lable: "재처리방식" },
{ id: "inoutYn", lable: "위치(처리장 내외)" ,type: "selectYn" }, // 머가 내? 외?,
{ id: "rprcsMthdCd", lable: "재처리방식", type: "select", selectData: "rusePrcsMthdCd" },
{ id: "inoutYn", lable: "위치(처리장 내외)" ,type: "select" , selectData: "yn"}, // 머가 내? 외?,
{ id: "mainSplyPumpCpct", lable: "메인공급펌프 용량", type: "number" },
];
@@ -100,12 +101,12 @@ export const businessMoneyInfo: formDetailType[] = [
{ id: "lcep", lable: "지방비", type: "number" },
{ id: "cusrAlotm", lable: "민간투자비", type: "number" },
{ id: "etc", lable: "기타" },
{ id: "totalMoney", lable: "합계", type: "number" ,option:"readOnly" },
{ id: "totalMoney", lable: "합계", type: "number" ,option:"disabled" },
];
export const businessSttnInfo: formDetailType[] = [
{ id: "seCd", lable: "구분" },
{ id: "bizMthdCd", lable: "사업방식" },
{ id: "seCd", lable: "구분", type: "select", selectData: "ruseSeCd" },
{ id: "bizMthdCd", lable: "사업방식", type: "select", selectData: "ruseBizMthdCd" },
{ id: "bizCmcnDay", lable: "사업준공일", type: "date" },
{ id: "instlAprvSncnDt", lable: "설치승인인가일", type: "date" },
];
@@ -118,15 +119,15 @@ export const ruseFcltIntakeList: formDetailType[] = [
];
export const ruseWaterQulityList: formDetailType[] = [
{ id: "bod", lable: "BOD(mg/L)", type: "number" },
{ id: "grssClfg", lable: "총대장균군수(개/mg)", type: "number" },
{ id: "combResdch", lable: "결합잔류염소(mh/L)", type: "number" },
{ id: "trbt", lable: "탁도(NTU)", type: "number" },
{ id: "smll", lable: "냄새", type: "number" },
{ id: "crmty", lable: "색도", type: "number" },
{ id: "tn", lable: "총질소(mg/L)", type: "number" },
{ id: "tph", lable: "총인(mg/L)", type: "number" },
{ id: "ph", lable: "수소이온농도", type: "number" },
{ id: "clIonQnt", lable: "염화물(mg/L)", type: "number" },
{ id: "cdtnv", lable: "전기전도도(μs/cm)​", type: "number" },
{ id: "bod", lable: "BOD(mg/L)", type: "text" },
{ id: "grssClfg", lable: "총대장균군수(개/mg)", type: "text" },
{ id: "combResdch", lable: "결합잔류염소(mh/L)", type: "text" },
{ id: "trbt", lable: "탁도(NTU)", type: "text" },
{ id: "smll", lable: "냄새", type: "text" },
{ id: "crmty", lable: "색도", type: "text" },
{ id: "tn", lable: "총질소(mg/L)", type: "text" },
{ id: "tph", lable: "총인(mg/L)", type: "text" },
{ id: "ph", lable: "수소이온농도", type: "text" },
{ id: "clIonQnt", lable: "염화물(mg/L)", type: "text" },
{ id: "cdtnv", lable: "전기전도도(μs/cm)​", type: "text" },
];
@@ -52,7 +52,9 @@ export type pubFacState = {
fcOrgwtsHeadList: TableUpperProps[];
pbnstMngRegData: customPbnst[];
pbnsUpdateIndex: pbnstInsertIndex;
fcOrgwtsNmList:pubHasuType[];
dmdComList: any[];
apiMessage:string;
};
};
type pubFacActions = {
@@ -70,6 +72,7 @@ type pubFacActions = {
pbnstRuseFcltUpdate: (ruseRes: customPbnst[]) => void;
pbnstRuseBizUpdate: (ruseRes: fcBizType[]) => void;
pubHasuSearch :(page: string, sggOrCity: string , searchTxt:string) => void
getFcOrgwts: (fcltPkList: string[], size:number) => void;
};
export type pubFacType = pubFacState & pubFacActions;
@@ -90,7 +93,9 @@ export const pubFacStateExport: pubFacState = {
pbnstBizCycle: [{ text: "없음", val: "1" }],
pbnstMngRegData: [],
pbnsUpdateIndex: {} as pbnstInsertIndex,
fcOrgwtsNmList:[],
dmdComList: [],
apiMessage:''
},
};
@@ -102,12 +107,10 @@ const checkWrRegList = async () => {
const hasuListFetch = async (page: string, sggOrCity: string, searchTxt?:string) => {
const checkWrRegListRes = await checkWrRegList();
console.log(' checkWrRegListRes ', checkWrRegListRes)
let url = `/prcsFclt/ruseInfo/${page ? page : 1}/10/A/${sggOrCity? sggOrCity : "11"}/0/1/SW`;
const data = checkWrRegListRes.filter((item:string) => item !== "")
console.log(' checkWrRegListRes ' )
if(searchTxt){
url= url +`/${searchTxt}`
@@ -177,6 +180,7 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
try {
const pbnstHasuRes = await hasuListFetch(page, sggOrCity);
return set((state: pubFacState) => ({
...state,
pbnst: {
@@ -267,8 +271,13 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
ruseRes
);
if(response){
toast.success(` 하수재이용 시설 정보가 수정되었습니다.`);
}
} catch (error) {
console.error("Error pbnstRuseFcltUpdate:", error);
toast.error(` 하수재이용 시설 정보가 수정오류입니다.`);
}
} ,pbnstRuseBizUpdate : async(ruseRes:fcBizType[])=>{
try {
@@ -278,6 +287,10 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
`/reuseFc/pubFcBiz`,
ruseRes
);
if(response){
toast.success(` 하수재이용 시설 사업이 수정되었습니다.`);
}
const pbnstRes = await hasuListFetch("11", "11");
return set((state: pubFacState) => ({
@@ -290,6 +303,7 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
}));
} catch (error) {
console.error("Error pbnstRuseFcltUpdate:", error);
toast.error(` 하수재이용 시설 사업이 수정이 실패되엇습니다..`);
}
} ,
getBizCycle: async (pbnstObj: customPbnst) => {
@@ -399,6 +413,25 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => {
} catch (e) {
console.log(e);
}
},getFcOrgwts : async (fcltPkList: string[] ,size:number) => {
let url = `/prcsFclt/ruseInfo/1/${size}/A/0/0/1/SW`;
const fcOrgwtsNmList = await postFectch(
url,
fcltPkList,
fetchType.COMMON
);
return set((state: pubFacState) => ({
...state,
pbnst: {
...state.pbnst,
fcOrgwtsNmList:fcOrgwtsNmList.data??[]
},
}));
}
};
};
@@ -14,10 +14,10 @@ export type selectLabel = {
val: string;
};
export type pbnstInsertIndex ={
mng:number,
biz:number
}
export type pbnstInsertIndex = {
mng: number;
biz: number;
};
export type publicReuseFacilityType = {
sido: string;
@@ -45,20 +45,20 @@ export type ruseFcltType = {
};
export type PbnstDemandComType = {
dmdInfoPk : string | '';
bzmnNo : string | '';
dmdCoNm : string | '';
daddr : string | '';
telno : string | '';
rgtr : string | '';
useUsgCd : string | '';
ctpvCd : string | '';
sggCd : string | '';
dmdAplyPswd: string | '';
dmdInfoPk: string | "";
bzmnNo: string | "";
dmdCoNm: string | "";
daddr: string | "";
telno: string | "";
rgtr: string | "";
useUsgCd: string | "";
ctpvCd: string | "";
sggCd: string | "";
dmdAplyPswd: string | "";
splyCpct: number | 0;
splyFmYn:string|'Y'
// dmdInfoFk:string|''
}
splyFmYn: string | "Y";
// dmdInfoFk:string|''
};
const pubHasuZod = z.object({
choose: z.boolean().default(false),
@@ -68,23 +68,27 @@ const pubHasuZod = z.object({
bscFctNm: z.string().default(""),
efflWqltStndrdNm: z.string().default(""),
fctCpc: z.number().default(0),
addr:z.string().nullish()
addr: z.string().nullish(),
});
export type pubHasuType = z.infer<typeof pubHasuZod>;
const zDate = z.preprocess((v) => (v ? new Date(v as string) : undefined), z.date()).nullish() ;
const zDate = z
.preprocess((v) => (v ? new Date(v as string) : undefined), z.date())
.nullish();
const zDateNullish = zDate.nullish();
const zDateDefaultNow = zDate.default(new Date());
const zNum = z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number());
const zNum = z.preprocess(
(v) => (v !== null && v !== undefined ? Number(v) : v),
z.number()
);
const zNumNullish = zNum.nullish();
const zNumDefaultZero = zNum.default(0).nullish();
const pplInstlSchema = z.object({
ruseFcltBizFk: zNumNullish,
choose: z.boolean().default(false),
pdmt: z.string().default(''),
pdmt: z.string().default(""),
ppMqlt: z.string().default(""),
cpct: zNumDefaultZero,
});
@@ -115,22 +119,23 @@ export const fcOperSchema = z.object({
});
const fcOrgwtSchema = z.object({
pbnstRuseFcltBizFk:zNumNullish,
pbnstRuseFcltBizFk: zNumNullish,
swtrmtFcltFk: z.string().nullish(),
orgwtWtritQnt: zNum,
tmsYn: z.enum(["Y", "N"]).default("N"),
dschgFmYn: z.enum(["Y", "N"]).default("N"),
dtlPstnExpln: z.string().default(''),
dtlPstnExpln: z.string().default(""),
regDt: zDateNullish,
rgtr: z.string().default(''),
rgtr: z.string().default(""),
mdfcnDt: zDateNullish,
mdfr: z.string().default(''),
mdfr: z.string().default(""),
choose: z.boolean().default(false),
ruseUtztnFcltNm:z.string().nullish()
});
const dmdColist = z.object({
dmdInfoPk: z.string().default(''),
dmdCoNm: z.string().default(''),
dmdInfoPk: z.string().default(""),
dmdCoNm: z.string().default(""),
daddr: z.string(),
useUsgCd: z.string().nullish(),
splyBgngDay: zDateDefaultNow,
@@ -148,18 +153,18 @@ export const fcBizSchema = z.object({
pbnstRuseFcltFk: z.string().nullish(),
superkey: z.string().default("").nullish(),
cycl: zNum.default(1),
seCd: z.string().default(""),
seCd: z.string().max(10, "구분은 10자를 초과할 수 없습니다.").default(""),
bizMthdCd: z.string().default(""),
instlAprvSncnDt: zDateDefaultNow,
bizCmcnDay: zDateDefaultNow,
gvsbs: zNumDefaultZero,
lcep: zNumDefaultZero,
cusrAlotm: zNumDefaultZero,
etc: z.string().default(''),
etc: z.string().default(""),
totalMoney: zNumNullish.default(0),
rprcsFcltCpct: zNumDefaultZero,
majorPrcsMsrcCd: z.string().default(""),
addr: z.string().default(''),
majorPrcsMsrcCd: z.string().nullish(),
addr: z.string().default(""),
rprcsMthdCd: z.string().default(""),
inoutYn: z.enum(["Y", "N"]).default("Y"),
mainSplyPumpCpct: zNumNullish,
@@ -171,17 +176,29 @@ export const fcBizSchema = z.object({
wwtEmsnFcltPrmsnYn: z.string().default("Y"),
tmsInstlYn: z.string().default("Y"),
pbnstDrngInflwExclYn: z.string().default("Y"),
bod: zNumDefaultZero,
grssClfg: zNumDefaultZero,
combResdch: zNumDefaultZero,
trbt: zNumDefaultZero,
clIonQnt: zNumDefaultZero,
smll: zNumDefaultZero,
crmty: zNumDefaultZero,
tph: zNumDefaultZero,
tn: zNumDefaultZero,
ph: zNumDefaultZero,
cdtnv: zNumDefaultZero,
// bod: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "BOD 값은 5자리 미만이어야 합니다.")).nullish(),
// grssClfg: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총대장균군 값은 5자리 미만이어야 합니다.")).nullish(),
// combResdch: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "결합잔류염소 값은 5자리 미만이어야 합니다.")).nullish(),
// trbt: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "탁도 값은 5자리 미만이어야 합니다.")).nullish(),
// clIonQnt: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "염소이온량 값은 5자리 미만이어야 합니다.")).nullish(),
// smll: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "냄새 값은 5자리 미만이어야 합니다.")).nullish(),
// crmty: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "색도 값은 5자리 미만이어야 합니다.")).nullish(),
// tph: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총인 값은 5자리 미만이어야 합니다.")).nullish(),
// tn: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총질소 값은 5자리 미만이어야 합니다.")).nullish(),
// ph: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "수소이온농도 값은 5자리 미만이어야 합니다.")).nullish(),
// cdtnv: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "전기전도도 값은 5자리 미만이어야 합니다.")).nullish(),
bod: z.string().nullish(),
grssClfg: z.string().nullish(),
combResdch: z.string().nullish(),
trbt: z.string().nullish(),
clIonQnt: z.string().nullish(),
smll: z.string().nullish(),
crmty: z.string().nullish(),
tph: z.string().nullish(),
tn: z.string().nullish(),
ph: z.string().nullish(),
cdtnv: z.string().nullish(),
designCst: zNumNullish,
cstrnSpvsCst: zNumNullish,
fcltSbsdCst: zNumNullish,
@@ -217,10 +234,13 @@ export const fcBizSchema = z.object({
});
export const pbnstRegZod = z.object({
pbnstRuseFcltPk: z.string().default(''),
pbnstRuseFcltPk: z.string().default(""),
swtrmtFcltFk: z.string().default(""),
ruseUtztnFcltNm: z.string().default(""),
fcltCpct: zNumDefaultZero,
ruseUtztnFcltNm: z
.preprocess(
(v) => (v !== null && v !== undefined ? String(v).trim() : ""), // null/undefined → "", 공백 제거
z.string().min(1, "시설 이름을 입력하세요")),
fcltCpct: zNumDefaultZero,
ruseCpct: zNumDefaultZero,
splyExtraCpct: zNumDefaultZero,
regDt: zDateDefaultNow,
@@ -229,16 +249,20 @@ export const pbnstRegZod = z.object({
mdfr: z.string().default("test"),
fcFiles: z.array(fcFileSchema).default([]).nullish(),
fcBizs: z.array(fcBizSchema).default([]),
fcOpers: z.array(fcOperSchema).default([]).nullish(),
fcOpers: z
.array(fcOperSchema)
.refine((val) => val.length >= 1, {
message: "재이용시설의 운영정보를 최소 하나를 등록해주세요",
})
.default([]), // 기본값 설정
});
export const pbnstRegArrayZod = z.array(pbnstRegZod)
export const pbnstRegArrayZod = z.array(pbnstRegZod);
export type PbnstRegArrrayType = z.infer<typeof pbnstRegArrayZod>
export type PbnstRegArrrayType = z.infer<typeof pbnstRegArrayZod>;
export type fcBizType = z.infer<typeof fcBizSchema>;
export type fcOrgwtType = z.infer<typeof fcOrgwtSchema>;
export type ppInstlType = z.infer<typeof pplInstlSchema>;
export type operType = z.infer<typeof fcOperSchema>;
export type fcFileType = z.infer<typeof fcFileSchema>;
@@ -2,76 +2,29 @@ import type { TableUpperProps } from "@/app/store/common/type/header";
import { getFetch, postFectch, putFectch } from "@/components/lib/api";
import { z } from "zod";
import { makeMdfr, makeRstr } from "../bdstReg";
const validationMessages = {
required: (field: string) => `${field}을(를) 입력해주세요.`,
requiredSelect: (field: string) => `${field}을(를) 선택해주세요.`,
yearFormat: '4자리 연도만 입력하세요',
};
import toast from "react-hot-toast";
import { baseReuseBuildingSchema, chgBuildingSchema, validationMessages } from "./type";
export const TEST_UUID= '2d4b151d-1828-41e0-91d9-4cd28ea1cc7e'
const rainyObjSchema = z.object({
bdstReuseFcltFk: z.number().nullish(),
pntfColKnd: z.string().default(""),
pntfColArea: z.number().default(0),
pntfColKnd: z.string().min(1, { message: validationMessages.requiredSelect("종류") }),
pntfColArea: z.number().nullish() ,
dtfcltCpct: z.number().default(0),
filtrTrtmfctYn: z.string().default("Y").nullish(),
filtrTrtmfctYn: z.string().default("Y"),
filtrTrtmfctCpct: z.number().nullish().default(0),
filtrTrtmfctPrs: z.string().default(""),
annRnwtQty: z.number().default(0),
oilqMeasMthd: z.string().default("").nullish(),
rgtr:z.string().nullish(),
mdfr:z.string().nullish(),
}).refine((data) => data.pntfColKnd.length > 0, {
message: validationMessages.requiredSelect("종류"),
path: ["pntfColKnd"],
}).refine((data) => data.pntfColArea > 0, {
message: validationMessages.required("면적(m³)"),
path: ["pntfColArea"],
});
const reuseBuildingSchema = z.object({
bdstRuseFcltPk: z.number().nullish(),
bdstInfoFk:z.number().nullish(),
instlCmptnDt: z.coerce.date().default(() => new Date()),
instlCst: z.number().nullish(),
operMngCd: z.string().optional().default(""),
anlOperCst: z.number().default(0),
instlCstSprtYn: z.string().default("Y"),
instlCstSprtAmt: z.number().nullish(),
grntchBdstCertYn: z.string().default("Y"),
grntchBdstCertYr: z.string().default("")
.refine((year)=> year.length===0 || /^\d{4}$/.test(year),{
message: validationMessages.yearFormat
}),
grntchBdstCertNo: z.string().default(""),
grntchBdstCertGrd: z.string().default(""),
crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값
crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값
crgRdctAmt: z.number().default(0), // 안쓰는 값
ruseFcltFlg: z.string().default(""),
bdstUsgCd: z.number().default(0),
lawTrgtYn: z.string().default("Y"),
rgtr:z.string().nullish(),
mdfr:z.string().nullish(),
rainyObj: rainyObjSchema.default({})
}).refine((data) => data.bdstUsgCd > 0, {
message: validationMessages.requiredSelect("건축물용도"),
path: ["bdstUsgCd"],
});
const chgBuildingSchema = z.object({
bdstInfoFk: z.number().nullish(),
archChgPrmsnDt: z.coerce.date({required_error: validationMessages.required("허가일")}),
archChgArea: z.string().default("-"),
bdstChgNm: z.string().default(""),
rgtr:z.string().nullish(),
mdfr:z.string().nullish(),
}).refine(data => data.bdstChgNm.length > 0, {
message: validationMessages.requiredSelect("구분"),
path: ["bdstChgNm"]
});
const rainBuildingSchema = baseReuseBuildingSchema.extend({
rainyObj: rainyObjSchema,
})
export const rainZod = z.object({
bdstInfoPk: z.number().optional(),
@@ -81,11 +34,11 @@ export const rainZod = z.object({
bdstNm: z.string().default(""),
frstArchPrmsnDt: z.date().default(() => new Date()),
archChgPrmsnDt: z.coerce.date().nullish().default(null),
archArea: z.number().default(0),
archArea: z.string().default(''),
archChgArea: z.number().default(0),
rgtr:z.string().nullish(),
mdfr:z.string().nullish(),
reuseBuilding: z.array(reuseBuildingSchema).default([]),
reuseBuilding: z.array(rainBuildingSchema).default([]),
chgBuilding: z.array(chgBuildingSchema).default([])
})
@@ -209,26 +162,28 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = (
const test =await makeRstr(data);
const res = await postFectch("/reuseFc/rainyInfo", test);
if(res.ok){
if(res.status===200){
if(data.chgBuilding.length > 0){
await res.json().then((resData: any) => {
const chPK = resData?.data?.bdstInfoPk
const makeCh= resData.data.chgBuilding.map((item: chgBuilding) =>
const chPK = res?.data?.bdstInfoPk
const makeCh= res.data.chgBuilding.map((item: chgBuilding) =>
( {
...item,
bdstInfoFk: chPK,
})
);
const chch= postFectch("/reuseFc/chgBuilding", makeCh);
return chch;
}).then((chch: any) => {
if(chch.ok) {
alert("등록이 완료되었습니다.");
const chch= await postFectch("/reuseFc/chgBuilding", makeCh);
if(chch.status ===200) {
toast.success("건축물 등록이 완료되었습니다.");
}else{
toast.error("건축물 증축/개축/중축 오류입니다.");
}
})
}else{
alert("등록이 완료되었습니다.");
toast.success("건축물 등록이 완료되었습니다.");
}
}else{
toast.error("건축물 등록이 완료되었습니다.");
}
const rainRes = await initFetch(code);
set((state: rainFacState) => ({
@@ -241,7 +196,7 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = (
}));
} catch (e) {
console.log("e", e);
alert("등록 오류입니다.");
toast.error("등록시 오류가 발생하였습니다.");
} finally {
}
},
@@ -249,9 +204,9 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = (
try {
const chData =await makeMdfr(data);
const res = await putFectch("/reuseFc/rainyInfo", chData);
console.log('rainUpdate', res);
if(res.ok){
alert(" 수정이 완료되었습니다.")
if(res.status === 200){
toast.success("수정이 완료되었습니다.");
}
const rainRes = await initFetch(code);
set((state: rainFacState) => ({
@@ -264,7 +219,7 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = (
}));
} catch (e) {
console.log("e", e);
alert("업데이트 오류입니다.");
toast.error("수정시 오류가 발생하였습니다.");
} finally {
}
}
@@ -0,0 +1,90 @@
import { z } from "zod";
export const validationMessages = {
required: (field: string) => `${field}을(를) 입력해주세요.`,
requiredSelect: (field: string) => `${field}을(를) 선택해주세요.`,
yearFormat: '4자리 연도만 입력하세요',
};
export const baseReuseBuildingSchema = z.object({
bdstRuseFcltPk: z.number().nullish(),
bdstInfoFk: z.number().nullish(),
instlCmptnDt: z.coerce.date().default(() => new Date()),
instlCst: z.number().nullish(),
operMngCd: z.string().optional().default(""),
anlOperCst: z.number().default(0),
instlCstSprtYn: z.string().default("Y"),
instlCstSprtAmt: z.number().nullish(),
grntchBdstCertYn: z.string().default("Y"),
grntchBdstCertYr: z
.string()
.max(4, { message: validationMessages.yearFormat })
.refine(
(year) => year.length === 0 || /^\d{4}$/.test(year),
{
message: validationMessages.yearFormat,
}
),
grntchBdstCertNo: z.string().default(""),
grntchBdstCertGrd: z.string().default(""),
crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값
crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값
crgRdctAmt: z.number().default(0), // 안쓰는 값
ruseFcltFlg: z.string().default(""),
bdstUsgCd: z
.number()
.default(0)
.refine((val) => val === -1 || val >= 1, {
message: validationMessages.requiredSelect("건축물용도"),
}),
lawTrgtYn: z.string().default("Y"),
rgtr: z.string().nullish(),
mdfr: z.string().nullish(),
});
export const chgBuildingSchema = z.object({
bdstInfoFk: z.number().nullish(),
archChgPrmsnDt: z.coerce.date({required_error: validationMessages.required("허가일")}),
archChgArea: z.string().default("-"),
bdstChgNm: z.string().min(1, { message: validationMessages.requiredSelect("구분") }),
rgtr:z.string().nullish(),
mdfr:z.string().nullish(),
});
export const htdflReuseBuildingSchema = z.object({
bdstRuseFcltPk: z.number().nullish(),
bdstInfoFk: z.number().nullish(),
instlCmptnDt: z.coerce.date().default(() => new Date()),
instlCst: z.number().nullish(),
operMngCd: z.string().optional().nullish(),
anlOperCst: z.number().default(0),
instlCstSprtYn: z.string().default("Y"),
instlCstSprtAmt: z.number().nullish(),
grntchBdstCertYn: z.string().default("Y"),
grntchBdstCertYr: z
.string()
.max(4, { message: validationMessages.yearFormat })
.refine(
(year) => year.length === 0 || /^\d{4}$/.test(year),
{
message: validationMessages.yearFormat,
}
)
.nullish(),
grntchBdstCertNo: z.string().default(""),
grntchBdstCertGrd: z.string().default(""),
crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값
crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값
crgRdctAmt: z.number().default(0), // 안쓰는 값
ruseFcltFlg: z.string().default(""),
bdstUsgCd: z.number().default(0).nullish(),
lawTrgtYn: z.string().default("Y").nullish(),
rgtr: z.string().nullish(),
mdfr: z.string().nullish(),
});
@@ -1,9 +1,32 @@
import { getFetch, postFectch, putFectch } from "@/components/lib/api";
import { upHeadList, type chgBuilding } from "../rainReg";
import { upHeadList, type chgBuilding } from "../rainReg";
import type { TableUpperProps } from "@/app/store/common/type/header";
import { z } from "zod";
import { baseReuseBuildingSchema, chgBuildingSchema, validationMessages } from "../rainReg/type";
import { makeMdfr, makeRstr } from "../bdstReg";
import toast from "react-hot-toast";
const rswtrObjSchema = z.object({
bdstRuseFcltFk: z.number().nullish(),
//wtrUsqty: z.number({ required_error: validationMessages.required("물사용량(m³)") }).positive({ message: validationMessages.required("물사용량(m³)") }),
//oblgTrgtWtrUsqty: z.number().nullish().default(0),
orgwtKndCd: z.string({ required_error: validationMessages.requiredSelect("원수 종류") }).min(1, { message: validationMessages.requiredSelect("원수 종류") }),
filtrTrtmfctCpct: z.number({ required_error: validationMessages.required("처리시설 용량") }),
filtrTrtmfctProcs: z.string({ required_error: validationMessages.required("주요처리공정") }).min(1, { message: validationMessages.required("주요처리공정") }),
filtrTrtmfctUsgCd: z.string().default("").nullish(),
rswtrUsqty: z.number().default(0),
altUswtrUtztnYn: z.enum(["Y", "N"]).nullish(), //안쓰는값
altUswtrKndCd: z.string().default("").nullish(), //안쓰는값
altUswtrUtztnQnt: z.number().default(0), //안쓰는값
altUswtrIdntyMthdCd: z.string().default("").nullish(), //안쓰는값
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
});
const rswtBuildingSchema = baseReuseBuildingSchema.extend({
rswtrObj: rswtrObjSchema,
})
export const rswtZod = z.object({
bdstInfoPk: z.number().nullish(),
@@ -14,80 +37,15 @@ export const rswtZod = z.object({
frstArchPrmsnDt: z.date().default(() => new Date()),
archChgPrmsnDt: z.coerce.date().nullish().default(null),
chBuildingYn: z.string().nullish(),
archArea: z.number().default(0),
archArea: z.string().default(''),
archChgArea: z.number().default(0),
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
bdstUsgText: z.string().default(""),
cityText: z.string().default(""),
sggText: z.string().default(""),
reuseBuilding: z
.array(
z.object({
bdstRuseFcltPk: z.number().nullish(),
bdstInfoFk: z.number().nullish(),
instlCmptnDt: z.coerce.date().default(() => new Date()),
instlCst: z.number().default(0),
operMngCd: z.string().nullish().default(""),
anlOperCst: z.number().default(0),
instlCstSprtYn: z.enum(["Y", "N"]),
instlCstSprtAmt: z.number().default(0),
grntchBdstCertYn: z.enum(["Y", "N"]),
grntchBdstCertYr: z
.string()
.default("")
.refine((year) => year.length === 0 || /^\d{4}$/.test(year), {
message: "4자리 연도만 입력하세요",
}),
grntchBdstCertNo: z.string().default(""),
grntchBdstCertGrd: z.string().default(""),
crgRdctYn: z.string().default("N").nullish(), // 안쓰는 값
crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값
crgRdctAmt: z.number().default(0), // 안쓰는 값
ruseFcltFlg: z.string().default(""),
bdstUsgCd: z.number().default(0),
lawTrgtYn: z.string().default(""),
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
rswtrObj: z
.object({
bdstRuseFcltFk: z.number().nullish(),
wtrUsqty: z.number().default(0),
oblgTrgtWtrUsqty: z.number().nullish().default(0),
orgwtKndCd: z.string().default(""),
filtrTrtmfctCpct: z.number().default(0),
filtrTrtmfctProcs: z.string().default(""),
filtrTrtmfctUsgCd: z.string().default("").nullish(),
rswtrUsqty: z.number().default(0),
altUswtrUtztnYn: z.enum(["Y", "N"]).nullish(), //안쓰는값
altUswtrKndCd: z.string().default("").nullish(), //안쓰는값
altUswtrUtztnQnt: z.number().default(0), //안쓰는값
altUswtrIdntyMthdCd: z.string().default("").nullish(), //안쓰는값
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
})
.default({}),
})
)
.default([]),
chgBuilding: z
.array(
z.object({
bdstInfoFk: z.number().nullish(),
archChgPrmsnDt: z.coerce.date(),
archChgArea: z.string().default("-"),
bdstChgNm: z.string().default(""),
rgtr: z.string().nullish().default(null),
mdfr: z.string().nullish().default(null),
})
)
.default([]),
reuseBuilding: z.array(rswtBuildingSchema).default([]),
chgBuilding: z.array(chgBuildingSchema).default([]),
});
export type customRswtBdst = z.infer<typeof rswtZod>;
@@ -195,8 +153,8 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = (
try {
const chData = await makeMdfr(data);
const res = await putFectch("/reuseFc/waterreuseFc", chData);
if (res.ok) {
alert("등록이 완료되었습니다.");
if (res.status ===200) {
toast.success("중수도 건축물 수정이 완료되었습니다.");
const rswtRes = await initFetch(code);
set((state: rswtFacState) => ({
...state,
@@ -208,10 +166,8 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = (
}));
}
} catch (errorUpdate) {
alert(
"업데이트 오류입니다. " +
(errorUpdate instanceof Error ? errorUpdate.message : "")
);
toast.error("중수도 건축물 수정 오류입니다. ");
} finally {
}
},
@@ -219,29 +175,28 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = (
try {
const chData = await makeRstr(data);
const res = await postFectch("/reuseFc/waterreuseFc", chData);
if (res.ok) {
if (data.chgBuilding.length > 0) {
await res
.json()
.then((resData: any) => {
const chPK = resData?.data?.bdstInfoPk;
const makeCh = resData.data.chgBuilding.map(
(item: chgBuilding) => ({
...item,
bdstInfoFk: chPK,
})
);
const chch = postFectch("/reuseFc/chgBuilding", makeCh);
return chch;
})
.then((chch: any) => {
if (chch.ok) {
alert("등록이 완료되었습니다.");
}
});
if (res.status===200) {
if(data.chgBuilding.length > 0){
const chPK = res?.data?.bdstInfoPk
const makeCh= res.data.chgBuilding.map((item: chgBuilding) =>
( {
...item,
bdstInfoFk: chPK,
})
);
const chch= await postFectch("/reuseFc/chgBuilding", makeCh);
if(chch.status ===200) {
toast.success("건축물 등록이 완료되었습니다.");
}else{
toast.error("건축물 증축/개축/중축 오류입니다.");
}
} else {
alert("등록이 완료되었습니다.");
toast.success("건축물 등록이 완료되었습니다.");
}
}else{
toast.error("건축물 등록이 완료되었습니다.");
}
const rainRes = await initFetch(code);
set((state: rswtFacState) => ({
@@ -254,7 +209,7 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = (
}));
} catch (e) {
console.log("e", e);
alert("등록 오류입니다.");
toast.error("등록시 오류가 발생하였습니다.");
} finally {
}
},
@@ -5,6 +5,7 @@ import type { z } from "zod";
import { controlHeadView } from "../rainSttn";
import type { customHtdflBdst } from "../../fcltReg/htdflReg";
import { fetchType, getFetch } from "@/components/lib/api";
import type { SelectDataType } from "@common/business_components/ui";
const htdflSttHeadList = htdflSttHeadListExport;
const htdflSttNodeList = htdflSttNodeListExport;
@@ -20,8 +21,12 @@ export type htdflSttnState = {
};
};
type htdflSttnActions = {
initHtdfl: (con: string) => void;
htdflSttnSearch: (page: number, total: string, con: string) => void;
initHtdfl: (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => void;
htdflSttnSearch: (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => void;
htdflChSttnHead: (
headList: z.infer<typeof sttnHeadListType>,
) => void;
@@ -38,10 +43,17 @@ export const htdflSttnStateExport: htdflSttnState = {
isInit: false,
},
};
const initFetch = async (code: string | number) => {
const initFetch = async (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => {
const htdflRes = await getFetch(
"wr",
`/drain/facilityList/1/20/${code}`)
`/drain/facilityList` +
(page ? `/${page}` : "/1") +
(total ? `/${total}` : "/20") +
(con ? `/${con}` : "/0") +
(txt ? `/${txt}` : "")
);
return htdflRes;
};
@@ -50,9 +62,11 @@ export const htdflSttnActionsExport: (set: any) => htdflSttnActions = (
set: any
) => {
return {
initHtdfl: async (code: string | number) => {
initHtdfl: async (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => {
try {
const htdflSttnRes = await initFetch(code);
const htdflSttnRes = await initFetch(page, total, con, txt, sidoCode, sggCode);
set((state: htdflSttnState) => {
return {
...state,
@@ -67,17 +81,22 @@ export const htdflSttnActionsExport: (set: any) => htdflSttnActions = (
console.log(error);
}
},
htdflSttnSearch: async (page: number, total: string, con: string) => {
htdflSttnSearch: async (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => {
try {
console.log("page", page)
const htdflSttnRes = await getFetch(
fetchType.WR,
`/drain/facilityList/${page}/${total}/${con}`
`/drain/facilityList` +
(page ? `/${page}` : "/1") +
(total ? `/${total}` : "/20") +
(con ? `/${con}` : "/0") +
(txt ? `/${txt}` : "")
);
if (Number(total) > 9000) {
set((state: htdflSttnState) => {
return {
...state,
htdflSttn: {
@@ -31,8 +31,8 @@ export const rainSttHeadListExport: z.infer<typeof sttnHeadListType> = [
visible: true,
},
{
id: "bdstUsgCd",
title: "건축물용도코드",
id: "bdsgUsgText",
title: "건축물용도",
visible: true,
},
{
@@ -209,8 +209,8 @@ export const rainSttNodeListExport: CommonTableProps[] = [
colSpan: 1,
},
{
id: "bdstUsgCd",
title: "건축물용도코드",
id: "bdsgUsgText",
title: "건축물용도",
upName: "bdnstInfo",
visible: true,
row: 2,
@@ -1,4 +1,4 @@
import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn";
import { fetchType, getFetch } from "@/components/lib/api";
import {
type customRainBdst,
@@ -16,34 +16,34 @@ import type { CommonTableProps } from "@/app/store/common/type/header";
import type { SelectDataType } from "@common/business_components/ui";
// 테이블 헤드 리스트
const rainSttHeadList = rainSttHeadListExport;
const rainSttHeadList = rainSttHeadListExport;
const rainSttNodeList = rainSttNodeListExport;
const rainSttnDetailNodeList = rainSttnDetailNodeListExport;
const rainSttnDetailHeadList = rainSttnDetailHeadListExport;
const rainSttnRes: customRainBdst[] = [];
const rainChkRes : rainChkZodType ={} as rainChkZodType
const rainChkRes: rainChkZodType = {} as rainChkZodType
const visibleHeadList = (falseHeads:CommonTableProps[], orgNodeList:CommonTableProps[])=>{
const visibleHeadList = (falseHeads: CommonTableProps[], orgNodeList: CommonTableProps[]) => {
const visibleHeads = orgNodeList.map((node) => {
const copyNode = {...node}
falseHeads.forEach((head)=>{
if(copyNode.id ===head.id)
copyNode.visible = head.visible
const copyNode = { ...node }
falseHeads.forEach((head) => {
if (copyNode.id === head.id)
copyNode.visible = head.visible
})
return copyNode;
})
return visibleHeads
}
const getColspanReductionMap = (falseHeads:CommonTableProps[], orgList:CommonTableProps[]):Record<string, number> => {
const reduceMap:Record<string, number> = {};
const getColspanReductionMap = (falseHeads: CommonTableProps[], orgList: CommonTableProps[]): Record<string, number> => {
const reduceMap: Record<string, number> = {};
falseHeads.forEach(head => {
const node = orgList.find(n => n.id === head.id);
if (node?.upName) {
reduceMap[node.upName] = (reduceMap[node.upName] || 0) + 1;
if (node.row&& node.row >= 3) {
if (node.row && node.row >= 3) {
const upperUpName = orgList.find(n => n.id === node.upName)?.upName;
if (upperUpName) {
reduceMap[upperUpName] = (reduceMap[upperUpName] || 0) + 1;
@@ -78,35 +78,37 @@ const applyColspanReduction = (
});
};
// 일단 헤더가 3개 이상인경우 visible 처리를 위해서 공통함수로 작업
export const controlHeadView = (headList:CommonTableProps[], orgNodeList:CommonTableProps[] ) =>{
export const controlHeadView = (headList: CommonTableProps[], orgNodeList: CommonTableProps[]) => {
const falseHeads = headList.filter((item) => item.visible == false);
const viewHeadList = visibleHeadList(falseHeads, orgNodeList)
const reduceMap= getColspanReductionMap( falseHeads,viewHeadList)
return applyColspanReduction(viewHeadList,reduceMap);
const reduceMap = getColspanReductionMap(falseHeads, viewHeadList)
return applyColspanReduction(viewHeadList, reduceMap);
}
export type rainSttnState = {
rainSttn: {
rainSttnRes: customRainBdst[];
rainSttHeadList: CommonTableProps[];
rainSttNodeList: CommonTableProps[];
rainSttnDetailHeadList:CommonTableProps[]
rainSttnDetailNodeList:CommonTableProps[]
rainChkRes:rainChkZodType
rainSttnDetailHeadList: CommonTableProps[]
rainSttnDetailNodeList: CommonTableProps[]
rainChkRes: rainChkZodType
excelList: [];
isInit: boolean;
};
};
type rainSttnActions = {
rainSttnInit:(con:string) => void
rainSttnSearch: (page: number, total: string, con: string, sidoCode: SelectDataType[], sggCode: SelectDataType[]) => void;
rainSttnInit: (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => void
rainSttnSearch: (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => void;
rainChSttnHead: (
headList: z.infer<typeof sttnHeadListType>,
) => void;
rainSttnChkSearch:(rusePk:string) => void;
rainSttnChkSearch: (rusePk: string) => void;
};
export type rainSttnType = rainSttnState & rainSttnActions;
@@ -115,10 +117,10 @@ export const rainSttnStateExport: rainSttnState = {
rainSttn: {
rainSttHeadList: rainSttHeadList,
rainSttNodeList: rainSttNodeList,
rainSttnDetailHeadList:rainSttnDetailHeadList,
rainSttnDetailNodeList:rainSttnDetailNodeList,
rainSttnDetailHeadList: rainSttnDetailHeadList,
rainSttnDetailNodeList: rainSttnDetailNodeList,
rainSttnRes: rainSttnRes,
rainChkRes:rainChkRes,
rainChkRes: rainChkRes,
excelList: [],
isInit: false,
},
@@ -128,12 +130,18 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = (
set: any
) => {
return {
rainSttnInit: async( con:string)=>{
try{
rainSttnInit: async (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => {
try {
const rainSttnRes = await getFetch(
const rainSttnRes = await getFetch(
fetchType.WR,
`/reuseFc/rainyInfoList/1/20/${con}`
`/reuseFc/rainyInfoList` +
(page ? `/${page}` : "/1") +
(total ? `/${total}` : "/20") +
(con ? `/${con}` : "/0") +
(txt ? `/${txt}` : "")
);
set((state: rainSttnState) => {
return {
@@ -145,17 +153,21 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = (
},
};
});
}catch(e){
} catch (e) {
console.log(e)
}
},
rainSttnSearch: async (page: number, total: string, con: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => {
rainSttnSearch: async (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => {
try {
const rainSttnRes = await getFetch(
fetchType.WR,
`/reuseFc/rainyInfoList/${page}/${total}/${con}`
`/reuseFc/rainyInfoList` +
(page ? `/${page}` : "/1") +
(total ? `/${total}` : "/20") +
(con ? `/${con}` : "/0") +
(txt ? `/${txt}` : "")
);
if (Number(total) > 9000) {
const rainExcel = excelRainSttnParsing(rainSttnRes, sidoCode, sggCode);
@@ -186,7 +198,7 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = (
rainChSttnHead: async (
headList: z.infer<typeof sttnHeadListType>
) => {
const changeNodeList= controlHeadView(headList,rainSttNodeList)
const changeNodeList = controlHeadView(headList, rainSttNodeList)
return set((state: rainSttnState) => ({
rainSttn: {
@@ -196,21 +208,21 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = (
},
}));
},
rainSttnChkSearch: async(
rusePk:string
)=>{
rainSttnChkSearch: async (
rusePk: string
) => {
const rainChkRes = await getFetch(
fetchType.WR,
`/reuseChk/rainy/${rusePk}`
);
return set((state: rainSttnState) =>({
return set((state: rainSttnState) => ({
rainSttn: {
...state.rainSttn,
rainChkRes:rainChkRes
rainChkRes: rainChkRes
},
}) )
}))
}
};
@@ -28,9 +28,12 @@ export type rswtSttnState = {
};
type rswtSttnActions = {
rswtSttnInit: (con: string) => void;
rswtSttnSearch: (page: number, total: string, con: string, sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => void;
rswtSttnInit: (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => void;
rswtSttnSearch: (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => void;
rswtChSttnHead: (
headList: z.infer<typeof sttnHeadListType>,
) => void;
@@ -54,12 +57,18 @@ export const rswtSttnActionsExport: (set: any) => rswtSttnActions = (
set: any
) => {
return {
rswtSttnInit: async (con: string) => {
rswtSttnInit: async (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => {
try {
const rswtSttnRes = await getFetch(
fetchType.WR,
`/reuseFc/waterreuseFc/1/20/${con}`
`/reuseFc/waterreuseFc` +
(page ? `/${page}` : "/1") +
(total ? `/${total}` : "/20") +
(con ? `/${con}` : "/0") +
(txt ? `/${txt}` : "")
);
set((state: rswtSttnState) => {
return {
@@ -75,13 +84,18 @@ export const rswtSttnActionsExport: (set: any) => rswtSttnActions = (
console.log(e)
}
},
rswtSttnSearch: async (page: number, total: string, con: string,
rswtSttnSearch: async (page: number, total: string, con: string, txt: string,
sidoCode: SelectDataType[],
sggCode: SelectDataType[]) => {
try {
console.log("헐", page)
const rswtSttnRes = await getFetch(
fetchType.WR,
`/reuseFc/waterreuseFc/${page}/${total}/${con}`
`/reuseFc/waterreuseFc` +
(page ? `/${page}` : "/1") +
(total ? `/${total}` : "/20") +
(con ? `/${con}` : "/0") +
(txt ? `/${txt}` : "")
);
if (Number(total) > 9000) {
const rswtExcel = excelRSwtSttnParsing(rswtSttnRes, sidoCode, sggCode);

Some files were not shown because too many files have changed in this diff Show More