ship-gis/src/satellite/components/SatelliteProviderRegisterPopup.jsx
jeonghyo.K 81255c4839 추가 수정사항 반영
1. 페이지네이션, 스크롤, 날짜 선택 ui 추가
2. 공통코드 조회 api 적용 (위성 조회, 등록 팝업 등)
3. 필수값 입력 메세지 추가
2026-02-11 16:49:26 +09:00

258 lines
8.6 KiB
JavaScript

import { useState, useEffect } from 'react';
import {createPortal} from "react-dom";
import { showToast } from '@/components/common/Toast';
import { fetchCommonCodeList } from '@/api/commonApi';
import {
saveSatelliteCompany,
fetchSatelliteCompanyDetail,
updateSatelliteCompany,
} from '@/api/satelliteApi';
import useDraggable from '../hooks/useDraggable';
const NATIONAL_OPTIONS = [
{ code: '440', name: '대한민국' },
{ code: '338', name: '미국' },
{ code: '413', name: '중국' },
{ code: '431', name: '일본' },
];
/**
* 위성 사업자 등록/수정 팝업
* @param {{ companyNo?: number, onClose: () => void, onSaved: () => void }} props
* companyNo가 있으면 수정 모드, 없으면 등록 모드
*/
export default function SatelliteProviderRegisterPopup({ companyNo, onClose, onSaved }) {
const isEditMode = !!companyNo;
const [companyTypeOptions, setCompanyTypeOptions] = useState([]);
const [companyTypeCode, setCompanyTypeCode] = useState('');
const [companyName, setCompanyName] = useState('');
const [nationalCode, setNationalCode] = useState('');
const [location, setLocation] = useState('');
const [companyDetail, setCompanyDetail] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const [error, setError] = useState(null);
const { position, handleMouseDown } = useDraggable();
// 마운트 시 사업자 분류 공통코드 로드 + 수정 모드면 상세조회
useEffect(() => {
let cancelled = false;
async function load() {
setIsLoading(true);
setError(null);
try {
// 공통코드 로드
const codeList = await fetchCommonCodeList('000039');
if (cancelled) return;
setCompanyTypeOptions(codeList);
// 수정 모드: 상세조회 후 역매핑
if (companyNo) {
const data = await fetchSatelliteCompanyDetail(companyNo);
if (cancelled || !data) return;
// companyTypeName → companyTypeCode 역매핑
const matchedType = codeList.find(
(opt) => opt.commonCodeTypeName === data.companyTypeName
);
setCompanyTypeCode(matchedType?.commonCodeTypeNumber || '');
// nationalCodeName → nationalCode 역매핑
const matchedNation = NATIONAL_OPTIONS.find(
(opt) => opt.name === data.nationalCodeName
);
setNationalCode(matchedNation?.code || '');
setCompanyName(data.companyName || '');
setLocation(data.location || '');
setCompanyDetail(data.companyDetail || '');
}
} catch {
setError('데이터 조회 중 오류가 발생했습니다.');
} finally {
if (!cancelled) setIsLoading(false);
}
}
load();
return () => { cancelled = true; };
}, [companyNo]);
const handleSave = async () => {
if (!companyTypeCode) {
showToast('사업자 분류를 선택해주세요.');
return;
}
if (!companyName.trim()) {
showToast('사업자명을 입력해주세요.');
return;
}
if (!nationalCode) {
showToast('국가를 선택해주세요.');
return;
}
setIsSaving(true);
setError(null);
try {
if (isEditMode) {
await updateSatelliteCompany({
companyNo,
companyTypeCode,
companyName,
nationalCode,
location,
companyDetail,
});
} else {
await saveSatelliteCompany({
companyTypeCode,
companyName,
nationalCode,
location,
companyDetail,
});
}
onSaved?.();
onClose();
} catch {
setError('저장 중 오류가 발생했습니다.');
} finally {
setIsSaving(false);
}
};
return createPortal(
<div className="popupUtillWrap" style={{ transform: `translate(calc(-50% + ${position.x}px), calc(-50% + ${position.y}px))` }}>
<div className="popupUtill">
<div className="puHeader" onMouseDown={handleMouseDown} style={{ cursor: 'grab' }}>
<span className="title">
{isEditMode ? '위성 사업자 상세' : '위성 사업자 등록'}
</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={onClose}
/>
</div>
<div className="puBody">
{isLoading && <div className="loading">조회 ...</div>}
{error && <div className="error">{error}</div>}
{!isLoading && (
<table className="table">
<caption>
위성 사업자 {isEditMode ? '상세' : '등록'} - 사업자 분류, 사업자명, 국가, 소재지, 상세내역에 대한 내용을 {isEditMode ? '조회/수정' : '등록'}하는 표입니다.
</caption>
<colgroup>
<col style={{ width: '30%' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">사업자 분류 <span className="required">*</span></th>
<td>
<select
aria-label="사업자 분류"
value={companyTypeCode}
onChange={(e) => setCompanyTypeCode(e.target.value)}
>
<option value="">선택</option>
{companyTypeOptions.map((opt) => (
<option key={opt.commonCodeTypeNumber} value={opt.commonCodeTypeNumber}>
{opt.commonCodeTypeName}
</option>
))}
</select>
</td>
</tr>
<tr>
<th scope="row">사업자명 <span className="required">*</span></th>
<td>
<input
type="text"
placeholder="사업자명"
aria-label="사업자명"
value={companyName}
onChange={(e) => setCompanyName(e.target.value)}
/>
</td>
</tr>
<tr>
<th scope="row">국가 <span className="required">*</span></th>
<td>
<select
aria-label="국가"
value={nationalCode}
onChange={(e) => setNationalCode(e.target.value)}
>
<option value="">선택</option>
{NATIONAL_OPTIONS.map((opt) => (
<option key={opt.code} value={opt.code}>
{opt.name}
</option>
))}
</select>
</td>
</tr>
<tr>
<th scope="row">소재지</th>
<td>
<input
type="text"
placeholder="소재지"
aria-label="소재지"
value={location}
onChange={(e) => setLocation(e.target.value)}
/>
</td>
</tr>
<tr>
<th scope="row">상세내역</th>
<td>
<textarea
placeholder="내용을 입력하세요"
aria-label="상세내역"
value={companyDetail}
onChange={(e) => setCompanyDetail(e.target.value)}
/>
</td>
</tr>
</tbody>
</table>
)}
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button
type="button"
className="btn basic"
onClick={handleSave}
disabled={isSaving}
>
{isSaving ? '저장 중...' : isEditMode ? '수정' : '저장'}
</button>
<button
type="button"
className="btn dark"
onClick={onClose}
>
취소
</button>
</div>
</div>
</div>
</div>,
document.body
);
}