diff --git a/public/css/common.css b/public/css/common.css
index 6f7b459c..2c5e9079 100644
--- a/public/css/common.css
+++ b/public/css/common.css
@@ -71,6 +71,8 @@
.schInput{height: 3.5rem; font-family: 'NanumSquare', sans-serif;font-size: var(--fs-m); color: var(--white);background-color: var(--tertiary1);padding: 0 1.2rem; border: 0;}
.schInput::placeholder { color:rgba(var(--white-rgb),.3); }
.dateInput {background:url(../images/ico_input_cal.svg) no-repeat center right .5rem/2.4rem; padding-right: 3rem; cursor: pointer;}
+.dateInput { position: relative; }
+.dateInput::-webkit-calendar-picker-indicator { opacity: 0; position: absolute; right: 0; width: 3rem; height: 100%;cursor: pointer; }
.dateInput::placeholder { color:var(--white); }
/* =========================
@@ -107,6 +109,15 @@
.colList.lineSB li a .title {font-size: var(--fs-m); font-weight: var(--fw-bold);}
.colList.lineSB li a .meta {font-size: var(--fs-s); font-weight: var(--fw-regular); color:rgba(var(--white-rgb),.5);}
+/* 페이지네이션 */
+.pagination {display: flex; align-items: center; justify-content: center; gap: .5rem; padding: 1.4rem 0;}
+.pagination button {min-width: 2.8rem; height: 2.8rem; padding: 0 .6rem; border-radius: .4rem; background-color: var(--secondary1); color: var(--white); font-size: var(--fs-m); font-weight: var(--fw-bold); border: 1px solid var(--secondary3); cursor: pointer; transition: background-color .15s ease, border-color .15s ease;}
+.pagination button:hover {background-color: var(--secondary3); border-color: var(--secondary4);}
+.pagination button.on {background-color: var(--primary1); border-color: var(--primary1);}
+.pagination button.on:hover {background-color: var(--primary2); border-color: var(--primary2);}
+.pagination button.disabled {opacity: 0.4; cursor: default; pointer-events: none;}
+.pagination .ellipsis {color: rgba(var(--white-rgb), .4); font-size: var(--fs-m); padding: 0 .2rem; user-select: none;}
+
/* 아코디언리스트 */
.accordionWrap {display: flex;flex-direction: column;transition: max-height 0.3s ease;}
.accordionWrap .acdHeader {display: flex; justify-content: space-between; align-items: center; height: 4rem; background-color: var(--secondary1); padding: 1rem; border-bottom: .1rem solid var(--secondary3);}
@@ -292,7 +303,7 @@
align-items: center;
z-index: 999;
}
-.popupUtillWrap { position: absolute;top: 50%; left: 50%;transform: translate(-50%, -50%);z-index :85;}
+.popupUtillWrap { position: fixed;top: 50%; left: 50%;transform: translate(-50%, -50%);z-index :100;}
.popupUtill {display: flex; flex-direction: column; width: 52.5rem; height:auto;max-height: 80vh;overflow: hidden; background-color: var(--secondary2); border: .1rem solid var(--secondary3); padding:2.5rem 3rem;}
.popupUtill > .puHeader {display: flex; justify-content: space-between; align-items: center; padding-bottom: 2rem;}
.popupUtill > .puHeader > .title {font-weight: var(--fw-bold); font-size: var(--fs-xl);}
diff --git a/src/satellite/components/SatelliteImageManage.jsx b/src/satellite/components/SatelliteImageManage.jsx
index 4e909e7a..4dc146db 100644
--- a/src/satellite/components/SatelliteImageManage.jsx
+++ b/src/satellite/components/SatelliteImageManage.jsx
@@ -1,4 +1,5 @@
-import { useState, useCallback } from 'react';
+import { useState, useEffect, useCallback } from 'react';
+import { fetchCommonCodeList } from '@/api/commonApi';
import { fetchSatelliteVideoList, fetchSatelliteCsvFeatures } from '@/api/satelliteApi';
import { useSatelliteStore } from '@/stores/satelliteStore';
import { useMapStore } from '@/stores/mapStore';
@@ -11,6 +12,12 @@ const LIMIT = 10;
export default function SatelliteImageManage() {
const [isAccordionOpen, setIsAccordionOpen] = useState(false);
+ // 공통코드 옵션
+ const [videoKindOptions, setVideoKindOptions] = useState([]);
+ const [videoOriginOptions, setVideoOriginOptions] = useState([]);
+ const [videoOrbitOptions, setVideoOrbitOptions] = useState([]);
+ const [videoCycleOptions, setVideoCycleOptions] = useState([]);
+
// 폼 필터 state
const [startDate, setStartDate] = useState('');
const [endDate, setEndDate] = useState('');
@@ -42,6 +49,14 @@ export default function SatelliteImageManage() {
const setOpacity = useSatelliteStore((s) => s.setOpacity);
const setBrightness = useSatelliteStore((s) => s.setBrightness);
+ // 마운트 시 공통코드 로드
+ useEffect(() => {
+ fetchCommonCodeList('000109').then(setVideoKindOptions).catch(() => setVideoKindOptions([]));
+ fetchCommonCodeList('000111').then(setVideoOriginOptions).catch(() => setVideoOriginOptions([]));
+ fetchCommonCodeList('000110').then(setVideoOrbitOptions).catch(() => setVideoOrbitOptions([]));
+ fetchCommonCodeList('000108').then(setVideoCycleOptions).catch(() => setVideoCycleOptions([]));
+ }, []);
+
const toggleAccordion = () => setIsAccordionOpen((prev) => !prev);
const search = useCallback(async (targetPage) => {
@@ -162,11 +177,11 @@ export default function SatelliteImageManage() {
onChange={(e) => setSatelliteVideoKind(e.target.value)}
>
-
-
-
-
-
+ {videoKindOptions.map((opt) => (
+
+ ))}
@@ -191,10 +207,11 @@ export default function SatelliteImageManage() {
onChange={(e) => setSatelliteVideoOrbit(e.target.value)}
>
-
-
-
-
+ {videoOrbitOptions.map((opt) => (
+
+ ))}
diff --git a/src/satellite/components/SatelliteManageRegisterPopup.jsx b/src/satellite/components/SatelliteManageRegisterPopup.jsx
index 23ef156c..8f787f1c 100644
--- a/src/satellite/components/SatelliteManageRegisterPopup.jsx
+++ b/src/satellite/components/SatelliteManageRegisterPopup.jsx
@@ -1,6 +1,9 @@
import { useState, useEffect } from 'react';
+import {createPortal} from "react-dom";
+import { showToast } from '@/components/common/Toast';
import { fetchCommonCodeList } from '@/api/commonApi';
import {
+ fetchSatelliteCompanyList,
saveSatelliteManage,
fetchSatelliteManageDetail,
updateSatelliteManage,
@@ -16,6 +19,7 @@ export default function SatelliteManageRegisterPopup({ satelliteManageId, onClos
const isEditMode = !!satelliteManageId;
const [sensorTypeOptions, setSensorTypeOptions] = useState([]);
+ const [companyOptions, setCompanyOptions] = useState([]);
const [companyNo, setCompanyNo] = useState('');
const [satelliteName, setSatelliteName] = useState('');
@@ -38,9 +42,13 @@ export default function SatelliteManageRegisterPopup({ satelliteManageId, onClos
setError(null);
try {
- const codeList = await fetchCommonCodeList('000092');
+ const [codeList, companyList] = await Promise.all([
+ fetchCommonCodeList('000092'),
+ fetchSatelliteCompanyList(),
+ ]);
if (cancelled) return;
setSensorTypeOptions(codeList);
+ setCompanyOptions(companyList);
if (satelliteManageId) {
const data = await fetchSatelliteManageDetail(satelliteManageId);
@@ -65,6 +73,15 @@ export default function SatelliteManageRegisterPopup({ satelliteManageId, onClos
}, [satelliteManageId]);
const handleSave = async () => {
+ if (!companyNo) {
+ showToast('사업자명을 선택해주세요.');
+ return;
+ }
+ if (!satelliteName.trim()) {
+ showToast('위성명을 입력해주세요.');
+ return;
+ }
+
setIsSaving(true);
setError(null);
@@ -98,134 +115,140 @@ export default function SatelliteManageRegisterPopup({ satelliteManageId, onClos
}
};
- return (
-
-
-
+ return createPortal(
+
+
+
{isEditMode ? '위성 관리 상세' : '위성 관리 등록'}
-
-
+
+
-
- {isLoading &&
조회 중...
}
- {error &&
{error}
}
+
+ {isLoading &&
조회 중...
}
+ {error &&
{error}
}
- {!isLoading && (
-
+ )}
+
-
-
-
-
+
+
+
+
+
-
-
+
,
+ document.body
);
}
diff --git a/src/satellite/components/SatelliteProviderRegisterPopup.jsx b/src/satellite/components/SatelliteProviderRegisterPopup.jsx
index 7c180354..68099702 100644
--- a/src/satellite/components/SatelliteProviderRegisterPopup.jsx
+++ b/src/satellite/components/SatelliteProviderRegisterPopup.jsx
@@ -1,4 +1,6 @@
import { useState, useEffect } from 'react';
+import {createPortal} from "react-dom";
+import { showToast } from '@/components/common/Toast';
import { fetchCommonCodeList } from '@/api/commonApi';
import {
saveSatelliteCompany,
@@ -56,13 +58,13 @@ export default function SatelliteProviderRegisterPopup({ companyNo, onClose, onS
// companyTypeName → companyTypeCode 역매핑
const matchedType = codeList.find(
- (opt) => opt.commonCodeTypeName === data.companyTypeName
+ (opt) => opt.commonCodeTypeName === data.companyTypeName
);
setCompanyTypeCode(matchedType?.commonCodeTypeNumber || '');
// nationalCodeName → nationalCode 역매핑
const matchedNation = NATIONAL_OPTIONS.find(
- (opt) => opt.name === data.nationalCodeName
+ (opt) => opt.name === data.nationalCodeName
);
setNationalCode(matchedNation?.code || '');
@@ -82,6 +84,19 @@ export default function SatelliteProviderRegisterPopup({ companyNo, onClose, onS
}, [companyNo]);
const handleSave = async () => {
+ if (!companyTypeCode) {
+ showToast('사업자 분류를 선택해주세요.');
+ return;
+ }
+ if (!companyName.trim()) {
+ showToast('사업자명을 입력해주세요.');
+ return;
+ }
+ if (!nationalCode) {
+ showToast('국가를 선택해주세요.');
+ return;
+ }
+
setIsSaving(true);
setError(null);
@@ -113,129 +128,130 @@ export default function SatelliteProviderRegisterPopup({ companyNo, onClose, onS
}
};
- return (
-
-
-
+ return createPortal(
+
+
+
{isEditMode ? '위성 사업자 상세' : '위성 사업자 등록'}
-
-
+
+
-
- {isLoading &&
조회 중...
}
- {error &&
{error}
}
+
+ {isLoading &&
조회 중...
}
+ {error &&
{error}
}
- {!isLoading && (
-
+ )}
+
-
-
-
-
+
+
+
+
+
-
-
+
,
+ document.body
);
}
diff --git a/src/satellite/components/SatelliteRegisterPopup.jsx b/src/satellite/components/SatelliteRegisterPopup.jsx
index 5f7c2992..b5f549f0 100644
--- a/src/satellite/components/SatelliteRegisterPopup.jsx
+++ b/src/satellite/components/SatelliteRegisterPopup.jsx
@@ -1,4 +1,6 @@
import { useState, useEffect } from 'react';
+import {createPortal} from "react-dom";
+import { showToast } from '@/components/common/Toast';
import {
fetchSatelliteCompanyList,
fetchSatelliteManageList,
@@ -120,6 +122,23 @@ export default function SatelliteRegisterPopup({ satelliteId, onClose, onSaved }
}, [companyNo, isEditMode]);
const handleSave = async () => {
+ if (!isEditMode && !satelliteManageId) {
+ showToast('사업자명/위성명을 선택해주세요.');
+ return;
+ }
+ if (!photographDate) {
+ showToast('영상 촬영일을 입력해주세요.');
+ return;
+ }
+ if (!isEditMode && !tifFile) {
+ showToast('위성영상파일을 선택해주세요.');
+ return;
+ }
+ if (!satelliteVideoName.trim()) {
+ showToast('위성영상명을 입력해주세요.');
+ return;
+ }
+
setIsSaving(true);
setError(null);
@@ -172,7 +191,7 @@ export default function SatelliteRegisterPopup({ satelliteId, onClose, onSaved }
// setPhotographDate(changedFormat);
// }
- return (
+ return createPortal(
@@ -478,6 +497,7 @@ export default function SatelliteRegisterPopup({ satelliteId, onClose, onSaved }
-
+
,
+ document.body
);
}
diff --git a/src/scss/SideComponent.scss b/src/scss/SideComponent.scss
index 21e32fd8..622d4805 100644
--- a/src/scss/SideComponent.scss
+++ b/src/scss/SideComponent.scss
@@ -416,6 +416,30 @@
}
}
+ // 위성/기상 메뉴 스크롤바 다크 테마
+ .tabBtm,
+ .tabBtmCnt {
+ &::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: transparent;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(var(--white-rgb), 0.3);
+ border-radius: 3px;
+
+ &:hover {
+ background: rgba(var(--white-rgb), 0.5);
+ }
+ }
+
+ scrollbar-width: thin;
+ scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
+ }
+
}
.toogle {