Merge remote-tracking branch 'origin/iipbs/sn/dev'

This commit is contained in:
swcho
2025-07-22 22:24:16 +09:00
13 changed files with 576 additions and 664 deletions
@@ -3,7 +3,7 @@ import React from "react";
interface InputSectionProps {
title: string;
data: { year: string; type: string }[];
data: { year: string; type: string, bizNm: string, bizType: string }[];
sectionKey: string;
onInputChange: (section: string, index: number, field: string, value: string) => void;
}
@@ -13,21 +13,26 @@ const InputSection: React.FC<InputSectionProps> = ({ title, data, sectionKey, on
<div>
{/* 섹션 제목 */}
<div className="card-header">
<h3 className="text-2xl mx-3 font-medium text-label">{title}</h3>
<h3 className="text-2xl mx-3 font-medium text-label whitespace-nowrap">{title}</h3>
</div>
{/* 테이블 */}
<div className="card !m-[1.2rem] !p-0r">
<Table variant="secondary" className="w-full">
<TableHeader>
<TableRow>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-[80px]">년도</TableHead>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto">구분</TableHead>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto whitespace-nowrap">구분</TableHead>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-[80px] whitespace-nowrap">년도</TableHead>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto whitespace-nowrap">사업명</TableHead>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto whitespace-nowrap">유형</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((row, index) => (
<TableRow key={index}>
<TableCell className="border border-gray-300 px-2 py-2 text-sm w-[80px]">
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto whitespace-nowrap">
{row.type}
</TableCell>
<TableCell className="border border-gray-300 px-2 py-2 text-sm w-[80px] whitespace-nowrap">
<input
type="text"
value={row.year}
@@ -35,8 +40,11 @@ const InputSection: React.FC<InputSectionProps> = ({ title, data, sectionKey, on
className="w-full text-xl border-radius-0 px-3 py-2 text-base text-center leading-normal"
/>
</TableCell>
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto">
{row.type}
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto whitespace-nowrap">
{row.bizNm}
</TableCell>
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto whitespace-nowrap">
{row.bizType}
</TableCell>
</TableRow>
))}
@@ -49,8 +49,8 @@ export const handleInputChange = (section: string,
value: string,
dataMap: {
[key: string]: {
data: { year: string; type: string }[];
setData: React.Dispatch<React.SetStateAction<{ year: string; type: string }[]>>;
data: { year: string; type: string, bizNm: string, bizType: string }[];
setData: React.Dispatch<React.SetStateAction<{ year: string; type: string, bizNm: string, bizType: string }[]>>;
};
}
) => {
@@ -28,22 +28,17 @@ export const MsSwagePplCol = [
// { id: 'lyrTypeNm', title: '레이어타입명' },
]
export const MsSwagePplCol2 = [
{ id: 'fcltMngNo', title: '시설관리번호' },
{ id: 'instMngNo', title: '기관관리번호' },
{ id: 'fcltMngNo', title: '시설 관리번호' },
{ id: 'instMngNo', title: '기관 관리번호' },
{ id: 'instlYmd', title: '설치일자' },
{ id: 'mapshtNo', title: '도엽번호' },
{ id: 'pdmt', title: '관경(mm)' },
{ id: 'prlg', title: '연장(m)' },
{ id: 'avgGrdn', title: '평균구배(m/m)' },
{ id: 'rnysnFvlc', title: '우기 시 유속(m/s)' },
{ id: 'clarFvlc', title: '건기 시 유속(m/s)' },
{ id: 'drtwMltvArea', title: '오수배수면적(m²)' },
{ id: 'exclnMltvArea', title: '우수배수면적(m²)' },
{ id: 'tmptDpth', title: '시점깊이(m)' },
{ id: 'tmptMhlMngNo', title: '시점맨홀관리번호' },
{ id: 'tmptMhlMngNo', title: '시점맨홀 관리번호' },
{ id: 'scendDpth', title: '종점깊이(m)' },
{ id: 'scendMhlMngNo', title: '종점맨홀관리번호' },
{ id: 'trflnCnt', title: '차선수' },
{ id: 'scendMhlMngNo', title: '종점맨홀 관리번호' },
{ id: 'vrtcLen', title: '세로길이(m)' },
{ id: 'wdthLen', title: '가로길이(m)' },
// { id: 'fcltyShpCd', title: '시설물형태코드' },
@@ -306,35 +301,35 @@ export const SwtrmtFcltCol2 = [
{ id: 'rgnExpln', title: '지역설명' },
{ id: 'cmcnYmd', title: '준공일자' },
{ id: 'oprtngStrtYmd', title: '가동개시일자' },
{ id: 'clsgYmd', title: '폐쇄일자' },
// { id: 'clsgYmd', title: '폐쇄일자' },
// { id: 'prcsMsrcCd', title: '처리공법코드' },
// { id: 'laeCd', title: '지자체코드' },
{ id: 'laeNm', title: '지자체명' },
{ id: 'ctpvNm', title: '시도명' },
{ id: 'sggNm', title: '시군구명' },
// { id: 'ctpvNm', title: '시도명' },
// { id: 'sggNm', title: '시군구명' },
// { id: 'sareaCd', title: '권역코드' },
{ id: 'sareaNm', title: '권역명' },
{ id: 'lrgeSareaNm', title: '대권역명' },
{ id: 'rvrNm', title: '하천명' },
{ id: 'addr', title: '주소' },
{ id: 'dtlAddr', title: '상세주소' },
{ id: 'rdnmAddr', title: '도로명주소' },
{ id: 'dtlRdnmAddr', title: '상세도로명주소' },
// { id: 'addr', title: '주소' },
// { id: 'dtlAddr', title: '상세주소' },
// { id: 'rdnmAddr', title: '도로명주소' },
// { id: 'dtlRdnmAddr', title: '상세도로명주소' },
{ id: 'dschgWtstNm', title: '방류수계명' },
{ id: 'pllnArea', title: '부지면적(m²)' },
{ id: 'mngRprtvBzentyNm', title: '관리대행업제명' },
// { id: 'pllnArea', title: '부지면적(m²)' },
// { id: 'mngRprtvBzentyNm', title: '관리대행업체명' },
{ id: 'swtrmtPpltnCnt', title: '하수처리인구수(인)' },
{ id: 'prcsZoneAreaSum', title: '처리구역면적합계(m²)' },
{ id: 'xcrd', title: 'X좌표' },
{ id: 'ycrd', title: 'Y좌표' },
// { id: 'xcrd', title: 'X좌표' },
// { id: 'ycrd', title: 'Y좌표' },
// { id: 'oprtngSttsCd', title: '가동상태코드' },
{ id: 'rprsMsrcCn', title: '대표공법내용' },
{ id: 'sttyCrtrBod', title: '법정기준생물화학적산소요구량(mg/L)' },
{ id: 'sttyCrtrToc', title: '법정기준TOC(mg/L)' },
{ id: 'sttyCrtrSs', title: '법정기준SS(mg/L)' },
{ id: 'sttyCrtrTn', title: '법정기준총질소(mg/L)' },
{ id: 'sttyCrtrTph', title: '법정기준총인(mg/L)' },
{ id: 'sttyCrtrClbclCnt', title: '법정기준대장균수(군수/100mL)' },
// { id: 'sttyCrtrBod', title: '법정기준생물화학적산소요구량(mg/L)' },
// { id: 'sttyCrtrToc', title: '법정기준TOC(mg/L)' },
// { id: 'sttyCrtrSs', title: '법정기준SS(mg/L)' },
// { id: 'sttyCrtrTn', title: '법정기준총질소(mg/L)' },
// { id: 'sttyCrtrTph', title: '법정기준총인(mg/L)' },
// { id: 'sttyCrtrClbclCnt', title: '법정기준대장균수(군수/100mL)' },
{ id: 'bsnNm', title: '유역명' },
// { id: 'obsrvnSptCd', title: '관측지점코드' },
];
@@ -3,6 +3,8 @@ import { VerticalTable } from '@/components/ui/verticalTable';
import React, { useState, useEffect } from 'react'
import InputSection from '../InputSection';
import { handleInputChange } from '../common';
import { getData } from "@/components/lib/api";
// import { useBizByFcltStore } from "../../../../store/StoreProvider";
// 하수관로
const inputColumn = [
@@ -15,33 +17,98 @@ const inputColumn = [
{ id: "layCd", title: "매설위치", type: "select", options: ["도로", "하상"] },
];
type operationDataType = { year: string; type: string, bizNm: string, bizType: string };
const MsSwagePpl = ({ featureData, onSaveClick }: { featureData: any, onSaveClick?: () => void }) => {
const [repairData, setRepairData] = useState([
{ year: "2025", type: "개대체" },
{ year: "2025", type: "보수" },
]);
const [cleaningData, setCleaningData] = useState([
{ year: "2025", type: "청소" },
{ year: "2025", type: "준설" },
]);
const [inspectionData, setInspectionData] = useState([
{ year: "2025", type: "관로기술진단" },
{ year: "2025", type: "관로기술진단" },
]);
const [formData, setFormData] = useState<any>({});
const [repairData, setRepairData] = useState<operationDataType[]>([]);
const [cleaningData, setCleaningData] = useState<operationDataType[]>([]);
const [inspectionData, setInspectionData] = useState<operationDataType[]>([]);
const dataMap = {
repair: { data: repairData, setData: setRepairData },
cleaning: { data: cleaningData, setData: setCleaningData },
inspection: { data: inspectionData, setData: setInspectionData },
};
const [formData, setFormData] = useState<any>({});
// featureData가 변경될 때 formData 초기화
// const BIZ_SE_CD = useBizByFcltStore(state => state.BIZ_SE_CD);
const BIZ_SE_CD = [
{ lwpoCd: "100", lwpoCdNm: "관로기술진단" },
{ lwpoCd: "200", lwpoCdNm: "개대체" },
{ lwpoCd: "201", lwpoCdNm: "유지보수" },
{ lwpoCd: "203", lwpoCdNm: "청소, 준설" },
{ lwpoCd: "204", lwpoCdNm: "단순보수" },
];
const BIZ_TY_CD = [
{ lwpoCd: "SV", lwpoCdNm: "용역" },
{ lwpoCd: "SF", lwpoCdNm: "자체" },
{ lwpoCd: "SC", lwpoCdNm: "공사" },
];
console.log('BIZ_SE_CD', BIZ_SE_CD);
// 마운트 및 업데이트 시 API 호출
useEffect(() => {
if (featureData) {
setFormData(featureData);
}
}, [featureData]);
const fetchAll = async () => {
const operationData = await getData(`v1/bizInfo/bizByFclt?mngInstCd=${featureData?.mng_inst_cd}&fcltMngNo=${featureData?.fclt_mng_no}&page=0&size=1`);
let inspectionArr: operationDataType[] = [];
let cleaningArr: operationDataType[] = [];
let repairArr: operationDataType[] = [];
if (Array.isArray(operationData)) {
operationData.forEach(item => {
if (!item.flfmt_yr) return;
// biz_se_cd에 해당하는 type명 찾기
const seCodeObj = BIZ_SE_CD.find(code => code.lwpoCd === item.biz_se_cd);
const tyCodeObj = BIZ_TY_CD.find(code => code.lwpoCd === item.biz_ty_cd);
const seTypeName = seCodeObj ? seCodeObj.lwpoCdNm : "";
const tyTypeName = tyCodeObj ? tyCodeObj.lwpoCdNm : "";
switch (item.biz_se_cd) {
case "100":
inspectionArr.push({
year: String(item.flfmt_yr),
type: seTypeName,
bizNm: String(item.biz_nm),
bizType: tyTypeName,
});
break;
case "203":
cleaningArr.push({
year: String(item.flfmt_yr),
type: seTypeName,
bizNm: String(item.biz_nm),
bizType: tyTypeName,
});
break;
case "200":
case "201":
case "204":
repairArr.push({
year: String(item.flfmt_yr),
type: seTypeName,
bizNm: String(item.biz_nm),
bizType: tyTypeName,
});
break;
}
});
}
setInspectionData(inspectionArr);
setCleaningData(cleaningArr);
setRepairData(repairArr);
console.log('inspectionArr', inspectionArr);
console.log('cleaningArr', cleaningArr);
console.log('repairArr', repairArr);
};
fetchAll();
}, []);
// 폼 데이터가 변경될 때 호출될 핸들러
const handleFormDataChange = (newFormData: any) => {
@@ -56,6 +123,18 @@ const MsSwagePpl = ({ featureData, onSaveClick }: { featureData: any, onSaveClic
}
};
// 변환 함수: 객체 → [{ year, type }]
// function convertToYearTypeArray(obj: any): { year: string, type: string }[] {
// if (!obj || typeof obj !== "object") return [];
// // 이미 배열이면 그대로 반환
// if (Array.isArray(obj)) return obj;
// // 객체의 각 key를 type, value를 year로 변환
// return Object.entries(obj).map(([type, year]) => ({
// year: String(year ?? ""),
// type: String(type ?? ""),
// }));
// }
return (
<>
@@ -8,7 +8,7 @@ import { getData } from "@/components/lib/api";
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
const inputColumn = [
{ id: "prrtymngYn", title: "중점여부", type: "select", options: ["중점우수토실", "우수토실"] },
{ id: "prrtymngYn", title: "중점여부", type: "select", options: ["우수토실", "중점우수토실"] },
{ id: "swtrmtZoneCd", title: "처리구역", type: "input" },
];
@@ -6,7 +6,7 @@ import { useGisContext } from "@common/business_components/ui/map/Gis";
import { Button } from "@common/components/ui";
import { useEffect, useRef, useState, useCallback, useMemo } from "react";
import { getLayerContentComponent } from "./components/common";
import { SwtrmtFcltCol, SwtrmtPrcsZoneCol, untrtSwgRainCol1, untrtSwgRainCol2, MsSwagePplCol, ExclnSewagePumpCol, MsSwageMhlCol, stofcbrCol, MS_SWAGE_MHL_CODES, DRTW_SEWAGE_PUMP_USE_CODES } from "./components/common/table";
import { SwtrmtFcltCol, SwtrmtFcltCol2, SwtrmtPrcsZoneCol, SwtrmtPrcsZoneCol2, untrtSwgRainCol1, untrtSwgRainCol2, MsSwagePplCol, MsSwagePplCol2, ExclnSewagePumpCol, ExclnSewagePumpCol2, DrtwSewagePumpCol1, DrtwSewagePumpCol2, MsSwageMhlCol, MsSwageMhlCol2, stofcbrCol, stofcbrCol2, MS_SWAGE_MHL_CODES, DRTW_SEWAGE_PUMP_USE_CODES } from "./components/common/table";
import InfilChart from './components/popupComponent/chart/infilChart';
import RdiiChart from './components/popupComponent/chart/rdiiChart';
import { useInfilChartModalStore, useRdiiChartModalStore } from "./components/popupComponent/chartModalStore";
@@ -33,6 +33,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
const { setIsOpen: setIsModalInfil } = useInfilChartModalStore();
const { setIsOpen: setIsModalRdii } = useRdiiChartModalStore();
const [curPage, setCurPage] = useState(1); // 현재 페이지 상태
const [isClicked, setIsClicked] = useState(true); // 클릭 여부 상태
// 🎯 관로관리번호 추출
const pplMngNo = featureData?.values_?.fclt_mng_no || "";
@@ -66,8 +67,9 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
}
});
// console.log("featureLayer.get('lyrTyNm');", featureLayer.get('lyrTyNm'));
// SearchModal 클릭 이벤트와 Gis 클릭 이벤트 구분하기
// SearchModal 클릭 이벤트
if (featureLayer.get('lyrNm') === null) {
setIsClicked(false);
if (featureData.lyrTypeNm === 'tb_il_swage_prcs_fclt') result = '하수처리시설';
else if (featureData.lyrTypeNm === 'tb_ms_swage_prcs_zone') result = '하수처리구역';
else if (featureData.lyrTypeNm === 'tb_ms_stofcbr') result = '우수토실';
@@ -77,15 +79,17 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(code => code.text === '오수펌프장');
console.log('값 비교', featureData.values_.usg_cd.trim(), drtwPumpCode?.val);
if (drtwPumpCode && featureData.values_.usg_cd.trim() === drtwPumpCode.val) {
setIsDrtwSewagePump(true);
} else {
setIsDrtwSewagePump(false);
} else {
setIsDrtwSewagePump(true);
}
result = '하수펌프장';
}
else if (featureData.lyrTypeNm === 'tb_ms_swage_mhl') result = '하수맨홀';
}
else {
// Gis 레이어 클릭 이벤트
setIsClicked(true);
const lyrTyNm = featureLayer.get('lyrTyNm');
if (lyrTyNm === 'tb_il_swage_prcs_fclt') result = '하수처리시설';
else if (lyrTyNm === 'tb_ms_swage_prcs_zone') result = '하수처리구역';
@@ -96,9 +100,9 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(code => code.text === '오수펌프장');
console.log('값 비교', featureData.values_.usg_cd.trim(), drtwPumpCode?.val);
if (drtwPumpCode && featureData.values_.usg_cd.trim() === drtwPumpCode.val) {
setIsDrtwSewagePump(true);
} else {
setIsDrtwSewagePump(false);
} else {
setIsDrtwSewagePump(true);
}
result = '하수펌프장';
}
@@ -135,15 +139,16 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
// 🎯 기본 데이터 포맷팅
const formattedData = formatDisplayData(data);
if (featureData.values_.fclt_mng_no) setLayerSpecificNm(featureData.values_.fclt_mng_no);
else if (featureData.values_.swtrmt_fclt_cd) setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
else setLayerSpecificNm(featureData.values_.manage_no);
if (featureData?.values_?.fclt_mng_no) setLayerSpecificNm(featureData.values_.fclt_mng_no);
else if (featureData?.values_?.swtrmt_fclt_cd) setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
else setLayerSpecificNm(featureData?.values_?.manage_no);
setFetchData(formattedData);
setTableData(formattedData);
switch (selectedLayerName) {
case '하수관로':
setHeadlist(MsSwagePplCol)
if (isClicked) setHeadlist(MsSwagePplCol)
else setHeadlist(MsSwagePplCol2)
setButtonChk(true)
// ✅ codeMapper 사용하여 API 결과도 변환
@@ -155,7 +160,8 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
// setTableData(mappedData);
break;
case '하수맨홀':
setHeadlist(MsSwageMhlCol)
if (isClicked) setHeadlist(MsSwageMhlCol)
else setHeadlist(MsSwageMhlCol2)
setButtonChk(true)
// ✅ codeMapper 사용하여 API 결과도 변환
@@ -167,10 +173,17 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
// setTableData(mappedData);
break;
case '하수펌프장':
setHeadlist(ExclnSewagePumpCol)
if (isDrtwSewagePump) {
if (isClicked) setHeadlist(DrtwSewagePumpCol1)
else setHeadlist(DrtwSewagePumpCol2)
} else {
if (isClicked) setHeadlist(ExclnSewagePumpCol)
else setHeadlist(ExclnSewagePumpCol2)
}
setButtonChk(true)
setTableData(codeMapper({
setTableData(codeMapper({
layerName: selectedLayerName,
data: { ...formattedData }
}));
@@ -178,15 +191,18 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
// setTableData(mappedData);
break;
case '하수처리시설':
setHeadlist(SwtrmtFcltCol);
if (isClicked) setHeadlist(SwtrmtFcltCol)
else setHeadlist(SwtrmtFcltCol2)
setButtonChk(false)
break;
case '하수처리구역':
setHeadlist(SwtrmtPrcsZoneCol)
if (isClicked) setHeadlist(SwtrmtPrcsZoneCol)
else setHeadlist(SwtrmtPrcsZoneCol2)
setButtonChk(false)
break;
case '우수토실':
setHeadlist(stofcbrCol)
if (isClicked) setHeadlist(stofcbrCol)
else setHeadlist(stofcbrCol2)
setButtonChk(true)
setIsMainSsc(false)
break;
@@ -477,7 +493,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
<Modal isModalOpen={isModalOpen} title="자체입력정보 저장" setIsModalOpen={setIsModalOpen} closeModal={handleModalClose} saveModal={handleModalSave}><div className="text-lg">정보를 저장하시겠습니까?</div></Modal>
{ContentComponent &&
<ContentComponent
featureData={tableData}
featureData={fetchData}
isMainSsc={isMainSsc}
isDrtwSewagePump={isDrtwSewagePump}
rainMeasureData={rainMeasureData}
+17 -17
View File
@@ -4,7 +4,7 @@ import Gis, { useGisContext } from "@common/business_components/ui/map/Gis";
import { getData } from "../../components/lib/api";
import { useCallback, useEffect, useRef, useState } from "react";
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import SearchModal from "./SearchModal";
import SearchModal from "./searchModal/searchModal";
import FeatureClickEventPopup from "./featureClickEventPopup";
// 타입 정의 추가
@@ -115,22 +115,22 @@ const Map = () => {
};
return (
<div className="bg-container">
<Gis
treeData={treeData}
isSearch={true}
gisMaps={gisMaps}
isMiniMap={false}
searchView={SearchModal}
detailView={FeatureClickEventPopup}
onFeatureSelect={onFeatureSelect}
setIsOpen={setIsOpen}
setFeatureData={setFeatureData}
featureData={featureData}
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={undefined}
/>
<Gis
treeData={treeData}
isSearch={true}
gisMaps={gisMaps}
isMiniMap={false}
searchView={SearchModal}
detailView={FeatureClickEventPopup}
onFeatureSelect={onFeatureSelect}
setIsOpen={setIsOpen}
setFeatureData={setFeatureData}
featureData={featureData}
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={undefined}
/>
</div>
);
};
@@ -219,7 +219,7 @@ const BaseLayout = ({
<div className="flex items-center w-full">
{showDatePicker && ( // showDatePicker 값에 따라 DatePicker 렌더링
<>
<Label className="ml-4 w-20 text-xl text-gray-600 whitespace-nowrap">분석기간</Label>
<Label className="ml-4 w-20 text-xl text-gray-600 whitespace-nowrap">상수사용기간</Label>
<DateMonthPicker
field={{
value: aplcnYmFrom || (() => { // aplcnYm이 없으면 현재 연월을 기본값으로 설정
+41 -30
View File
@@ -1,5 +1,18 @@
import {useState, useEffect} from 'react';
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
import { useEffect, useState } from "react";
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || '';
export const getData = async (url: string, data?: object): Promise<any> => {
const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, {
method: "GET",
body: JSON.stringify(data),
});
if (res.ok) {
const data = await res.json();
return data;
}
throw new Error('Failed to fetch data');
};
export const postData = async (url: string, data: object): Promise<any> => {
const options: RequestInit = {
@@ -15,11 +28,6 @@ export const postData = async (url: string, data: object): Promise<any> => {
if (res.ok) {
const resData = await res.json();
return resData;
// const text = await res.text();
// if (text) {
// return JSON.parse(text); // JSON 데이터가 있으면 파싱하여 반환
// }
// return true; // 응답 본문이 비어 있으면 true 반환
}
};
@@ -46,29 +54,6 @@ export const putData = async (url: string, data: object): Promise<any> => {
return resData;
};
export const getData = async (url: string, data?: object): Promise<any> => {
const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, {
method: "GET",
});
if (res.ok) {
const data = await res.json();
return data;
}
throw new Error('Failed to fetch data');
};
export const getDcData = async (url: string, data?: object): Promise<any> => {
console.log(url);
const res = await fetch('/dc-service'+url, {
method: "GET",
});
if (res.ok) {
const data = await res.json();
return data;
}
throw new Error('Failed to fetch data');
};
export const deleteData = async (url: string, data?: object): Promise<any> => {
const options: RequestInit = {
method: "DELETE",
@@ -82,6 +67,32 @@ export const deleteData = async (url: string, data?: object): Promise<any> => {
return res;
};
// RSONE DC API 호출
export const getDcData = async (url: string, data?: object): Promise<any> => {
console.log(url);
const res = await fetch('/dc-service' + url, {
method: "GET",
});
if (res.ok) {
const data = await res.json();
return data;
}
throw new Error('Failed to fetch data');
};
// RSONE DC API 호출
export const getCmsData = async (url: string, data?: object): Promise<any> => {
console.log(url);
const res = await fetch('/cms-service' + url, {
method: "GET",
});
if (res.ok) {
const data = await res.json();
return data;
}
throw new Error('Failed to fetch data');
};
export function getCookie(name:string) {
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
if (match && match[2]) {
@@ -90,7 +90,7 @@ export function YearRangeSelectBox({
return (
<div className={`flex items-center ${className}`}>
<SelectBox2
className="flex-1 mr-3 min-w-[120px] max-w-[170px]"
className="flex-1 mr-3"
selectClass={selectClass}
label={startLabel}
selectArray={getStartYearArray()}
@@ -99,7 +99,7 @@ export function YearRangeSelectBox({
/>
<span className="font-semibold text-xl mx-2">~</span>
<SelectBox2
className="flex-1 ml-1 mr-6 min-w-[70px] max-w-[150px]"
className="flex-1 ml-1"
selectClass={selectClass}
label={endLabel}
selectArray={getEndYearArray()}
@@ -1,38 +0,0 @@
import { Tabs, TabsList, TabsTrigger } from '@common/components/ui';
import React from 'react';
interface TabItem {
value: string;
label: string;
onClick?: () => void;
}
interface UITabProps extends React.HTMLAttributes<HTMLDivElement> {
defaultValue?: string;
tabList: TabItem[];
children: React.ReactNode;
}
const UITab2 = React.forwardRef<
HTMLDivElement,
UITabProps
>(({defaultValue, tabList, children}, ref)=>{
return (
<Tabs defaultValue={defaultValue || tabList[0]?.value} className={"w-full"}>
<TabsList className="flex">
{tabList.map((tab) => (
<TabsTrigger
key={tab.value}
value={tab.value}
onClick={tab.onClick}
className="py-2 px-4 text-sm font-medium border-b-2 focus:outline-none data-[state=active]:border-blue-500 data-[state=active]:text-blue-600 data-[state=inactive]:border-transparent data-[state=inactive]:text-gray-500 hover:text-gray-700 hover:border-gray-300"
>
{tab.label}
</TabsTrigger>
))}
</TabsList>
{children}
</Tabs>
)
})
export { UITab2 };
@@ -1,155 +0,0 @@
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components";
import { useEffect, useState } from "react";
import UiPageNations from "./uiPageNations";
type UiTableProps = {
headName: React.ReactNode | undefined;
tableData: { [key: string]: string | number | boolean | object }[];
headlist: UiHeadProps[];
pageSize: number;
total: number;
children: React.ReactNode | undefined;
cellClick: ((index: number | undefined, headId: string) => void) | undefined;
onPageChange: (page: number) => void;
className?: string;
resetPageNo?: boolean;
cur?: number;
height?: string; // ✅ 추가된 prop
};
type UiHeadProps = {
id: string;
title: string;
align?: string;
};
export function UiTable2({
tableData,
headlist,
pageSize,
total,
children,
headName,
cellClick,
onPageChange,
className = '',
resetPageNo = false,
cur,
height = '45vh', // ✅ 기본값 설정
}: UiTableProps) {
const [idx, setIdx] = useState<number>(-1);
const [curState, setCurState] = useState<number>(cur ?? 1);
useEffect(() => {
setCurState(cur ?? 1);
}, [cur]);
useEffect(() => {
if (resetPageNo) {
setCurState(1);
onPageChange(1);
}
}, [onPageChange, resetPageNo]);
const handlePageChange = (page: number) => {
setCurState(page);
onPageChange(page);
};
const handleCellClick = (index: number, headId: string) => {
if (cellClick) {
cellClick(index, headId);
setIdx(index);
}
};
const startIdx = (curState - 1) * pageSize;
return (
<>
{headName && (
<div className="card-header">
<div className="flex w-full items-center justify-between">
<h3 className="text-2xl font-medium text-label">{headName}</h3>
</div>
</div>
)}
<div className={`card !m-[1.2rem] !p-0 relative ${className}`}>
{/* ✅ 외부에서 조절 가능한 높이 적용 */}
<div className="overflow-auto" style={{ height }}>
<Table variant="secondary" className="w-full">
<TableHeader className="bg-white">
{children ? (
<>{children}</>
) : (
<TableRow>
{headlist.map((head) => (
<TableHead
key={head.id}
className="min-w-[120px] max-w-[180px] w-[150px]
text-center whitespace-nowrap overflow-hidden
text-ellipsis bg-white"
>
{head.title}
</TableHead>
))}
</TableRow>
)}
</TableHeader>
<TableBody className="text-center">
{tableData.length > 0 ? (
tableData.map((el, index) => (
<TableRow key={index + "row"}>
{headlist.map((head) => (
<TableCell
key={head.id + index}
className={`
${head.align === 'right'
? 'text-right'
: head.align === 'center'
? 'text-center'
: 'text-center'
}
${cellClick && idx === index ? '!font-[1000] cursor-pointer' :
cellClick ? 'cursor-pointer' : ''}
`}
onClick={() => handleCellClick(startIdx + index, head.id)}
>
{typeof el[head.id] === 'number'
? String(el[head.id]).replace(/\B(?=(\d{3})+(?!\d))/g, ",")
: String(el[head.id] ?? '')}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={headlist.length} className="p-0">
{/* ✅ 높이 보장 + 가운데 정렬용 div */}
<div className="flex justify-center items-center text-gray-500" style={{ height: `calc(${height} - 70px)` }}>
<span className="text-xl">🔍 검색 결과가 없습니다.</span>
</div>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
{/* 페이지네이션 하단 고정 */}
{(
<div className="sticky left-0 right-0 bottom-0 bg-white z-20 mt-5">
<UiPageNations
total={total}
cur={curState}
pageSize={pageSize}
onPageChange={handlePageChange}
/>
</div>
)}
</div>
</>
);
}