Merge remote-tracking branch 'origin/iipbs/sn/dev' into iipbs/sn/main
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import {useState, useEffect} from 'react';
|
||||
import { getData } from '@/components/lib/api';
|
||||
import moment from "moment";
|
||||
|
||||
export type TInftwtAnls = {
|
||||
inftwtAnlsSn:number;
|
||||
aplcnYm: string;
|
||||
@@ -122,7 +124,7 @@ export const selectInftwtAnls = async(aplcnYm:String) => { // 침입수분석
|
||||
};
|
||||
export const getChartDataInftwtAnlsRslt = (arr: TInftwtAnlsRslt[]): { label: string; val: number }[] => {
|
||||
const options = arr.map(anlsRslt => ({
|
||||
label: anlsRslt.anlsTm.substring(4),
|
||||
label: moment(anlsRslt.anlsTm, "YYYYMMDDHHmmss").format("DD HH:mm"),
|
||||
val: anlsRslt.predcOilq,
|
||||
}));
|
||||
return options;
|
||||
@@ -170,11 +172,13 @@ export const selectPplInftwtQty = async(inftwtAnlsSn:number, pplMngNo:string) =>
|
||||
console.warn("inftwtAnlsSn or pplMngNo is not provided");
|
||||
return [];
|
||||
}
|
||||
const result = await getData(`v1/inftwtAnlsRslt/pplinftwtQty/${inftwtAnlsSn}/${pplMngNo}`);
|
||||
if (result && result.numberOfElements > 0) {
|
||||
console.log("selectPplInftwtQty", result.content);
|
||||
return result.content;
|
||||
}
|
||||
const result = await getData(`v1/inftwtAnlsRslt/pplInftwtQty/${inftwtAnlsSn}/${pplMngNo}`);
|
||||
if (result.length > 0) {
|
||||
console.log("selectPplInftwtQty", result);
|
||||
return result[0].avg_day_flow;
|
||||
} else {
|
||||
return 0; // 데이터가 없을 경우 0 반환
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -187,9 +191,9 @@ export const selectPplRdiiQty = async(rainInfwtAnlsSn:number, pplMngNo:string) =
|
||||
return [];
|
||||
}
|
||||
const result = await getData(`v1/rdiiAnlsRslt/pplRdiiQty/${rainInfwtAnlsSn}/${pplMngNo}`);
|
||||
if (result && result.numberOfElements > 0) {
|
||||
console.log("selectPplInftwtQty", result.content);
|
||||
return result.content;
|
||||
if (result?.sustaintime > 0) {
|
||||
console.log("selectPplRdiiQty", result);
|
||||
return result;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -219,14 +223,14 @@ export const selectRdiiAnls = async(aplcnYm:String) => { // 침입수분석
|
||||
};
|
||||
export const getChartDataRdiiAnlsRslt = (arr: TRdiiAnlsRslt[]): { label: string; val: number }[] => {
|
||||
const options = arr.map(anlsRslt => ({
|
||||
label: anlsRslt.anlsTm.substring(4),
|
||||
label: moment(anlsRslt.anlsTm, "YYYYMMDDHHmmss").format("DD HH:mm"),
|
||||
val: anlsRslt.predcOilq,
|
||||
}));
|
||||
return options;
|
||||
};
|
||||
export const getChartDataRdiiAnlsRsltMinq = (arr: TRdiiAnlsRslt[]): { label: string; val: number }[] => {
|
||||
const options = arr.map(anlsRslt => ({
|
||||
label: anlsRslt.anlsTm.substring(4),
|
||||
label: moment(anlsRslt.anlsTm, "YYYYMMDDHHmmss").format("DD HH:mm"),
|
||||
val: anlsRslt.minq,
|
||||
}));
|
||||
return options;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { CHART_FORM, type ChartConfig, MainChart, UiModal } from "@common/busine
|
||||
import { SelectBox } from '@common/business_components/ui';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import moment from "moment";
|
||||
|
||||
import type { TInftwtAnls, TOilqData } from './aplcnYms';
|
||||
import * as fn from './aplcnYms';
|
||||
@@ -16,19 +17,18 @@ type ModalOpenProp = {
|
||||
type DataType = {label: string; val: number};
|
||||
|
||||
const InfilChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal, pplMngNo }: ModalOpenProp) => {
|
||||
const [popupOpen, setPopupOpen] = React.useState(true);
|
||||
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("0");
|
||||
const [aplcnYm, setAplcnYm] = useState("");
|
||||
const [inftwtAnlss, setInftwtAnlss] = useState<TInftwtAnls[]>([]);
|
||||
const [inftwtAnlsInfos, setInftwtAnlsInfos] = useState<any[]>([{ val: 0, text: '선택' }]); // 침입수분석정보 options
|
||||
const [inftwtAnls, setInftwtAnls] = useState<TInftwtAnls | null>(null); // 침입수분석정보
|
||||
const [inftwtAnlsSn, setInftwtAnlsSn] = useState<number>(0); // 침입수분석 정보 선택시 사용
|
||||
const [chartData, setChartData] = useState<{ chartData1: DataType[]; chartData2: DataType[] }>({
|
||||
chartData1: [], chartData2: [{ label: "1", val: 15 },
|
||||
{ label: "2", val: 35 },
|
||||
{ label: "3", val: 55 },],
|
||||
chartData1: [], chartData2: [],
|
||||
});
|
||||
const [inftwtQty, setInftwtQty] = useState<number>(0); // 침입수량
|
||||
const aplcnYms = fn.useAplcnYms('inftwtAnlsRslt'); // 상수사용월 데이터 가져오기
|
||||
pplMngNo = "L"+pplMngNo; // "L13101003";
|
||||
console.log("pplMngNo", pplMngNo);
|
||||
@@ -51,15 +51,14 @@ const InfilChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal, pplMngNo
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen === false) {
|
||||
setPopupOpen(true); // 모달이 닫힐 때 조회조건팝업은 열어두기
|
||||
setPopupOpen(false); // 모달이 닫힐 때 조회조건팝업은 열어두기
|
||||
setAplcnYm(""); // 상수사용월 초기화
|
||||
setInftwtAnlss([]); // 침입수분석 초기화
|
||||
setInftwtAnlsInfos([{ val: 0, text: '선택' }]); // 침입수분석정보 초기화
|
||||
setInftwtAnls(null); // 침입수분석정보 초기화
|
||||
setInftwtAnlsSn(0); // 침입수분석 일련번호 초기화
|
||||
setChartData({chartData1: [], chartData2: [{ label: "1", val: 15 },
|
||||
{ label: "2", val: 35 },
|
||||
{ label: "3", val: 55 },],}); // 차트 데이터 초기화
|
||||
setChartData({chartData1: [], chartData2: [],}); // 차트 데이터 초기화
|
||||
setInftwtQty(0); // 침입수량 초기화
|
||||
}
|
||||
}, [isModalOpen]);
|
||||
|
||||
@@ -119,31 +118,45 @@ const InfilChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal, pplMngNo
|
||||
chartData2: [] // 유량 데이터 초기화
|
||||
}));
|
||||
});
|
||||
fn.selectPplInftwtQty(inftwtAnlsSn, pplMngNo).then((data) => {// 침입수량 조회
|
||||
if (data > 0) {
|
||||
setInftwtQty(data);
|
||||
}
|
||||
});
|
||||
setPopupOpen(false);
|
||||
};
|
||||
|
||||
const renderBody = () => (
|
||||
<>
|
||||
{/* <Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal} > */}
|
||||
{/* 분석 입력현황 조회 */}
|
||||
<div className="text-black text-3xl font-bold mb-5">침입수분석결과</div>
|
||||
|
||||
<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="bg-white px-4 py-2 ">
|
||||
<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">120</span>
|
||||
<span className="ml-2 text-black">m³/d</span>
|
||||
<span className="text-black text-xl font-bold">
|
||||
{(aplcnYm)? moment(aplcnYm, "YYYYMM").format("YYYY-MM"):""}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* 침입수량 분석결과 */}
|
||||
<div className="bg-white px-4 py-2 ">
|
||||
<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">{inftwtQty}</span>
|
||||
<span className="ml-2 text-black text-xl">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 className="w-[570px] h-[360px] bg-white text-2xl">
|
||||
<MainChart chartConfig={chartConfig} chartForm={chartForm} chartData={chartData} chartTitle={'관로유량'}
|
||||
yFieldText2="시간" yFieldText="유량"/>
|
||||
</div>
|
||||
</div>
|
||||
{popupOpen && (
|
||||
@@ -243,15 +256,17 @@ const InfilChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal, pplMngNo
|
||||
<UiModal
|
||||
open={isModalOpen}
|
||||
onOpenChange={setIsModalOpen}
|
||||
headerTitle=""
|
||||
bodyClass={`
|
||||
h-[50vh]
|
||||
w-[40vw]
|
||||
w-[50vw]
|
||||
max-w-none
|
||||
max-h-none
|
||||
overflow-visible
|
||||
z-[50]
|
||||
`}
|
||||
body={renderBody()}
|
||||
noFooterYn = 'Y'
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,20 +3,21 @@ import { CHART_FORM, type ChartConfig, MainChart, UiModal } from "@common/busine
|
||||
import { SelectBox } from '@common/business_components/ui';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import moment from "moment";
|
||||
|
||||
import type { TRdiiAnls, TRdiiAnlsRslt, TOilqData } from './aplcnYms';
|
||||
import { selectRdiiAnlsRslt, getChartDataRdiiAnlsRslt, getChartDataRdiiAnlsRsltMinq } from './aplcnYms';
|
||||
import { getOptionsRdiiAnlsInfo, selectRdiiAnls, useAplcnYms } from './aplcnYms';
|
||||
import * as fn from './aplcnYms';
|
||||
type ModalOpenProp = {
|
||||
isModalOpen: boolean,
|
||||
setIsModalOpen: (open: boolean) => void,
|
||||
closeModal: () => void,
|
||||
btnTxt?: string,
|
||||
pplMngNo?: string, // 관로관리번호
|
||||
pplMngNo: string, // 관로관리번호
|
||||
}
|
||||
type DataType = {label: string; val: number};
|
||||
|
||||
const RdiiChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal, pplMngNo }: ModalOpenProp) => {
|
||||
const [popupOpen, setPopupOpen] = React.useState(true);
|
||||
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("");
|
||||
@@ -24,11 +25,12 @@ const RdiiChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal, pplMngNo }
|
||||
const [rdiiAnlsInfos, setRdiiAnlsInfos] = useState<any[]>([{ val: 0, text: '선택' }]); // 강우유입수분석정보 options
|
||||
const [rdiiAnls, setRdiiAnls] = useState<TRdiiAnls | null>(null); // 강우유입수분석정보
|
||||
const [rainInfwtAnlsSn, setRainInfwtAnlsSn] = useState<number>(0); // 강우유입수분석 정보 선택시 사용
|
||||
const [chartData, setChartData] = useState<{ chartData1: DataType[]; chartData2: DataType[] }>({
|
||||
chartData1: [], chartData2: [],
|
||||
const [chartData, setChartData] = useState<{ chartData1: DataType[]; }>({
|
||||
chartData1: [], //chartData2: [],
|
||||
});
|
||||
|
||||
const aplcnYms = useAplcnYms('rdiiAnlsRslt'); // 상수사용월 데이터 가져오기
|
||||
const [rdiiQty, setRdiiQty] = useState<number>(0); // 강우유입수량
|
||||
const [totalVolume, setTotalVolume] = useState<number>(0); // 총체적 m³/mm
|
||||
const aplcnYms = fn.useAplcnYms('rdiiAnlsRslt'); // 상수사용월 데이터 가져오기
|
||||
pplMngNo = "L"+pplMngNo; // "L13101003";
|
||||
console.log("pplMngNo", pplMngNo);
|
||||
const openPopup = () => {
|
||||
@@ -41,23 +43,24 @@ const RdiiChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal, pplMngNo }
|
||||
}
|
||||
setPopupOpen(true); // 팝업 열기
|
||||
};
|
||||
//console.log("btnTxt", btnTxt);
|
||||
|
||||
const chartForm = CHART_FORM.LINE;
|
||||
const chartConfig = {
|
||||
chartData1: { label: 'BSF', color: "orange", },
|
||||
chartData2: { label: '강우유입수예측유량', color: "red", },
|
||||
chartData1: { label: 'RDII(m³/h)', color: "red", },
|
||||
//chartData2: { label: '순오수량(m³/h)', color: "orange", },
|
||||
} as ChartConfig;
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen === false) {
|
||||
setPopupOpen(true); // 모달이 닫힐 때 팝업은
|
||||
setPopupOpen(false); // 모달이 닫힐 때 팝업은
|
||||
setAplcnYm(""); // 상수사용월 초기화
|
||||
setRdiiAnlss([]); // 강우유입수분석 초기화
|
||||
setRdiiAnlsInfos([{ val: 0, text: '선택' }]); // 강우유입수분석정보 초기화
|
||||
setRdiiAnls(null); // 강우유입수분석정보 초기화
|
||||
setRainInfwtAnlsSn(0); // 강우유입수분석 일련번호 초기화
|
||||
setChartData({chartData1: [], chartData2: []}); // 차트 데이터 초기화
|
||||
setChartData({chartData1: [], }); // 차트 데이터 초기화
|
||||
setRdiiQty(0); // 강우유입수량 초기화
|
||||
setTotalVolume(0); // 총체적 m³/mm 초기화
|
||||
}
|
||||
}, [isModalOpen]);
|
||||
|
||||
@@ -69,11 +72,11 @@ const RdiiChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal, pplMngNo }
|
||||
setRdiiAnlsInfos([]);
|
||||
return;
|
||||
}
|
||||
|
||||
selectRdiiAnls(aplcnYm)
|
||||
console.log(moment(aplcnYm).format("YYYY-MM"));
|
||||
fn.selectRdiiAnls(aplcnYm)
|
||||
.then((data) => {
|
||||
setRdiiAnlss(data ?? []);
|
||||
setRdiiAnlsInfos(getOptionsRdiiAnlsInfo(data));
|
||||
setRdiiAnlss(data ?? []); // 선택한 월의 분석성공한 RDII분석데이터[]
|
||||
setRdiiAnlsInfos(fn.getOptionsRdiiAnlsInfo(data));
|
||||
setRdiiAnls(null); // 오수전환율 이후 초기화
|
||||
setRainInfwtAnlsSn(0); // 초기화
|
||||
})
|
||||
@@ -95,41 +98,57 @@ const RdiiChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal, pplMngNo }
|
||||
}, [rainInfwtAnlsSn]);
|
||||
|
||||
const closePopup = () => { // 팝업 닫기
|
||||
selectRdiiAnlsRslt(rainInfwtAnlsSn, pplMngNo).then((data) => {// 강우유입수분석 결과 조회
|
||||
const rsltData1 = getChartDataRdiiAnlsRsltMinq(data ?? []);
|
||||
const rsltData2 = getChartDataRdiiAnlsRslt(data ?? []);
|
||||
fn.selectRdiiAnlsRslt(rainInfwtAnlsSn, pplMngNo).then((data) => {// 강우유입수분석 결과 조회
|
||||
const rsltData1 = fn.getChartDataRdiiAnlsRslt(data ?? []);
|
||||
//const rsltData2 = fn.getChartDataRdiiAnlsRsltMinq(data ?? []);
|
||||
setChartData(prev => ({
|
||||
chartData1: rsltData1, // BSF
|
||||
chartData2: rsltData2 // 강우유입수분석결과
|
||||
chartData1: rsltData1, // 순오수량(m³/h)
|
||||
// chartData2: rsltData2 // RDII(m³/h) 강우유입수분석결과
|
||||
}));
|
||||
});
|
||||
|
||||
fn.selectPplRdiiQty(rainInfwtAnlsSn, pplMngNo).then((data) => {// 강우유입수량 조회
|
||||
setRdiiQty(Math.round(data.rdiih*24)); // cmd: m³/d 단위로 변환
|
||||
setTotalVolume(Math.round(data.rdiih * data.sustaintime)); // 총체적 m³/mm
|
||||
});
|
||||
setPopupOpen(false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const renderBody = () => (
|
||||
<>
|
||||
{/* 분석 입력현황 조회 */}
|
||||
<div className="text-black text-3xl font-bold mb-5">RDII분석결과</div>
|
||||
|
||||
<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="bg-white px-4 py-2 ">
|
||||
<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">120</span>
|
||||
<span className="text-black text-xl font-bold">
|
||||
{(aplcnYm)? moment(aplcnYm, "YYYYMM").format("YYYY-MM"):""}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 강우유입수량 분석결과 */}
|
||||
<div className="bg-white px-4 py-2 ">
|
||||
<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">{rdiiQty}</span>
|
||||
<span className="ml-2 text-black">m³/d</span>
|
||||
</div>
|
||||
<div className="flex items-center mt-2">
|
||||
<span className="text-black text-xl font-bold">{totalVolume}</span>
|
||||
<span className="ml-2 text-black">m³/mm</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 차트 */}
|
||||
<div className="w-[500px] h-[300px] bg-white p-4">
|
||||
<MainChart chartConfig={chartConfig} chartForm={chartForm} chartData={chartData} chartTitle={'관로유량'} />
|
||||
<div className="w-[570px] h-[360px] bg-white text-2xl">
|
||||
<MainChart chartConfig={chartConfig} chartForm={chartForm} chartData={chartData} chartTitle={'관로유량'}
|
||||
yFieldText2="시간" yFieldText="유량" />
|
||||
</div>
|
||||
</div>
|
||||
{popupOpen && (
|
||||
@@ -229,15 +248,17 @@ const RdiiChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal, pplMngNo }
|
||||
<UiModal
|
||||
open={isModalOpen}
|
||||
onOpenChange={setIsModalOpen}
|
||||
headerTitle=""
|
||||
bodyClass={`
|
||||
h-[50vh]
|
||||
w-[40vw]
|
||||
w-[50vw]
|
||||
max-w-none
|
||||
max-h-none
|
||||
overflow-visible
|
||||
z-[50]
|
||||
`}
|
||||
body={renderBody()}
|
||||
noFooterYn = 'Y'
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { getData } from '@/components/lib/api'
|
||||
import ContentLayout from '@/components/ui/contentLayout'
|
||||
import { GovSidoSigunguSearchForm } from '@/components/ui/govSidoSigunguSearchForm'
|
||||
import { DatePicker, Form, SearchFormRight, SelectBox } from '@common/business_components/ui'
|
||||
import { DatePicker, DateMonthPicker, Form, SearchFormRight, SelectBox } from '@common/business_components/ui'
|
||||
import ListDiv from '@common/business_components/ui/list_div'
|
||||
import { SelectBox2 } from '@common/business_components/ui/select/index-2'
|
||||
import { Button, Label } from '@common/components/ui'
|
||||
@@ -51,8 +51,10 @@ type BaseLayout = {
|
||||
onSwageTreatmentPlantChange?: (value: string | number) => void;
|
||||
selectedSwageProcessingZone?: string | number;
|
||||
onSwageProcessingZoneChange?: (value: string | number) => void;
|
||||
aplcnYm?: Date
|
||||
setAplcnYm?: (aplcnYm: Date | undefined) => void;
|
||||
aplcnYmFrom?: string
|
||||
setAplcnYmFrom?: (aplcnYmFrom: string) => void;
|
||||
aplcnYmTo?: string
|
||||
setAplcnYmTo?: (aplcnYmTo: string) => void;
|
||||
isLoading?: boolean;
|
||||
// Excel 다운로드 관련 props 추가
|
||||
onExcelDownload?: () => void;
|
||||
@@ -89,8 +91,10 @@ const BaseLayout = ({
|
||||
onSwageTreatmentPlantChange,
|
||||
selectedSwageProcessingZone,
|
||||
onSwageProcessingZoneChange,
|
||||
aplcnYm,
|
||||
setAplcnYm,
|
||||
aplcnYmFrom,
|
||||
setAplcnYmFrom,
|
||||
aplcnYmTo,
|
||||
setAplcnYmTo,
|
||||
isLoading,
|
||||
// Excel 다운로드 관련 props 추가
|
||||
onExcelDownload,
|
||||
@@ -99,7 +103,7 @@ const BaseLayout = ({
|
||||
selectedRowData
|
||||
}: BaseLayout) => {
|
||||
|
||||
const defaultOption: ApiSelectOption = { val: '0', text: '선택' };
|
||||
const defaultOption: ApiSelectOption = { val: '0', text: '전체' };
|
||||
|
||||
const [processingZoneOptions, setProcessingZoneOptions] = useState<ApiSelectOption[]>([]);
|
||||
const [treatmentFacilityOptions, setTreatmentFacilityOptions] = useState<ApiSelectOption[]>([]);
|
||||
@@ -216,13 +220,37 @@ const BaseLayout = ({
|
||||
{showDatePicker && ( // showDatePicker 값에 따라 DatePicker 렌더링
|
||||
<>
|
||||
<Label className="ml-4 w-20 text-xl text-gray-600 whitespace-nowrap">분석기간</Label>
|
||||
<DatePicker
|
||||
mode={"single"}
|
||||
<DateMonthPicker
|
||||
field={{
|
||||
value: aplcnYm ?? new Date(),
|
||||
onChange: setAplcnYm ?? ((date: Date | undefined) => console.log("DatePicker onChange:", date))
|
||||
value: aplcnYmFrom || (() => { // aplcnYm이 없으면 현재 연월을 기본값으로 설정
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
})(), // yyyyMM 형식
|
||||
onChange: (value: string) => {
|
||||
setAplcnYmFrom?.(value); // string 값을 직접 전달
|
||||
}
|
||||
}}
|
||||
selectClass="ml-5 date-picker-trigger" />
|
||||
selectClass="ml-5 w-[100px]"
|
||||
startYear={1900} // 시작 연도 설정
|
||||
/>
|
||||
<span className="font-semibold text-xl mx-2">~</span>
|
||||
<DateMonthPicker
|
||||
field={{
|
||||
value: aplcnYmTo || (() => { // aplcnYm이 없으면 현재 연월을 기본값으로 설정
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
})(), // yyyyMM 형식
|
||||
onChange: (value: string) => {
|
||||
setAplcnYmTo?.(value); // string 값을 직접 전달
|
||||
}
|
||||
}}
|
||||
selectClass="ml-5 w-[100px]"
|
||||
startYear={1900} // 시작 연도 설정
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<GovSidoSigunguSearchForm
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { postData } from '@/components/lib/api'
|
||||
import FileUploadForm from '@/components/ui/fileUploadForm'
|
||||
import FormSection from '@/components/ui/formSection'
|
||||
import { DatePicker, filePicked } from '@common/business_components/ui'
|
||||
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'
|
||||
@@ -59,7 +59,13 @@ const IrSetting = () => {
|
||||
initialFileInfo // 3: 유량자료 등록
|
||||
]);
|
||||
|
||||
const [selectedDate, setSelectedDate] = useState(new Date());
|
||||
// selectedDate를 yyyyMM 문자열 형식으로 변경
|
||||
const [selectedDate, setSelectedDate] = useState(() => {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
});
|
||||
const [input1, setInput1] = useState('');
|
||||
const [input2, setInput2] = useState('');
|
||||
const [select1, setSelect1] = useState('0'); // 환경청코드
|
||||
@@ -87,7 +93,11 @@ const IrSetting = () => {
|
||||
initialFileInfo,
|
||||
initialFileInfo
|
||||
]);
|
||||
setSelectedDate(new Date()); // 날짜 선택 초기화
|
||||
// selectedDate를 현재 연월로 초기화
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
setSelectedDate(`${year}${month}`);
|
||||
setInput1(''); // 입력 필드 초기화
|
||||
setInput2(''); // 입력 필드 초기화
|
||||
setSelect1('0'); // 선택 필드 초기화
|
||||
@@ -138,22 +148,6 @@ const IrSetting = () => {
|
||||
const handleAnalysisClick = async () => {
|
||||
setIsLoadingResult(true); // 로딩 상태 시작
|
||||
|
||||
// 오수전환율 값 0 ~ 1 사이인지 검증
|
||||
if (input1 === '' || input1 === null || input1 === undefined) {
|
||||
setIsLoadingResult(false);
|
||||
console.error('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
alert('오수전환율을 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const numericInput1 = parseFloat(input1);
|
||||
if (isNaN(numericInput1) || numericInput1 < 0 || numericInput1 > 1) {
|
||||
setIsLoadingResult(false);
|
||||
console.error('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
alert('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(uploadedFilesData);
|
||||
const file1 = uploadedFilesData[0]; // 오수관망도
|
||||
const file2 = uploadedFilesData[1]; // 수용가-맨홀 매칭결과
|
||||
@@ -173,6 +167,22 @@ const IrSetting = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 오수전환율 값 0 ~ 1 사이인지 검증
|
||||
if (input1 === '' || input1 === null || input1 === undefined) {
|
||||
setIsLoadingResult(false);
|
||||
console.error('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
alert('오수전환율을 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const numericInput1 = parseFloat(input1);
|
||||
if (isNaN(numericInput1) || numericInput1 < 0 || numericInput1 > 1) {
|
||||
setIsLoadingResult(false);
|
||||
console.error('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
alert('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
type MatchingResult = { anlsSn: number; mhlMngNo: string; rcptrNo: string; };
|
||||
type TimePattern = { hrId: number; rt?: number | null | undefined; };
|
||||
type FlowRate = { pplMngNo: string; mtrngDt: string; oilq?: number | null | undefined; };
|
||||
@@ -236,7 +246,7 @@ const IrSetting = () => {
|
||||
swtrmtZoneCd: (select5 && select5 !== '0' && select5 !== 0)
|
||||
? select5.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리구역코드 (선택값)
|
||||
aplcnYm: new Date(selectedDate).toISOString().substring(0, 7).replace("-", ""), // 적용연월
|
||||
aplcnYm: selectedDate, // 적용연월
|
||||
drtwSwtcRt: parseFloat(input1) || null, // 오수전환율 (숫자로 변환)
|
||||
anlsInfo: input2, // 분석정보
|
||||
atflCd: file1?.atflCd, // 첨부파일CD (오수관망도)
|
||||
@@ -316,19 +326,17 @@ const IrSetting = () => {
|
||||
<FormSection
|
||||
name='inputdate'
|
||||
form={form}
|
||||
titleNm='▷ 상수사용량 적용기간'>
|
||||
<DatePicker
|
||||
mode={"single"}
|
||||
titleNm='▷ 상수사용량 적용월'>
|
||||
{/* 연월 선택 라이브러리로 수정 필요 */}
|
||||
<DateMonthPicker
|
||||
field={{
|
||||
value: selectedDate, // 현재 페이지의 selectedDate 상태 사용
|
||||
onChange: (date: Date | undefined) => {
|
||||
if (date) {
|
||||
setSelectedDate(date);
|
||||
form.setValue('inputdate', date, { shouldValidate: true });
|
||||
}
|
||||
value: selectedDate, // yyyyMM 형식
|
||||
onChange: (value: string) => {
|
||||
setSelectedDate(value);
|
||||
}
|
||||
}}
|
||||
selectClass="ml-5 date-picker-trigger" // date-picker.css의 스타일 적용
|
||||
selectClass="ml-5 w-[150px]"
|
||||
startYear={1900} // 시작 연도 설정
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection
|
||||
|
||||
@@ -4,7 +4,7 @@ import { postData } from '@/components/lib/api'
|
||||
import FileUploadForm from "@/components/ui/fileUploadForm"
|
||||
import FormSection from "@/components/ui/formSection"
|
||||
import BaseLayout from "../BaseLayout"
|
||||
import { DatePicker, filePicked } from "@common/business_components/ui"
|
||||
import { DateMonthPicker, filePicked } from "@common/business_components/ui"
|
||||
import { Input } from "@common/components/ui"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { Label } from '@radix-ui/react-label'
|
||||
@@ -38,11 +38,11 @@ const formSchema = z.object({
|
||||
})),
|
||||
// osumap: z.instanceof(File).optional(), // 오수관망도
|
||||
// matching: z.instanceof(File).optional(), // 수용가-맨홀 매칭결과
|
||||
aplcnYm: z.string(), // 상수사용량 적용기간 (YYYYMM 형식)
|
||||
aplcnYm: z.string(), // 상수사용량 적용월 (YYYYMM 형식)
|
||||
rate: z.string(), // 오수전환율
|
||||
// rain: z.instanceof(File).optional(), // 강우자료
|
||||
analysisInfo: z.string().optional(), // 분석 정보
|
||||
date: z.date(),
|
||||
date: z.string(), // Date 객체에서 string(yyyyMM 형식)으로 변경
|
||||
// date: z.coerce.date(),
|
||||
})
|
||||
// zod 스키마 타입 추론
|
||||
@@ -72,7 +72,12 @@ const defaultFormValues: FormSchemaType = {
|
||||
rate: "0.8", // 문자열로 기본값 설정
|
||||
// rainFile: undefined,
|
||||
analysisInfo: "test 분석정보",
|
||||
date: new Date(),
|
||||
date: (() => { // 현재 연월을 yyyyMM 형식으로 설정
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
})(),
|
||||
};
|
||||
|
||||
// 각 파일 업로드 폼의 데이터를 저장할 인터페이스 정의
|
||||
@@ -108,7 +113,6 @@ export default function RTKForm() {
|
||||
const [select4, setSelect4] = useState<string | number>('0'); // 하수처리장 (swageTmplntCd)
|
||||
const [select5, setSelect5] = useState<string | number>('0'); // 하수처리구역 (swtrmtZoneCd)
|
||||
|
||||
|
||||
// form 인스턴스 생성
|
||||
const form = useForm<FormSchemaType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
@@ -163,23 +167,6 @@ export default function RTKForm() {
|
||||
// 침입수 분석 실행 버튼 클릭 핸들러
|
||||
const handleAnalysisClick = async () => {
|
||||
setIsLoadingResult(true); // 로딩 상태 시작
|
||||
|
||||
// 오수전환율 값 0 ~ 1 사이인지 검증
|
||||
const rateValue = form.getValues().rate;
|
||||
if (rateValue === '' || rateValue === null || rateValue === undefined) {
|
||||
setIsLoadingResult(false);
|
||||
console.error('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
alert('오수전환율을 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const numericInput1 = parseFloat(rateValue);
|
||||
if (isNaN(numericInput1) || numericInput1 < 0 || numericInput1 > 1) {
|
||||
setIsLoadingResult(false);
|
||||
console.error('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
alert('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = form.getValues();
|
||||
console.log(uploadedFilesData);
|
||||
@@ -198,6 +185,23 @@ export default function RTKForm() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 오수전환율 값 0 ~ 1 사이인지 검증
|
||||
const rateValue = form.getValues().rate;
|
||||
if (rateValue === '' || rateValue === null || rateValue === undefined) {
|
||||
setIsLoadingResult(false);
|
||||
console.error('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
alert('오수전환율을 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const numericInput1 = parseFloat(rateValue);
|
||||
if (isNaN(numericInput1) || numericInput1 < 0 || numericInput1 > 1) {
|
||||
setIsLoadingResult(false);
|
||||
console.error('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
alert('오수전환율은 0에서 1 사이의 값으로 입력해야 합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
const filePickers = [
|
||||
{ file: file2, parser: excelToMatchingResultData },
|
||||
{ file: file3, parser: excelToRainData },
|
||||
@@ -241,7 +245,7 @@ export default function RTKForm() {
|
||||
// 새로운 JSON 데이터 생성
|
||||
const combinedAnalysisData = {
|
||||
enoCd: (select1 && select1 !== '0') ? select1.toString() : null, // 환경청코드 (선택값)
|
||||
aplcnYm: new Date(formData.date).toISOString().substring(0, 7).replace("-", ""), // 적용연월 (YYYYMM 형식)
|
||||
aplcnYm: formData.date, // 적용연월 (YYYYMM 형식)
|
||||
ctpvCd: (select2 && select2 !== '0') ? select2.toString() : null, // 시도코드 (선택값)
|
||||
sggCd: (select3.val && select3.val !== '0') ? select3.val.toString() : null, // 시군구코드 (선택값)
|
||||
swageTmplntCd: (select4 && select4 !== '0' && select4 !== 0)
|
||||
@@ -355,7 +359,7 @@ export default function RTKForm() {
|
||||
selectedSwageTreatmentPlant={select4}
|
||||
onSwageTreatmentPlantChange={setSelect4}
|
||||
selectedSwageProcessingZone={select5}
|
||||
onSwageProcessingZoneChange={setSelect5}
|
||||
onSwageProcessingZoneChange={setSelect4}
|
||||
isLoading={isLoadingResult}
|
||||
onAnalysis={handleAnalysisClick}
|
||||
>
|
||||
@@ -447,30 +451,16 @@ export default function RTKForm() {
|
||||
placeholder="예: 0.75"
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection name='date' form={form} titleNm='○ 상수사용량 적용기간'>
|
||||
{/* <DatePicker
|
||||
selected={form.watch("date")}
|
||||
onSelect={(date: Date | undefined) => {
|
||||
if (date) {
|
||||
form.setValue("date", date, { shouldValidate: true });
|
||||
}
|
||||
}}
|
||||
mode={"single"}
|
||||
className="ml-5"
|
||||
/> */}
|
||||
<DatePicker
|
||||
mode={"single"} // mode는 DayPickerSingleProps에서 오므로 그대로 사용 가능
|
||||
<FormSection name='date' form={form} titleNm='○ 상수사용량 적용월'>
|
||||
<DateMonthPicker
|
||||
field={{
|
||||
value: form.watch("date"), // react-hook-form의 "date" 필드 값 사용
|
||||
onChange: (date: Date | undefined) => {
|
||||
if (date) {
|
||||
form.setValue("date", date, { shouldValidate: true });
|
||||
}
|
||||
value: form.watch("date"), // form의 date 값을 직접 사용
|
||||
onChange: (value: string) => {
|
||||
form.setValue("date", value, { shouldValidate: true }); // form의 date 필드에 직접 설정
|
||||
}
|
||||
}}
|
||||
// className을 직접 사용하거나, DatePicker 내부에서 selectClass를 처리하도록 수정 필요
|
||||
// 여기서는 DatePicker의 selectClass prop을 사용하고, date-picker.css의 스타일을 적용
|
||||
selectClass="ml-5 date-picker-trigger"
|
||||
selectClass="ml-5 w-[200px]"
|
||||
startYear={1900} // 시작 연도 설정
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection name='rate' form={form} titleNm='○ 분석 정보'>
|
||||
|
||||
@@ -18,12 +18,24 @@ const formSchema = z.object({
|
||||
}).default({ val: '0', text: '전체' }),
|
||||
swageTmplntCd: z.string().default('0'), // 하수처리장
|
||||
swtrmtZoneCd: z.string().default('0'), // 하수처리구역
|
||||
aplcnYm: z.date().default(new Date()), // 분석일자 (DatePicker와 연동)
|
||||
aplcnYmFrom: z.string().default(() => { // Date에서 string(yyyyMM 형식)으로 변경
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
}), // 분석기간 시작연월
|
||||
aplcnYmTo: z.string().default(() => { // Date에서 string(yyyyMM 형식)으로 변경
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
}), // 분석기간 종료연월
|
||||
})
|
||||
|
||||
type ResultFormValues = z.infer<typeof formSchema>
|
||||
|
||||
const tableColumn = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
{ id: 'enoNm', title: '환경청' },
|
||||
{ id: 'ctpvNm', title: '시도' },
|
||||
{ id: 'sggNm', title: '시군구' },
|
||||
@@ -35,7 +47,8 @@ const tableColumn = [
|
||||
{ id: 'cntbArea', title: 'RDII 기여면적' },
|
||||
{ id: 'anlsInfo', title: 'RDII 분석정보' },
|
||||
{ id: 'regDt', title: '분석일자' },
|
||||
{ id: 'rgtrNm', title: '분석자' }
|
||||
{ id: 'rgtrNm', title: '분석자' },
|
||||
{ id: 'scsYn', title: '분석성공여부' }
|
||||
]
|
||||
|
||||
const Result = () => {
|
||||
@@ -47,7 +60,18 @@ const Result = () => {
|
||||
sggCd: { val: '0', text: '전체' },
|
||||
swageTmplntCd: '0',
|
||||
swtrmtZoneCd: '0',
|
||||
aplcnYm: new Date(),
|
||||
aplcnYmFrom: (() => { // 현재 연월을 yyyyMM 형식으로 설정
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
})(),
|
||||
aplcnYmTo: (() => { // 현재 연월을 yyyyMM 형식으로 설정
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
})(),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -67,21 +91,32 @@ const Result = () => {
|
||||
|
||||
let inftwtAnlsSearchDto;
|
||||
|
||||
// swageTmplntCd 처리 로직 추가
|
||||
const processedSwageTmplntCd = formValues.swageTmplntCd && formValues.swageTmplntCd !== '0'
|
||||
? (() => {
|
||||
const valString = String(formValues.swageTmplntCd);
|
||||
const parts = valString.split('.');
|
||||
return parts.length > 1 ? parts[1] : valString;
|
||||
})()
|
||||
: null;
|
||||
|
||||
if (customFormValues) {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm,
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom,
|
||||
"aplcnYmTo": formValues.aplcnYmTo,
|
||||
"enoCd": formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
};
|
||||
} else {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm ? formValues.aplcnYm.toISOString().substring(0, 7).replace("-", "") : null,
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
|
||||
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
};
|
||||
}
|
||||
console.log("Search DTO:", inftwtAnlsSearchDto);
|
||||
@@ -101,10 +136,18 @@ const Result = () => {
|
||||
console.log("API: ", `v1/rdiiAnls${queryString ? `?${queryString}` : ''}`);
|
||||
try {
|
||||
const data = await getData(`v1/rdiiAnls${queryString ? `?${queryString}` : ''}`);
|
||||
setTableData(data.content || []);
|
||||
setTotalData(tableData.length);
|
||||
// 일련번호를 추가한 데이터로 변환
|
||||
const dataWithRowNumber = (data.content || []).map((item: any, index: number) => ({
|
||||
...item,
|
||||
rowNumber: index + 1, // 1부터 시작하는 일련번호 추가
|
||||
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn // scsYn이 null이면 'N'으로 변경
|
||||
}));
|
||||
// setTableData(data.content || []);
|
||||
// setTotalData(tableData.length);
|
||||
setTableData(dataWithRowNumber);
|
||||
setTotalData(dataWithRowNumber.length);
|
||||
console.log("response:", data);
|
||||
console.log("response data:", data.content || []);
|
||||
console.log("response data:", dataWithRowNumber);
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
@@ -198,11 +241,12 @@ const Result = () => {
|
||||
|
||||
// 삭제 후 테이블 새로고침을 위해 재검색
|
||||
const customFormValues: Partial<ResultFormValues> = {
|
||||
aplcnYm: selectedRowData.aplcnYm || null,
|
||||
aplcnYmFrom: selectedRowData.aplcnYmFrom || null,
|
||||
aplcnYmTo: selectedRowData.aplcnYmTo || null,
|
||||
enoCd: selectedRowData.enoCd || null,
|
||||
ctpvCd: selectedRowData.ctpvCd || null,
|
||||
sggCd: selectedRowData.sggCd || null,
|
||||
swtrmtZoneCd: selectedRowData.swtrmtZoneCd || null,
|
||||
swageTmplntCd: selectedRowData.swageTmplntCd || null,
|
||||
};
|
||||
await handleSearchButtonClick(customFormValues);
|
||||
|
||||
@@ -241,14 +285,10 @@ const Result = () => {
|
||||
onSwageTreatmentPlantChange={(val) => form.setValue("swageTmplntCd", String(val))}
|
||||
selectedSwageProcessingZone={form.watch("swtrmtZoneCd")}
|
||||
onSwageProcessingZoneChange={(val) => form.setValue("swtrmtZoneCd", String(val))}
|
||||
aplcnYm={form.watch("aplcnYm")}
|
||||
setAplcnYm={(val) => {
|
||||
if (val instanceof Date) {
|
||||
form.setValue("aplcnYm", val)
|
||||
} else if (val) {
|
||||
form.setValue("aplcnYm", new Date(val))
|
||||
}
|
||||
}}
|
||||
aplcnYmFrom={form.watch("aplcnYmFrom")}
|
||||
setAplcnYmFrom={(val: string) => {form.setValue("aplcnYmFrom", val)}}
|
||||
aplcnYmTo={form.watch("aplcnYmTo")}
|
||||
setAplcnYmTo={(val: string) => {form.setValue("aplcnYmTo", val)}}
|
||||
isLoading={isLoadingResult}
|
||||
// Excel 다운로드 핸들러 추가
|
||||
onExcelDownload={handleExcelDownload}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import { deleteData, getData } from '@/components/lib/api'
|
||||
import { UiTable, downloadExcelWithKoreanHeaders } from '@common/business_components/ui'
|
||||
import { UiTable, downloadExcel, downloadExcelWithKoreanHeaders } from '@common/business_components/ui'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
@@ -18,12 +18,24 @@ const formSchema = z.object({
|
||||
}).default({ val: '0', text: '전체' }),
|
||||
swageTmplntCd: z.string().default('0'), // 하수처리장
|
||||
swtrmtZoneCd: z.string().default('0'), // 하수처리구역
|
||||
aplcnYm: z.date().default(new Date()), // 분석일자 (DatePicker와 연동)
|
||||
aplcnYmFrom: z.string().default(() => { // Date에서 string(yyyyMM 형식)으로 변경
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
}), // 분석기간 시작연월
|
||||
aplcnYmTo: z.string().default(() => { // Date에서 string(yyyyMM 형식)으로 변경
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
}), // 분석기간 종료연월
|
||||
})
|
||||
|
||||
type ResultFormValues = z.infer<typeof formSchema>
|
||||
|
||||
const tableColumn = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
{ id: 'enoNm', title: '환경청' },
|
||||
{ id: 'ctpvNm', title: '시도' },
|
||||
{ id: 'sggNm', title: '시군구' },
|
||||
@@ -33,7 +45,8 @@ const tableColumn = [
|
||||
{ id: 'drtwSwtcRt', title: '오수 전환율' },
|
||||
{ id: 'anlsInfo', title: '침입수 분석정보' },
|
||||
{ id: 'regDt', title: '분석일자' },
|
||||
{ id: 'rgtrNm', title: '분석자' }
|
||||
{ id: 'rgtrNm', title: '분석자' },
|
||||
{ id: 'scsYn', title: '분석성공여부' }
|
||||
]
|
||||
|
||||
const Result = () => {
|
||||
@@ -45,7 +58,18 @@ const Result = () => {
|
||||
sggCd: { val: '0', text: '전체' },
|
||||
swageTmplntCd: '0',
|
||||
swtrmtZoneCd: '0',
|
||||
aplcnYm: new Date(),
|
||||
aplcnYmFrom: (() => { // 현재 연월을 yyyyMM 형식으로 설정
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
})(),
|
||||
aplcnYmTo: (() => { // 현재 연월을 yyyyMM 형식으로 설정
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
return `${year}${month}`;
|
||||
})(),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -65,21 +89,34 @@ const Result = () => {
|
||||
|
||||
let inftwtAnlsSearchDto;
|
||||
|
||||
// swageTmplntCd 처리 로직 추가
|
||||
const processedSwageTmplntCd = formValues.swageTmplntCd && formValues.swageTmplntCd !== '0'
|
||||
? (() => {
|
||||
const valString = String(formValues.swageTmplntCd);
|
||||
const parts = valString.split('.');
|
||||
return parts.length > 1 ? parts[1] : valString;
|
||||
})()
|
||||
: null;
|
||||
|
||||
if (customFormValues) {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm,
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom,
|
||||
"aplcnYmTo": formValues.aplcnYmTo,
|
||||
"enoCd": formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
// "swtrmtZoneCd": formValues.swtrmtZoneCd,
|
||||
};
|
||||
} else {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm ? formValues.aplcnYm.toISOString().substring(0, 7).replace("-", "") : null,
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
|
||||
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
// "swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd,
|
||||
};
|
||||
}
|
||||
console.log("Search DTO:", inftwtAnlsSearchDto);
|
||||
@@ -99,10 +136,18 @@ const Result = () => {
|
||||
console.log("API: ", `v1/inftwtAnls${queryString ? `?${queryString}` : ''}`);
|
||||
try {
|
||||
const data = await getData(`v1/inftwtAnls${queryString ? `?${queryString}` : ''}`);
|
||||
setTableData(data.content || []);
|
||||
setTotalData(tableData.length);
|
||||
// 일련번호를 추가한 데이터로 변환
|
||||
const dataWithRowNumber = (data.content || []).map((item: any, index: number) => ({
|
||||
...item,
|
||||
rowNumber: index + 1, // 1부터 시작하는 일련번호 추가
|
||||
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn // scsYn이 null이면 'N'으로 변경
|
||||
}));
|
||||
// setTableData(data.content || []);
|
||||
// setTotalData(tableData.length);
|
||||
setTableData(dataWithRowNumber);
|
||||
setTotalData(dataWithRowNumber.length);
|
||||
console.log("response:", data);
|
||||
console.log("response data:", data.content || []);
|
||||
console.log("response data:", dataWithRowNumber);
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
@@ -198,11 +243,13 @@ const Result = () => {
|
||||
|
||||
// 삭제 후 테이블 새로고침을 위해 재검색
|
||||
const customFormValues: Partial<ResultFormValues> = {
|
||||
aplcnYm: selectedRowData.aplcnYm || null,
|
||||
aplcnYmFrom: selectedRowData.aplcnYmFrom || null,
|
||||
aplcnYmTo: selectedRowData.aplcnYmTo || null,
|
||||
enoCd: selectedRowData.enoCd || null,
|
||||
ctpvCd: selectedRowData.ctpvCd || null,
|
||||
sggCd: selectedRowData.sggCd || null,
|
||||
swtrmtZoneCd: selectedRowData.swtrmtZoneCd || null,
|
||||
swageTmplntCd: selectedRowData.swageTmplntCd || null,
|
||||
// swtrmtZoneCd: selectedRowData.swtrmtZoneCd || null,
|
||||
};
|
||||
await handleSearchButtonClick(customFormValues);
|
||||
|
||||
@@ -241,14 +288,10 @@ const Result = () => {
|
||||
onSwageTreatmentPlantChange={(val) => form.setValue("swageTmplntCd", String(val))}
|
||||
selectedSwageProcessingZone={form.watch("swtrmtZoneCd")}
|
||||
onSwageProcessingZoneChange={(val) => form.setValue("swtrmtZoneCd", String(val))}
|
||||
aplcnYm={form.watch("aplcnYm")}
|
||||
setAplcnYm={(val) => {
|
||||
if (val instanceof Date) {
|
||||
form.setValue("aplcnYm", val)
|
||||
} else if (val) {
|
||||
form.setValue("aplcnYm", new Date(val))
|
||||
}
|
||||
}}
|
||||
aplcnYmFrom={form.watch("aplcnYmFrom")}
|
||||
setAplcnYmFrom={(val: string) => {form.setValue("aplcnYmFrom", val)}}
|
||||
aplcnYmTo={form.watch("aplcnYmTo")}
|
||||
setAplcnYmTo={(val: string) => {form.setValue("aplcnYmTo", val)}}
|
||||
isLoading={isLoadingResult} // 로딩 상태 전달
|
||||
// Excel 다운로드 핸들러 추가
|
||||
onExcelDownload={handleExcelDownload}
|
||||
|
||||
+43
-44
@@ -1,51 +1,50 @@
|
||||
"use client";
|
||||
import { Button } from "@common/components";
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function Home() {
|
||||
//redirect("/map");
|
||||
redirect("/map");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<h3 className="text-bold mx-4 gap-4 text-2xl">하수관망도</h3>
|
||||
<div className="flex flex-row gap-4 p-4">
|
||||
<Link href={"/map"}>
|
||||
<Button>하수관망도</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-bold mx-4 gap-4 text-2xl">운영정보 관리</h3>
|
||||
<div className="flex flex-row gap-4 p-4">
|
||||
<Link href={"/oper/floodPre"}>
|
||||
<Button>도시침수 예방사업</Button>
|
||||
</Link>
|
||||
<Link href={"/oper/oldPp"}>
|
||||
<Button>노후 하수관로 정비사업</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-bold mx-4 gap-4 text-2xl">
|
||||
I/I 분석(기초정보 설정)
|
||||
</h3>
|
||||
<div className="flex flex-row gap-4 p-4">
|
||||
<Link href={"/ii/irsetting"}>
|
||||
<Button>침입수 분석</Button>
|
||||
</Link>
|
||||
<Link href={"/ii/rdii"}>
|
||||
<Button>RDII 분석</Button>
|
||||
</Link>
|
||||
<Link href={"/ii/result"}>
|
||||
<Button>침입수 분석 결과</Button>
|
||||
</Link>
|
||||
<Link href={"/ii/rdiiResult"}>
|
||||
<Button>RDII 분석 결과</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// return (
|
||||
// <div>
|
||||
// <div>
|
||||
// <h3 className="text-bold mx-4 gap-4 text-2xl">하수관망도</h3>
|
||||
// <div className="flex flex-row gap-4 p-4">
|
||||
// <Link href={"/map"}>
|
||||
// <Button>하수관망도</Button>
|
||||
// </Link>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div>
|
||||
// <h3 className="text-bold mx-4 gap-4 text-2xl">운영정보 관리</h3>
|
||||
// <div className="flex flex-row gap-4 p-4">
|
||||
// <Link href={"/oper/floodPre"}>
|
||||
// <Button>도시침수 예방사업</Button>
|
||||
// </Link>
|
||||
// <Link href={"/oper/oldPp"}>
|
||||
// <Button>노후 하수관로 정비사업</Button>
|
||||
// </Link>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div>
|
||||
// <h3 className="text-bold mx-4 gap-4 text-2xl">
|
||||
// I/I 분석(기초정보 설정)
|
||||
// </h3>
|
||||
// <div className="flex flex-row gap-4 p-4">
|
||||
// <Link href={"/ii/irsetting"}>
|
||||
// <Button>침입수 분석</Button>
|
||||
// </Link>
|
||||
// <Link href={"/ii/rdii"}>
|
||||
// <Button>RDII 분석</Button>
|
||||
// </Link>
|
||||
// <Link href={"/ii/result"}>
|
||||
// <Button>침입수 분석 결과</Button>
|
||||
// </Link>
|
||||
// <Link href={"/ii/rdiiResult"}>
|
||||
// <Button>RDII 분석 결과</Button>
|
||||
// </Link>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"chart.js": "^4.4.8",
|
||||
"dayjs": "^1.11.13",
|
||||
"js-cookie": "^3.0.5",
|
||||
"moment": "^2.30.1",
|
||||
"next": "15.1.7",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from "./lib/chart";
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
/*
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
@@ -17,6 +18,22 @@ const customStyles = {
|
||||
marginTop: "10px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
*/
|
||||
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "12px",
|
||||
fontSize: "13px",
|
||||
fontWeight: "normal",
|
||||
marginTop: "16px",
|
||||
marginBottom: "8px",
|
||||
minHeight: "32px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
|
||||
type BarChartProps = {
|
||||
chartConfig: ChartConfig | undefined;
|
||||
@@ -60,7 +77,7 @@ const CustomTooltip = ({
|
||||
};
|
||||
|
||||
// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가)
|
||||
const CustomLegend = (props: any) => {
|
||||
/*const CustomLegend = (props: any) => {
|
||||
const { payload } = props;
|
||||
return (
|
||||
<div style={customStyles.legendContainer}>
|
||||
@@ -72,6 +89,38 @@ const CustomLegend = (props: any) => {
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};*/
|
||||
const CustomLegend = (props: any) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
|
||||
return (
|
||||
<div style={customStyles.legendContainer}>
|
||||
{payload.map((entry: any, index: number) => (
|
||||
<div
|
||||
key={`legend-${index}`}
|
||||
className="flex items-center"
|
||||
style={{
|
||||
minWidth: "90px",
|
||||
margin: "2px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
backgroundColor: entry.color,
|
||||
borderRadius: "3px",
|
||||
marginRight: "8px",
|
||||
minWidth: "14px",
|
||||
minHeight: "14px",
|
||||
}}
|
||||
></div>
|
||||
<span style={{ fontSize: "13px", whiteSpace: "nowrap" }}>{entry.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function DoubleBarChart({chartConfig=undefined, chartData=[], chartTitle='차트', chartClassName='w-full h-full', graphSize=40, yFieldText='', showLegend=false} : BarChartProps) {
|
||||
@@ -83,7 +132,7 @@ export default function DoubleBarChart({chartConfig=undefined, chartData=[], cha
|
||||
.map(key => item[key])
|
||||
).flat());
|
||||
};
|
||||
console.log(chartData);
|
||||
|
||||
const barMaxValue = getMaxValue(chartData, 'barVal');
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -48,6 +48,7 @@ type LegendProps = {
|
||||
};
|
||||
|
||||
/** 차트 기본 스타일 */
|
||||
/*
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
@@ -58,6 +59,23 @@ const customStyles = {
|
||||
marginTop: "10px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
*/
|
||||
|
||||
/** 차트 기본 스타일 (라인차트와 동일하게) */
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap", // 여러 줄로 자동 줄바꿈
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "12px", // 아이템 간 간격
|
||||
fontSize: "13px",
|
||||
fontWeight: "normal",
|
||||
marginTop: "16px",
|
||||
marginBottom: "8px",
|
||||
minHeight: "32px", // 범례 영역 높이 통일
|
||||
} as CSSProperties,
|
||||
};
|
||||
|
||||
// 커스텀 툴팁
|
||||
const CustomTooltip = ({ active, payload, label }: {
|
||||
@@ -82,6 +100,7 @@ const CustomTooltip = ({ active, payload, label }: {
|
||||
};
|
||||
|
||||
// 커스텀 범례
|
||||
/*
|
||||
const CustomLegend = (props: LegendProps) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
@@ -97,12 +116,52 @@ const CustomLegend = (props: LegendProps) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
*/
|
||||
|
||||
const CustomLegend = (props: LegendProps) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
|
||||
return (
|
||||
<div style={customStyles.legendContainer}>
|
||||
{payload.map((entry: LegendEntry, index: number) => (
|
||||
<div
|
||||
key={`legend-${index}`}
|
||||
className="flex items-center"
|
||||
style={{
|
||||
minWidth: "90px",
|
||||
margin: "2px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
backgroundColor: entry.fill,
|
||||
borderRadius: "3px",
|
||||
marginRight: "8px",
|
||||
minWidth: "14px",
|
||||
minHeight: "14px",
|
||||
}}
|
||||
></div>
|
||||
<span style={{ fontSize: "13px", whiteSpace: "nowrap" }}>
|
||||
{entry.payload?.label || entry.value}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function DoughnutChart(props: {
|
||||
chartConfig: any;
|
||||
chartData: ChartDataItem[] | undefined;
|
||||
chartTitle: string
|
||||
}) {
|
||||
const legendData = props.chartData?.map(item => ({
|
||||
value: item.label,
|
||||
fill: props.chartConfig?.[item.label]?.fill || 'blue',
|
||||
})) ?? [];
|
||||
return (
|
||||
<Card className="flex flex-col">
|
||||
<CardContent className="flex-1 pb-0">
|
||||
@@ -130,9 +189,9 @@ export default function DoughnutChart(props: {
|
||||
nameKey="label"
|
||||
innerRadius={60}
|
||||
/>
|
||||
<CustomLegend />
|
||||
</PieChart>
|
||||
</ChartContainer>
|
||||
<CustomLegend payload={legendData} />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -18,6 +18,7 @@ type LineChartProps = {
|
||||
}
|
||||
|
||||
/** 차트 기본 스타일 */
|
||||
/*
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
@@ -28,6 +29,23 @@ const customStyles = {
|
||||
marginTop: "10px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
*/
|
||||
|
||||
/** 차트 기본 스타일 */
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "12px",
|
||||
fontSize: "13px",
|
||||
fontWeight: "normal",
|
||||
marginTop: "16px",
|
||||
marginBottom: "8px",
|
||||
minHeight: "32px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
|
||||
type LegendEntry = {
|
||||
value: string;
|
||||
@@ -60,7 +78,7 @@ const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?:
|
||||
};
|
||||
|
||||
// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가)
|
||||
const CustomLegend = (props: LegendProps) => {
|
||||
/*const CustomLegend = (props: LegendProps) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
|
||||
@@ -74,6 +92,40 @@ const CustomLegend = (props: LegendProps) => {
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};*/
|
||||
|
||||
// 커스텀 범례 (도넛차트와 동일하게)
|
||||
const CustomLegend = (props: LegendProps) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
|
||||
return (
|
||||
<div style={customStyles.legendContainer}>
|
||||
{payload.map((entry: LegendEntry, index: number) => (
|
||||
<div
|
||||
key={`legend-${index}`}
|
||||
className="flex items-center"
|
||||
style={{
|
||||
minWidth: "90px",
|
||||
margin: "2px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
backgroundColor: entry.color,
|
||||
borderRadius: "3px",
|
||||
marginRight: "8px",
|
||||
minWidth: "14px",
|
||||
minHeight: "14px",
|
||||
}}
|
||||
></div>
|
||||
<span style={{ fontSize: "13px", whiteSpace: "nowrap" }}>{entry.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type DataType = {label: string; val: string};
|
||||
|
||||
@@ -17,6 +17,7 @@ type MonthPickerProps = {
|
||||
field: { value: string; onChange: (date: string) => void }; // yyyyMM 문자열
|
||||
selectClass?: string;
|
||||
readOnly?: boolean;
|
||||
startYear?: number; // 시작 연도 props 추가
|
||||
};
|
||||
|
||||
// yyyyMM → Date 객체
|
||||
@@ -37,6 +38,7 @@ export const DateMonthPicker = ({
|
||||
field,
|
||||
selectClass,
|
||||
readOnly = false,
|
||||
startYear,
|
||||
}: MonthPickerProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@@ -75,6 +77,7 @@ export const DateMonthPicker = ({
|
||||
<MonthPicker
|
||||
currentMonth={parsedDate}
|
||||
onMonthChange={handleSelect}
|
||||
startYear={startYear}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
@@ -28,35 +28,41 @@ export type MonthPickerProps = {
|
||||
newMonth: Date | undefined,
|
||||
shouldClosePopover?: boolean
|
||||
) => void;
|
||||
startYear?: number;
|
||||
};
|
||||
|
||||
export function MonthPicker({ currentMonth, onMonthChange }: MonthPickerProps) {
|
||||
export function MonthPicker({ currentMonth, onMonthChange, startYear }: MonthPickerProps) {
|
||||
|
||||
const today = startOfToday();
|
||||
const currentYear = today.getFullYear();
|
||||
const currentYear = today.getFullYear();
|
||||
const yearFromProp = format(currentMonth, "yyyy");
|
||||
const selectedYear = parseInt(yearFromProp);
|
||||
const selectedYear = parseInt(yearFromProp);
|
||||
|
||||
// 현재 월의 시작 날짜
|
||||
const fullYearStart = parse(yearFromProp, "yyyy", new Date());
|
||||
|
||||
const yearEnd =
|
||||
selectedYear === currentYear ? today : endOfYear(fullYearStart);
|
||||
|
||||
|
||||
|
||||
|
||||
const yearRange = Array.from({ length: 21 }, (_, i) => currentYear - 20 + i);
|
||||
|
||||
|
||||
// startYear가 없으면 기존과 동일하게 (현재년도 - 20년부터 현재년도까지)
|
||||
// startYear가 있으면 해당 년도부터 현재년도까지, 내림차순으로 정렬
|
||||
const yearRange = startYear
|
||||
? Array.from({ length: currentYear - startYear + 1 }, (_, i) => currentYear - i)
|
||||
: Array.from({ length: 21 }, (_, i) => currentYear - 20 + i);
|
||||
|
||||
const months = eachMonthOfInterval({ start: fullYearStart, end: yearEnd });
|
||||
|
||||
const getMonthButtonClass = (month: Date) =>
|
||||
cn(
|
||||
"inline-flex h-9 w-16 items-center justify-center rounded-md p-0 text-sm font-normal ring-offset-white transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 aria-selected:opacity-100 dark:ring-offset-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50 dark:focus-visible:ring-slate-800",
|
||||
isEqual(month, currentMonth) &&
|
||||
"bg-slate-900 text-slate-50 hover:bg-slate-900 hover:text-slate-50 focus:bg-slate-900 focus:text-slate-50 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50 dark:hover:text-slate-900 dark:focus:bg-slate-50 dark:focus:text-slate-900",
|
||||
"bg-slate-900 text-slate-50 hover:bg-slate-900 hover:text-slate-50 focus:bg-slate-900 focus:text-slate-50 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50 dark:hover:text-slate-900 dark:focus:bg-slate-50 dark:focus:text-slate-900",
|
||||
!isEqual(month, currentMonth) &&
|
||||
isEqual(month, getStartOfCurrentMonth()) &&
|
||||
"bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-50"
|
||||
isEqual(month, getStartOfCurrentMonth()) &&
|
||||
"bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-50"
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -77,7 +83,7 @@ const selectedYear = parseInt(yearFromProp);
|
||||
onMonthChange(newDate, false);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[120px] h-7">
|
||||
<SelectTrigger className="w-[140px] h-9 text-base">
|
||||
<SelectValue placeholder="년도" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -112,7 +118,7 @@ const selectedYear = parseInt(yearFromProp);
|
||||
onClick={() => onMonthChange(month, true)}
|
||||
>
|
||||
<time dateTime={format(month, "yyyy-MM-dd")}>
|
||||
{format(month, 'MMM', { locale: ko })}
|
||||
{format(month, 'MMM', { locale: ko })}
|
||||
</time>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { LoadingWithAnimation } from '@common/components/ui/loading';
|
||||
|
||||
interface LoadingOverlayProps {
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
const LoadingOverlay: React.FC<LoadingOverlayProps> = ({ isLoading }) => {
|
||||
if (!isLoading) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
|
||||
<div className="relative w-16 h-16">
|
||||
<LoadingWithAnimation color="primary" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingOverlay;
|
||||
Generated
+9
-6
@@ -347,6 +347,9 @@ importers:
|
||||
js-cookie:
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
moment:
|
||||
specifier: ^2.30.1
|
||||
version: 2.30.1
|
||||
next:
|
||||
specifier: 15.1.7
|
||||
version: 15.1.7(react-dom@18.3.1)(react@18.3.1)
|
||||
@@ -2401,7 +2404,7 @@ packages:
|
||||
magic-string: 0.27.0
|
||||
react-docgen-typescript: 2.2.2(typescript@5.6.3)
|
||||
typescript: 5.6.3
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
vite: 6.2.0(@types/node@22.13.5)
|
||||
dev: true
|
||||
|
||||
/@jridgewell/gen-mapping@0.3.8:
|
||||
@@ -2514,7 +2517,7 @@ packages:
|
||||
dnd-multi-backend: 8.1.2(dnd-core@16.0.1)
|
||||
framer-motion: 11.18.2(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dnd: 16.0.1(@types/node@22.13.9)(@types/react@18.3.18)(react@18.3.1)
|
||||
react-dnd: 16.0.1(@types/node@22.13.5)(@types/react@18.3.18)(react@18.3.1)
|
||||
react-dnd-html5-backend: 16.0.1
|
||||
react-dnd-touch-backend: 16.0.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@@ -3994,7 +3997,7 @@ packages:
|
||||
browser-assert: 1.2.1
|
||||
storybook: 8.6.0(prettier@3.5.2)
|
||||
ts-dedent: 2.2.0
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
vite: 6.2.0(@types/node@22.13.5)
|
||||
dev: true
|
||||
|
||||
/@storybook/components@8.6.0(storybook@8.6.0):
|
||||
@@ -4126,7 +4129,7 @@ packages:
|
||||
resolve: 1.22.10
|
||||
storybook: 8.6.0(prettier@3.5.2)
|
||||
tsconfig-paths: 4.2.0
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
vite: 6.2.0(@types/node@22.13.5)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- supports-color
|
||||
@@ -4601,7 +4604,7 @@ packages:
|
||||
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.9)
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.14.2
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
vite: 6.2.0(@types/node@22.13.5)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -9787,7 +9790,7 @@ packages:
|
||||
debug: 4.4.0
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.1.5(typescript@5.6.3)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
vite: 6.2.0(@types/node@22.13.5)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
Reference in New Issue
Block a user