Merge branch 'iipbs/sn/dev' into iipbs/sn/main
This commit is contained in:
@@ -115,7 +115,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const [yearFilter, setYearFilter] = useState({
|
||||
시작: "",
|
||||
종료: "",
|
||||
경과년도: "",
|
||||
// 경과년도: "",
|
||||
});
|
||||
|
||||
const [가동개시_시작_월, set가동개시_시작_월] = useState('');
|
||||
@@ -182,7 +182,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
setYearFilter({
|
||||
시작: "",
|
||||
종료: "",
|
||||
경과년도: "",
|
||||
// 경과년도: "",
|
||||
});
|
||||
setShowTable(false);
|
||||
setTableCount(0);
|
||||
@@ -247,34 +247,34 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const pplCqlFilters = () => {
|
||||
const cqlFilters: string[] = [];
|
||||
// 추후 작성 예정
|
||||
// if (변수1) cqlFilters.push(`용도코드='${변수1}'`);
|
||||
// if (변수2) cqlFilters.push(`재질코드='${변수2}'`);
|
||||
// if (변수3) cqlFilters.push(`행정읍면동코드='${변수3}'`);
|
||||
if (변수1) cqlFilters.push(`usg_cd='${변수1}'`);
|
||||
if (변수2) cqlFilters.push(`mqlt_cd='${변수2}'`);
|
||||
if (변수3) cqlFilters.push(`pbadms_emd_cd='${변수3}'`);
|
||||
// if (변수4) cqlFilters.push(`경과년도='${변수4}'`);
|
||||
// // 설치년도 범위
|
||||
// const startDate = getDateString(
|
||||
// yearFilter.시작,
|
||||
// 가동개시_시작_월,
|
||||
// 가동개시_시작_일,
|
||||
// "19000101",
|
||||
// );
|
||||
// const endDate = getDateString(
|
||||
// yearFilter.종료,
|
||||
// 가동개시_종료_월,
|
||||
// 가동개시_종료_일,
|
||||
// `${todayYear}${todayMonth}${todayDate}`,
|
||||
// );
|
||||
// cqlFilters.push(
|
||||
// `설치시작년도 >= '${startDate}' and 설치시작년도 <= '${endDate}'`,
|
||||
// );
|
||||
// // 관경(mm) 범위
|
||||
// if (시작1 && 종료1) cqlFilters.push(`관경 BETWEEN ${시작1} AND ${종료1}`);
|
||||
// else if (시작1) cqlFilters.push(`관경 >= ${시작1}`);
|
||||
// else if (종료1) cqlFilters.push(`관경 <= ${종료1}`);
|
||||
// // 연장(m) 범위
|
||||
// if (시작2 && 종료2) cqlFilters.push(`연장 BETWEEN ${시작2} AND ${종료2}`);
|
||||
// else if (시작2) cqlFilters.push(`연장 >= ${시작2}`);
|
||||
// else if (종료2) cqlFilters.push(`연장 <= ${종료2}`);
|
||||
// 설치년도 범위
|
||||
const startDate = getDateString(
|
||||
yearFilter.시작,
|
||||
가동개시_시작_월,
|
||||
가동개시_시작_일,
|
||||
"19000101",
|
||||
);
|
||||
const endDate = getDateString(
|
||||
yearFilter.종료,
|
||||
가동개시_종료_월,
|
||||
가동개시_종료_일,
|
||||
`${todayYear}${todayMonth}${todayDate}`,
|
||||
);
|
||||
cqlFilters.push(
|
||||
`instl_ymd >= '${startDate}' and instl_ymd <= '${endDate}'`,
|
||||
);
|
||||
// 관경(mm) 범위
|
||||
if (시작1 && 종료1) cqlFilters.push(`pdmt BETWEEN ${시작1} AND ${종료1}`);
|
||||
else if (시작1) cqlFilters.push(`pdmt >= ${시작1}`);
|
||||
else if (종료1) cqlFilters.push(`pdmt <= ${종료1}`);
|
||||
// 연장(m) 범위
|
||||
if (시작2 && 종료2) cqlFilters.push(`prlg BETWEEN ${시작2} AND ${종료2}`);
|
||||
else if (시작2) cqlFilters.push(`prlg >= ${시작2}`);
|
||||
else if (종료2) cqlFilters.push(`prlg <= ${종료2}`);
|
||||
|
||||
return cqlFilters;
|
||||
};
|
||||
@@ -319,27 +319,27 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const mhCqlFilters = () => {
|
||||
const cqlFilters: string[] = [];
|
||||
// // 설치년도 범위
|
||||
// const startDate = getDateString(
|
||||
// yearFilter.시작,
|
||||
// 가동개시_시작_월,
|
||||
// 가동개시_시작_일,
|
||||
// "19000101",
|
||||
// );
|
||||
// const endDate = getDateString(
|
||||
// yearFilter.종료,
|
||||
// 가동개시_종료_월,
|
||||
// 가동개시_종료_일,
|
||||
// `${todayYear}${todayMonth}${todayDate}`,
|
||||
// );
|
||||
// cqlFilters.push(
|
||||
// `설치시작년도 >= '${startDate}' and 설치시작년도 <= '${endDate}'`,
|
||||
// );
|
||||
const startDate = getDateString(
|
||||
yearFilter.시작,
|
||||
가동개시_시작_월,
|
||||
가동개시_시작_일,
|
||||
"19000101",
|
||||
);
|
||||
const endDate = getDateString(
|
||||
yearFilter.종료,
|
||||
가동개시_종료_월,
|
||||
가동개시_종료_일,
|
||||
`${todayYear}${todayMonth}${todayDate}`,
|
||||
);
|
||||
cqlFilters.push(
|
||||
`instl_ymd >= '${startDate}' and instl_ymd <= '${endDate}'`,
|
||||
);
|
||||
// if (변수1) cqlFilters.push(`경과년도='${변수1}'`);
|
||||
// if (변수2) cqlFilters.push(`용도코드='${변수2}'`);
|
||||
// // 구경(mm) 범위
|
||||
// if (시작1 && 종료1) cqlFilters.push(`구경 BETWEEN ${시작1} AND ${종료1}`);
|
||||
// else if (시작1) cqlFilters.push(`구경 >= ${시작1}`);
|
||||
// else if (종료1) cqlFilters.push(`구경 <= ${종료1}`);
|
||||
if (변수2) cqlFilters.push(`usg_cd='${변수2}'`);
|
||||
// 구경(mm) 범위
|
||||
if (시작1 && 종료1) cqlFilters.push(`swage_mh_diam BETWEEN ${시작1} AND ${종료1}`);
|
||||
else if (시작1) cqlFilters.push(`swage_mh_diam >= ${시작1}`);
|
||||
else if (종료1) cqlFilters.push(`swage_mh_diam <= ${종료1}`);
|
||||
|
||||
return cqlFilters;
|
||||
};
|
||||
@@ -347,32 +347,32 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const psmtnCqlFilters = () => {
|
||||
const cqlFilters: string[] = [];
|
||||
// 설치년도 범위
|
||||
// const startDate = getDateString(
|
||||
// yearFilter.시작,
|
||||
// 가동개시_시작_월,
|
||||
// 가동개시_시작_일,
|
||||
// "19000101",
|
||||
// );
|
||||
// const endDate = getDateString(
|
||||
// yearFilter.종료,
|
||||
// 가동개시_종료_월,
|
||||
// 가동개시_종료_일,
|
||||
// `${todayYear}${todayMonth}${todayDate}`,
|
||||
// );
|
||||
// cqlFilters.push(
|
||||
// `설치시작년도 >= '${startDate}' and 설치시작년도 <= '${endDate}'`,
|
||||
// );
|
||||
const startDate = getDateString(
|
||||
yearFilter.시작,
|
||||
가동개시_시작_월,
|
||||
가동개시_시작_일,
|
||||
"19000101",
|
||||
);
|
||||
const endDate = getDateString(
|
||||
yearFilter.종료,
|
||||
가동개시_종료_월,
|
||||
가동개시_종료_일,
|
||||
`${todayYear}${todayMonth}${todayDate}`,
|
||||
);
|
||||
cqlFilters.push(
|
||||
`instl_ymd >= '${startDate}' and instl_ymd <= '${endDate}'`,
|
||||
);
|
||||
// if (변수1) cqlFilters.push(`경과년도='${변수1}'`);
|
||||
// // 일일처리용량 범위
|
||||
// if (시작1 && 종료1)
|
||||
// cqlFilters.push(`일일처리용량 BETWEEN ${시작1} AND ${종료1}`);
|
||||
// else if (시작1) cqlFilters.push(`일일처리용량 >= ${시작1}`);
|
||||
// else if (종료1) cqlFilters.push(`일일처리용량 <= ${종료1}`);
|
||||
// // 우수양수능력 범위
|
||||
// if (시작2 && 종료2)
|
||||
// cqlFilters.push(`우수양수능력 BETWEEN ${시작2} AND ${종료2}`);
|
||||
// else if (시작2) cqlFilters.push(`우수양수능력 >= ${시작2}`);
|
||||
// else if (종료2) cqlFilters.push(`우수양수능력 <= ${종료2}`);
|
||||
// 일일처리용량 범위
|
||||
if (시작1 && 종료1)
|
||||
cqlFilters.push(`daily_prcs_cpct BETWEEN ${시작1} AND ${종료1}`);
|
||||
else if (시작1) cqlFilters.push(`daily_prcs_cpct >= ${시작1}`);
|
||||
else if (종료1) cqlFilters.push(`daily_prcs_cpct <= ${종료1}`);
|
||||
// 우수양수능력 범위
|
||||
if (시작2 && 종료2)
|
||||
cqlFilters.push(`excln_wpmp_ablt BETWEEN ${시작2} AND ${종료2}`);
|
||||
else if (시작2) cqlFilters.push(`excln_wpmp_ablt >= ${시작2}`);
|
||||
else if (종료2) cqlFilters.push(`excln_wpmp_ablt <= ${종료2}`);
|
||||
|
||||
return cqlFilters;
|
||||
};
|
||||
@@ -380,24 +380,24 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const otflCqlFilters = () => {
|
||||
const cqlFilters: string[] = [];
|
||||
// 설치년도 범위
|
||||
// const startDate = getDateString(
|
||||
// yearFilter.시작,
|
||||
// 가동개시_시작_월,
|
||||
// 가동개시_시작_일,
|
||||
// "19000101",
|
||||
// );
|
||||
// const endDate = getDateString(
|
||||
// yearFilter.종료,
|
||||
// 가동개시_종료_월,
|
||||
// 가동개시_종료_일,
|
||||
// `${todayYear}${todayMonth}${todayDate}`,
|
||||
// );
|
||||
// cqlFilters.push(
|
||||
// `설치시작년도 >= '${startDate}' and 설치시작년도 <= '${endDate}'`,
|
||||
// );
|
||||
const startDate = getDateString(
|
||||
yearFilter.시작,
|
||||
가동개시_시작_월,
|
||||
가동개시_시작_일,
|
||||
"19000101",
|
||||
);
|
||||
const endDate = getDateString(
|
||||
yearFilter.종료,
|
||||
가동개시_종료_월,
|
||||
가동개시_종료_일,
|
||||
`${todayYear}${todayMonth}${todayDate}`,
|
||||
);
|
||||
cqlFilters.push(
|
||||
`instl_ymd >= '${startDate}' and instl_ymd <= '${endDate}'`,
|
||||
);
|
||||
// if (변수1) cqlFilters.push(`경과년도='${변수1}'`);
|
||||
// if (변수2) cqlFilters.push(`하천명 ILIKE '%${변수1}%'`);
|
||||
// // 계획방류량 범위
|
||||
// 계획방류량 범위
|
||||
// if (시작1 && 종료1)
|
||||
// cqlFilters.push(`계획방류량 BETWEEN ${시작1} AND ${종료1}`);
|
||||
// else if (시작1) cqlFilters.push(`계획방류량 >= ${시작1}`);
|
||||
@@ -502,7 +502,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
/>
|
||||
|
||||
{/* 경과년도 */}
|
||||
<div className="flex items-center">
|
||||
{/* <div className="flex items-center">
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
경과년도
|
||||
</Label>
|
||||
@@ -516,7 +516,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
setYearFilter({ ...yearFilter, 경과년도: val });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
{/* 용도코드 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
용도코드
|
||||
@@ -729,7 +729,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 경과년도 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
{/* <Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
경과년도
|
||||
</Label>
|
||||
<Input
|
||||
@@ -741,9 +741,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setYearFilter({ ...yearFilter, 경과년도: val });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center mb-2'>
|
||||
/> */}
|
||||
{/* 용도코드 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
용도코드
|
||||
@@ -788,85 +786,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
)}
|
||||
{/* 분기처리: 하수펌프장 */}
|
||||
{basicType === "tb_ms_swage_pmstn" && (
|
||||
<div className="mb-3">
|
||||
{/* 설치년도 */}
|
||||
<div className="flex items-center mb-2">
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.시작}
|
||||
end={yearFilter.종료}
|
||||
onChange={range => setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 경과년도 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">경과년도</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={yearFilter.경과년도}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
setYearFilter({ ...yearFilter, 경과년도: val });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center mb-2">
|
||||
{/* 일일처리용량 - 오수맨홀 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
일일처리용량
|
||||
</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={시작1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set시작1(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={종료1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set종료1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 우수양수능력 - 우수맨홀 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
우수양수능력
|
||||
</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={시작2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set시작2(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={종료2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set종료2(val);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* 분기처리: 우수토실? 토구? */}
|
||||
{basicType === "tb_ms_stofcbr" && (
|
||||
<div className="mb-3">
|
||||
{/* 설치년도 */}
|
||||
<div className="flex items-center mb-2">
|
||||
@@ -878,34 +797,20 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 경과년도 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">경과년도</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={yearFilter.경과년도}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
setYearFilter({ ...yearFilter, 경과년도: val });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center mb-2">
|
||||
{/* 하천명 */}
|
||||
{/* <Label className="w-auto text-xl text-gray-600 mr-2">경과년도</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={yearFilter.경과년도}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
setYearFilter({ ...yearFilter, 경과년도: val });
|
||||
}}
|
||||
/> */}
|
||||
{/* 일일처리용량 - 오수맨홀 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
하천명
|
||||
</Label>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={변수1}
|
||||
onChange={(e) => {
|
||||
set변수1(e.target.value);
|
||||
}}
|
||||
/>
|
||||
{/* 계획방류량 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
계획방류량
|
||||
일일처리용량
|
||||
</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
@@ -928,6 +833,95 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
set종료1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 우수양수능력 - 우수맨홀 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
우수양수능력
|
||||
</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={시작2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set시작2(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={종료2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set종료2(val);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* 분기처리: 우수토실? 토구? */}
|
||||
{basicType === "tb_ms_stofcbr" && (
|
||||
<div className="mb-3">
|
||||
{/* 설치년도 */}
|
||||
<div className="flex items-center mb-2">
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.시작}
|
||||
end={yearFilter.종료}
|
||||
onChange={range => setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 경과년도 */}
|
||||
{/* <Label className="w-auto text-xl text-gray-600 mr-2">경과년도</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={yearFilter.경과년도}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
setYearFilter({ ...yearFilter, 경과년도: val });
|
||||
}}
|
||||
/> */}
|
||||
{/* 하천명 */}
|
||||
{/* <Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
하천명
|
||||
</Label>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={변수1}
|
||||
onChange={(e) => {
|
||||
set변수1(e.target.value);
|
||||
}}
|
||||
/> */}
|
||||
{/* 계획방류량 */}
|
||||
{/* <Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
계획방류량
|
||||
</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={시작1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set시작1(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={종료1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set종료1(val);
|
||||
}}
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -970,7 +964,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
/>
|
||||
<div className="flex items-center">
|
||||
{/* 경과년도 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
{/* <Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
경과년도
|
||||
</Label>
|
||||
<Input
|
||||
@@ -982,7 +976,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setYearFilter({ ...yearFilter, 경과년도: val });
|
||||
}}
|
||||
/>
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
{/* 조회 버튼 중앙 정렬 */}
|
||||
@@ -1095,6 +1089,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
z-[101]
|
||||
`}
|
||||
body={renderBody()}
|
||||
useDirectRender={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ type LayerComponentProps = {
|
||||
featureData: any;
|
||||
chkMainSsc?: boolean;
|
||||
additionalData?: any;
|
||||
onSaveClick?: () => void;
|
||||
};
|
||||
type LayerContentMap = {
|
||||
[key: string]: {
|
||||
|
||||
@@ -16,7 +16,7 @@ const inputColumn = [
|
||||
{ id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] },
|
||||
];
|
||||
|
||||
const MsSwagePpl = ({ featureData }: { featureData: any }) => {
|
||||
const MsSwagePpl = ({ featureData, onSaveClick }: { featureData: any, onSaveClick?: () => void }) => {
|
||||
const [repairData, setRepairData] = useState([
|
||||
{ year: "2025", type: "개대체" },
|
||||
{ year: "2025", type: "보수" },
|
||||
@@ -40,7 +40,7 @@ const MsSwagePpl = ({ featureData }: { featureData: any }) => {
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<VerticalTable
|
||||
headName='자체 입력정보'
|
||||
tableData={[]}
|
||||
@@ -53,6 +53,8 @@ const MsSwagePpl = ({ featureData }: { featureData: any }) => {
|
||||
onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}
|
||||
showSaveButton={true}
|
||||
onSaveClick={onSaveClick}
|
||||
/>
|
||||
<InputSection
|
||||
title="보수 및 갱신"
|
||||
@@ -72,7 +74,7 @@ const MsSwagePpl = ({ featureData }: { featureData: any }) => {
|
||||
sectionKey="inspection"
|
||||
onInputChange={(section, index, field, value) => handleInputChange(section, index, field, value, dataMap)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,23 @@ const inputColumn = [
|
||||
{ id: "freq", title: "설계빈도", type: "input" },
|
||||
]
|
||||
// 하수펌프장
|
||||
const sewagePump = () => {
|
||||
const sewagePump = ({ featureData, onSaveClick }: { featureData: any, onSaveClick?: () => void }) => {
|
||||
return (
|
||||
<>
|
||||
<VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={0} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
<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.');
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
showSaveButton={true}
|
||||
onSaveClick={onSaveClick}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,23 @@ const inputColumn = [
|
||||
{ id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] },
|
||||
{ id: 'spec', title: '규격', type: 'input' },
|
||||
];
|
||||
const Smh = () => {
|
||||
const Smh = ({ featureData, onSaveClick }: { featureData: any, onSaveClick?: () => void }) => {
|
||||
return (
|
||||
<div>
|
||||
<VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={20} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
<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>
|
||||
}}
|
||||
showSaveButton={true}
|
||||
onSaveClick={onSaveClick}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,19 @@ 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>
|
||||
<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.');
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -53,16 +53,16 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
|
||||
try {
|
||||
console.log(`추가 데이터 API 호출 시작: v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs`);
|
||||
|
||||
|
||||
// 🎯 타임아웃 설정 (10초)
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('API 타임아웃')), 10000)
|
||||
);
|
||||
|
||||
|
||||
const apiPromise = getData(`v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs`);
|
||||
|
||||
|
||||
const response = await Promise.race([apiPromise, timeoutPromise]) as any;
|
||||
|
||||
|
||||
console.log('추가 데이터 API 호출 성공:', response);
|
||||
return response?.content || [];
|
||||
} catch (error) {
|
||||
@@ -207,7 +207,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
useEffect(() => {
|
||||
if (layerNm === '중점우수토실' && featureData?.values_) {
|
||||
const data = featureData.values_;
|
||||
|
||||
|
||||
if (data?.adst_dstr_cd && data?.manage_no) {
|
||||
// 🎯 추가 데이터를 비동기로 로딩 (UI 블로킹 없음)
|
||||
fetchAdditionalData(data.adst_dstr_cd, data.manage_no)
|
||||
@@ -310,14 +310,14 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
color="white"
|
||||
onClick={openModalInfil}
|
||||
>
|
||||
Infil
|
||||
침입수분석
|
||||
</Button>
|
||||
<Button
|
||||
className="px-2 py-1 text-xl text-gray-600 border border-gray-600 rounded hover:bg-gray-700"
|
||||
color="white"
|
||||
onClick={openModalRdii}
|
||||
>
|
||||
RDII
|
||||
RDII분석
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
@@ -351,17 +351,13 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
total2={0}
|
||||
isEditable2={buttonChk}
|
||||
/>
|
||||
<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}
|
||||
onSaveClick={handleSaveClick}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -137,6 +137,23 @@ 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]; // 수용가-맨홀 매칭결과
|
||||
@@ -275,7 +292,7 @@ const IrSetting = () => {
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(0, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={""}
|
||||
accept={".inp"}
|
||||
fileIdx={0} />
|
||||
</FormSection>
|
||||
<FormSection name='suyeonga' form={form} titleNm='▷ 수용가-맨홀 매칭결과'>
|
||||
@@ -335,10 +352,15 @@ const IrSetting = () => {
|
||||
// '.' 기호가 포함된 경우, 처음이나 끝에 오면 안 됨
|
||||
if (newValue.includes('.') && newValue.startsWith('.')) return; // '.'이 처음에는 올 수 없음
|
||||
|
||||
// 숫자 값으로 변환하여 범위 체크 (0 이상 1 이하)
|
||||
const numericValue = parseFloat(newValue);
|
||||
if (isNaN(numericValue) || numericValue < 0 || numericValue > 1) return;
|
||||
|
||||
setInput1(newValue);
|
||||
}}
|
||||
className="ml-5 h-[3.2rem] text-center text-xl w-[150px]"
|
||||
placeholder="예: 0.75" />
|
||||
placeholder="예: 0.75"
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection
|
||||
name='analysis information'
|
||||
|
||||
@@ -163,6 +163,24 @@ 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);
|
||||
const file1 = uploadedFilesData[0]; // 오수관망도
|
||||
@@ -378,7 +396,7 @@ export default function RTKForm() {
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(0, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={""}
|
||||
accept={".inp"}
|
||||
fileIdx={0} />
|
||||
</div></FormSection>
|
||||
<FormSection name="regopm" form={form} titleNm="○ 수용가-맨홀 매칭결과"><div className="w-[350px]">
|
||||
@@ -400,7 +418,34 @@ export default function RTKForm() {
|
||||
fileIdx={2} />
|
||||
</div></FormSection>
|
||||
<FormSection name='rate' form={form} titleNm='○ 오수전환율'>
|
||||
<Input {...form.register("rate")} className="h-12 text-center text-xl w-[225px] ml-5" />
|
||||
<Input
|
||||
value={form.watch("rate")} // form의 rate 값을 가져옴
|
||||
onChange={(e) => {
|
||||
const newValue = e.target.value;
|
||||
|
||||
if (newValue === "") {
|
||||
form.setValue("rate", newValue); // form의 rate 필드에 설정
|
||||
return;
|
||||
}
|
||||
|
||||
// 숫자와 '.'만 허용하고, '.'은 최대 하나인지 확인
|
||||
const isValidChars = /^[0-9]*\.?[0-9]*$/.test(newValue);
|
||||
if (!isValidChars) return; // 유효하지 않은 문자 또는 여러 개의 '.' 포함
|
||||
|
||||
// '.' 기호가 처음에 오면 안 됨
|
||||
if (newValue.startsWith('.')) return;
|
||||
|
||||
// 완성된 숫자인 경우에만 범위 체크 (입력 중인 경우 허용)
|
||||
if (!newValue.endsWith('.')) {
|
||||
const numericValue = parseFloat(newValue);
|
||||
if (isNaN(numericValue) || numericValue < 0 || numericValue > 1) return;
|
||||
}
|
||||
|
||||
form.setValue("rate", newValue, { shouldValidate: true }); // form의 rate 필드에 설정
|
||||
}}
|
||||
className="h-12 text-center text-xl w-[225px] ml-5"
|
||||
placeholder="예: 0.75"
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection name='date' form={form} titleNm='○ 상수사용량 적용기간'>
|
||||
{/* <DatePicker
|
||||
|
||||
@@ -24,12 +24,14 @@ const formSchema = z.object({
|
||||
type ResultFormValues = z.infer<typeof formSchema>
|
||||
|
||||
const tableColumn = [
|
||||
{ id: 'rainInfwtAnlsSn', title: '분석번호' },
|
||||
{ id: 'enoNm', title: '환경청' },
|
||||
{ id: 'ctpvNm', title: '시도' },
|
||||
{ id: 'sggNm', title: '시군구' },
|
||||
{ id: 'swageTmplntNm', title: '하수처리장' },
|
||||
// { id: 'swtrmtZoneNm', title: '처리구역' },
|
||||
{ id: 'aplcnYm', title: '상수사용연월' },
|
||||
{ id: 'drtwSwtcRt', title: '오수 전환율' },
|
||||
{ id: 'srcNm', title: 'RTX 계수 출처' },
|
||||
{ id: 'srcNm', title: 'RTK 계수 출처' },
|
||||
{ id: 'cntbArea', title: 'RDII 기여면적' },
|
||||
{ id: 'anlsInfo', title: 'RDII 분석정보' },
|
||||
{ id: 'regDt', title: '분석일자' },
|
||||
|
||||
@@ -24,8 +24,9 @@ const formSchema = z.object({
|
||||
type ResultFormValues = z.infer<typeof formSchema>
|
||||
|
||||
const tableColumn = [
|
||||
// { id: 'no', title: '번호' },
|
||||
{ id: 'inftwtAnlsSn', title: '분석번호' },
|
||||
{ id: 'enoNm', title: '환경청' },
|
||||
{ id: 'ctpvNm', title: '시도' },
|
||||
{ id: 'sggNm', title: '시군구' },
|
||||
{ id: 'swageTmplntNm', title: '하수처리장' },
|
||||
// { id: 'swtrmtZoneNm', title: '처리구역' },
|
||||
{ id: 'aplcnYm', title: '적용연월' },
|
||||
|
||||
@@ -102,24 +102,6 @@ const Page = () => {
|
||||
selected={toDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} />
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">관리년도</Label>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setManageYear(String(value))}
|
||||
/>
|
||||
<span>~</span>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setMngQu(String(value))}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록자</Label>
|
||||
<Input
|
||||
|
||||
@@ -103,24 +103,6 @@ const Page = () => {
|
||||
<DatePicker mode={'single'}
|
||||
selected={toDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} />
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">관리년도</Label>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setManageYear(String(value))}
|
||||
/>
|
||||
<span>~</span>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setMngQu(String(value))}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록자</Label>
|
||||
<Input
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import UiPageNations from "@common/business_components/ui/table/uiPageNations";
|
||||
import { Table, TableBody, TableCell, TableRow } from "@common/components";
|
||||
import { Table, TableBody, TableCell, TableRow, Button } from "@common/components";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type UiTableProps = {
|
||||
@@ -27,6 +27,8 @@ type UiTableProps = {
|
||||
resetPageNo2?: boolean;
|
||||
isEditable2?: boolean;
|
||||
showSecondTable?: boolean;
|
||||
showSaveButton?: boolean;
|
||||
onSaveClick?: () => void;
|
||||
};
|
||||
|
||||
type UiHeadProps = {
|
||||
@@ -62,6 +64,8 @@ export function VerticalTable({
|
||||
resetPageNo2 = false,
|
||||
isEditable2 = false,
|
||||
showSecondTable = false,
|
||||
showSaveButton = false,
|
||||
onSaveClick,
|
||||
}: UiTableProps) {
|
||||
const [cur, setCur] = useState<number>(1);
|
||||
const [formData, setFormData] = useState<{ [key: string]: string | number | boolean }>({});
|
||||
@@ -93,8 +97,16 @@ export function VerticalTable({
|
||||
return (
|
||||
<>
|
||||
{headName ? (
|
||||
<div className="card-header">
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<h3 className="text-2xl mx-3 font-medium text-label">{headName}</h3>
|
||||
{showSaveButton && onSaveClick && (
|
||||
<Button
|
||||
onClick={onSaveClick}
|
||||
className="px-4 mr-3 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700"
|
||||
>
|
||||
저장
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
<div className={`card !m-[1.2rem] !p-0r ${className}`}>
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"libphonenumber-js": "^1.12.9",
|
||||
"lucide-react": "^0.474.0",
|
||||
"path": "^0.12.7",
|
||||
"react-day-picker": "^8.10.1",
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
'use client';
|
||||
|
||||
import { cn } from "../../lib/utils";
|
||||
import {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
Button,
|
||||
} from "@common/components/ui";
|
||||
import { ko } from "date-fns/locale";
|
||||
import { format } from "date-fns";
|
||||
import { useState } from "react";
|
||||
import { MonthPicker } from "./mothPicker";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
|
||||
type MonthPickerProps = {
|
||||
field: { value: string; onChange: (date: string) => void }; // yyyyMM 문자열
|
||||
selectClass?: string;
|
||||
readOnly?: boolean;
|
||||
};
|
||||
|
||||
// yyyyMM → Date 객체
|
||||
const parseYYYYMM = (value: string) => {
|
||||
const year = Number(value.substring(0, 4));
|
||||
const month = Number(value.substring(4, 6)) - 1;
|
||||
return new Date(year, month);
|
||||
};
|
||||
|
||||
// Date 객체 → yyyyMM 문자열
|
||||
const formatDateToYYYYMM = (date: Date) => {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
return `${year}${month}`;
|
||||
};
|
||||
|
||||
export const DateMonthPicker = ({
|
||||
field,
|
||||
selectClass,
|
||||
readOnly = false,
|
||||
}: MonthPickerProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const parsedDate = field?.value ? parseYYYYMM(field.value) : new Date();
|
||||
|
||||
const handleSelect = (
|
||||
date: Date | undefined,
|
||||
shouldClosePopover?: boolean
|
||||
) => {
|
||||
if (!readOnly && date) {
|
||||
field.onChange(formatDateToYYYYMM(date));
|
||||
if (shouldClosePopover) setOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={open && !readOnly} onOpenChange={readOnly ? undefined : setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
color="white"
|
||||
disabled={readOnly}
|
||||
className={cn(
|
||||
selectClass ?? "w-[240px] justify-between pl-3 text-left font-normal",
|
||||
!field?.value && "text-muted-foreground",
|
||||
readOnly && "opacity-60 cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
{parsedDate
|
||||
?format(parsedDate, "yyyy년 M월", { locale: ko })
|
||||
: "월을 선택하세요"}
|
||||
<CalendarIcon className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<MonthPicker
|
||||
currentMonth={parsedDate}
|
||||
onMonthChange={handleSelect}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
import { cn } from "../../lib/utils";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@common/components/ui";
|
||||
import {
|
||||
eachMonthOfInterval,
|
||||
endOfYear,
|
||||
format,
|
||||
isEqual,
|
||||
isFuture,
|
||||
parse,
|
||||
startOfMonth,
|
||||
startOfToday,
|
||||
} from "date-fns";
|
||||
import { ko } from "date-fns/locale";
|
||||
|
||||
function getStartOfCurrentMonth() {
|
||||
return startOfMonth(startOfToday());
|
||||
}
|
||||
|
||||
export type MonthPickerProps = {
|
||||
currentMonth: Date;
|
||||
onMonthChange: (
|
||||
newMonth: Date | undefined,
|
||||
shouldClosePopover?: boolean
|
||||
) => void;
|
||||
};
|
||||
|
||||
export function MonthPicker({ currentMonth, onMonthChange }: MonthPickerProps) {
|
||||
|
||||
const today = startOfToday();
|
||||
const currentYear = today.getFullYear();
|
||||
const yearFromProp = format(currentMonth, "yyyy");
|
||||
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);
|
||||
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",
|
||||
!isEqual(month, currentMonth) &&
|
||||
isEqual(month, getStartOfCurrentMonth()) &&
|
||||
"bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-50"
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="p-3">
|
||||
<div className="flex flex-col space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0">
|
||||
<div className="space-y-4">
|
||||
{/* 년도 셀렉트 */}
|
||||
<div className="flex justify-center pt-1">
|
||||
<Select
|
||||
value={yearFromProp}
|
||||
onValueChange={(selectedYearStr) => {
|
||||
const selectedYearNum = parseInt(selectedYearStr);
|
||||
const newDate = new Date(
|
||||
selectedYearNum,
|
||||
currentMonth.getMonth(),
|
||||
1
|
||||
);
|
||||
onMonthChange(newDate, false);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[120px] h-7">
|
||||
<SelectValue placeholder="년도" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{yearRange.map((year) => (
|
||||
<SelectItem key={year} value={String(year)}>
|
||||
{year}년
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 월 그리드 */}
|
||||
<div
|
||||
className="grid w-full grid-cols-3 gap-2"
|
||||
role="grid"
|
||||
aria-labelledby="month-picker"
|
||||
>
|
||||
{months.map((month) => (
|
||||
<div
|
||||
key={month.toString()}
|
||||
className="relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-slate-100 first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md dark:[&:has([aria-selected])]:bg-slate-800"
|
||||
role="presentation"
|
||||
>
|
||||
<button
|
||||
name="month"
|
||||
className={getMonthButtonClass(month)}
|
||||
disabled={isFuture(month)}
|
||||
role="gridcell"
|
||||
tabIndex={-1}
|
||||
type="button"
|
||||
onClick={() => onMonthChange(month, true)}
|
||||
>
|
||||
<time dateTime={format(month, "yyyy-MM-dd")}>
|
||||
{format(month, 'MMM', { locale: ko })}
|
||||
</time>
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader } from "@common/components";
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
type ModalType = {
|
||||
type ModalType = {
|
||||
bodyClass: string;
|
||||
body: React.ReactNode;
|
||||
open?: boolean;
|
||||
@@ -9,38 +10,45 @@ type ModalType = {
|
||||
noFooterYn?: 'N' | 'Y';
|
||||
headerTitle?: string;
|
||||
footerContent?: React.ReactNode;
|
||||
useDirectRender?: boolean;
|
||||
}
|
||||
|
||||
export function UiModal({
|
||||
bodyClass,
|
||||
body,
|
||||
open,
|
||||
export function UiModal({
|
||||
bodyClass,
|
||||
body,
|
||||
open,
|
||||
onOpenChange,
|
||||
noHeaderYn = 'N',
|
||||
noFooterYn = 'N',
|
||||
headerTitle,
|
||||
footerContent
|
||||
footerContent,
|
||||
useDirectRender = false
|
||||
}: ModalType) {
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className={bodyClass}>
|
||||
{noHeaderYn === 'N' && (
|
||||
<DialogHeader>
|
||||
<DialogDescription>
|
||||
{headerTitle}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
)}
|
||||
{(open && body)}
|
||||
{noFooterYn === 'N' && (
|
||||
<DialogFooter className="sm:justify-start">
|
||||
{footerContent}
|
||||
</DialogFooter>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className={bodyClass}>
|
||||
{noHeaderYn === 'N' && (
|
||||
<DialogHeader>
|
||||
<DialogDescription>
|
||||
{headerTitle}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
)}
|
||||
{(open && body) ?
|
||||
useDirectRender
|
||||
? body
|
||||
: <div>{body}</div>
|
||||
: <></>
|
||||
}
|
||||
{noFooterYn === 'N' && (
|
||||
<DialogFooter className="sm:justify-start">
|
||||
{footerContent}
|
||||
</DialogFooter>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,4 +20,5 @@ export * from "./table";
|
||||
export * from "./table_div";
|
||||
export * from "./title";
|
||||
export * from "./sdmInsertForm";
|
||||
export * from "./popover_ui"
|
||||
export * from "./popover_ui";
|
||||
export * from "./date_month_picker";
|
||||
@@ -3,9 +3,9 @@ import { SelectBox } from "../select";
|
||||
import { Input } from "@common/components";
|
||||
import { FormUi } from "../form_ui";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { AsYouType } from "libphonenumber-js";
|
||||
import type { ControllerRenderProps, UseFormReturn } from "react-hook-form";
|
||||
import { SdmSearchForm } from "./sdmNameBox";
|
||||
import { set } from "date-fns";
|
||||
|
||||
// 수요처 정보
|
||||
export type DemandCompType = {
|
||||
@@ -79,6 +79,19 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
|
||||
const [updateCoNm, setUpdateNm] = useState<string >('')
|
||||
|
||||
|
||||
|
||||
const formatPhoneNumber = (value:string) => {
|
||||
const input = value;
|
||||
const formatter = new AsYouType("KR");
|
||||
const formatted = formatter.input(input);
|
||||
|
||||
// 저장할 값 처리: 숫자만 추출
|
||||
const digitsOnly = input.replace(/\D/g, "");
|
||||
|
||||
return formatted
|
||||
};
|
||||
|
||||
|
||||
const initializeSigunData = useCallback((sidoCd: string) => {
|
||||
if (sidoCd === '0') {
|
||||
setSigunValue('0');
|
||||
@@ -325,14 +338,23 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
|
||||
{
|
||||
name: "telno",
|
||||
label: "전화번호",
|
||||
component: (field) => (
|
||||
component: (field) =>
|
||||
|
||||
{
|
||||
const formattedValue = formatPhoneNumber(field.value??''); // 초기 렌더링 시에도 포맷 적용
|
||||
|
||||
return (
|
||||
<Input
|
||||
placeholder="전화번호를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
{...field}
|
||||
value={formattedValue}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value.replace(/\D/g, ''); // 숫자만 저장
|
||||
field.onChange(value);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
@@ -41,8 +41,8 @@ const createFormSchema = (isBznmTotNoDisabled: boolean, isDuplicate: boolean ,up
|
||||
telno: z.string()
|
||||
.min(8, "전화번호는 8자 이상 입력해주세요")
|
||||
.max(11, "전화번호는 11자 이내로 입력해주세요")
|
||||
.regex(/^[0-9-]+$/, "전화번호는 숫자와 하이픈(-)만 입력 가능합니다"),
|
||||
rgtr: z.string().optional(),
|
||||
.regex(/^[0-9]+$/, "전화번호는 숫자만 입력 가능합니다"),
|
||||
rgtr: z.string().optional(),
|
||||
dmdAplyPswd : updateList?.dmdInfoPk // 업데이트일경우
|
||||
? z.string().optional() // 패스워드는 선택 사항이며 길이 제한 없음
|
||||
: z.string().optional()
|
||||
|
||||
@@ -301,7 +301,7 @@ function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], sel
|
||||
resetAllValues();
|
||||
// 리셋 후 트리거를 false로 설정
|
||||
if (setRtnPartValue) {
|
||||
setRtnPartValue('0');
|
||||
setRtnPartValue('A');
|
||||
}
|
||||
}
|
||||
}, [resetTrigger]);
|
||||
|
||||
@@ -110,9 +110,7 @@ export function UiTable2({
|
||||
? 'text-right'
|
||||
: head.align === 'center'
|
||||
? 'text-center'
|
||||
: typeof el[head.id] === 'number'
|
||||
? 'text-right'
|
||||
: 'text-center'
|
||||
: 'text-center'
|
||||
}
|
||||
${cellClick && idx === index ? '!font-[1000] cursor-pointer' :
|
||||
cellClick ? 'cursor-pointer' : ''}
|
||||
|
||||
@@ -82,7 +82,7 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr
|
||||
boldArray.includes(index)
|
||||
?
|
||||
<TableCell
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id] === 'number' ? 'text-right' : 'text-center'} ${
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : 'text-center'} ${
|
||||
cellClick && idx === index ? '!font-[1000] cursor-pointer' :
|
||||
cellClick ? 'cursor-pointer' : ''
|
||||
} !font-bold`}
|
||||
@@ -97,7 +97,7 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr
|
||||
</TableCell>
|
||||
:
|
||||
<TableCell
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id] === 'number' ? 'text-right' : 'text-center'} ${
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : 'text-center'} ${
|
||||
cellClick && idx === index ? '!font-[1000] cursor-pointer' :
|
||||
cellClick ? 'cursor-pointer' : ''
|
||||
}`}
|
||||
|
||||
Generated
+13
-6
@@ -538,6 +538,9 @@ importers:
|
||||
date-fns:
|
||||
specifier: ^3.6.0
|
||||
version: 3.6.0
|
||||
libphonenumber-js:
|
||||
specifier: ^1.12.9
|
||||
version: 1.12.9
|
||||
lucide-react:
|
||||
specifier: ^0.474.0
|
||||
version: 0.474.0(react@18.3.1)
|
||||
@@ -2398,7 +2401,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.5)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
dev: true
|
||||
|
||||
/@jridgewell/gen-mapping@0.3.8:
|
||||
@@ -2511,7 +2514,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.5)(@types/react@18.3.18)(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-html5-backend: 16.0.1
|
||||
react-dnd-touch-backend: 16.0.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@@ -3991,7 +3994,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.5)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
dev: true
|
||||
|
||||
/@storybook/components@8.6.0(storybook@8.6.0):
|
||||
@@ -4123,7 +4126,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.5)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- supports-color
|
||||
@@ -4598,7 +4601,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.5)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -7314,6 +7317,10 @@ packages:
|
||||
type-check: 0.4.0
|
||||
dev: true
|
||||
|
||||
/libphonenumber-js@1.12.9:
|
||||
resolution: {integrity: sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==}
|
||||
dev: false
|
||||
|
||||
/lie@3.3.0:
|
||||
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
||||
dependencies:
|
||||
@@ -9780,7 +9787,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.5)
|
||||
vite: 6.2.0(@types/node@22.13.9)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
Reference in New Issue
Block a user