import { useState, useEffect, useMemo } from 'react'; import { fetchOrganizations } from '@tabs/assets/services/assetsApi'; import type { AssetOrgCompat } from '@tabs/assets/services/assetsApi'; import { typeTagCls } from '@tabs/assets/components/assetTypes'; const PAGE_SIZE = 20; const regionShort = (j: string) => j.includes('남해') ? '남해청' : j.includes('서해') ? '서해청' : j.includes('중부') ? '중부청' : j.includes('동해') ? '동해청' : j.includes('제주') ? '제주청' : j; function VesselMaterialsPanel() { const [organizations, setOrganizations] = useState([]); const [loading, setLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(''); const [regionFilter, setRegionFilter] = useState('전체'); const [typeFilter, setTypeFilter] = useState('전체'); const [currentPage, setCurrentPage] = useState(1); const load = () => { setLoading(true); fetchOrganizations() .then(setOrganizations) .catch(err => console.error('[VesselMaterialsPanel] 데이터 로드 실패:', err)) .finally(() => setLoading(false)); }; useEffect(() => { let cancelled = false; fetchOrganizations() .then(data => { if (!cancelled) setOrganizations(data); }) .catch(err => console.error('[VesselMaterialsPanel] 데이터 로드 실패:', err)) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, []); const typeOptions = useMemo(() => { const set = new Set(organizations.map(o => o.type)); return Array.from(set).sort(); }, [organizations]); const filtered = useMemo(() => organizations .filter(o => o.vessel > 0) .filter(o => regionFilter === '전체' || o.jurisdiction.includes(regionFilter)) .filter(o => typeFilter === '전체' || o.type === typeFilter) .filter(o => !searchTerm || o.name.includes(searchTerm) || o.address.includes(searchTerm)), [organizations, regionFilter, typeFilter, searchTerm] ); const totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE)); const safePage = Math.min(currentPage, totalPages); const paged = filtered.slice((safePage - 1) * PAGE_SIZE, safePage * PAGE_SIZE); const handleFilterChange = (setter: (v: string) => void) => (e: React.ChangeEvent) => { setter(e.target.value); setCurrentPage(1); }; const pageNumbers = (() => { const range: number[] = []; const start = Math.max(1, safePage - 2); const end = Math.min(totalPages, safePage + 2); for (let i = start; i <= end; i++) range.push(i); return range; })(); return (
{/* 헤더 */}

방제선 보유자재 현황

총 {filtered.length}개 기관 (방제선 보유)

{ setSearchTerm(e.target.value); setCurrentPage(1); }} className="w-56 px-3 py-2 text-xs bg-bg-2 border border-border rounded-md text-text-1 placeholder-text-3 focus:border-primary-cyan focus:outline-none font-korean" />
{/* 테이블 */}
{loading ? (
불러오는 중...
) : ( {paged.length === 0 ? ( ) : paged.map((org, idx) => ( ))}
번호 유형 관할청 기관명 주소 방제선 유회수기 이송펌프 방제차량 살포장치 총자산
조회된 기관이 없습니다.
{(safePage - 1) * PAGE_SIZE + idx + 1} {org.type} {regionShort(org.jurisdiction)} {org.name} {org.address} {org.vessel > 0 ? org.vessel : } {org.skimmer > 0 ? org.skimmer : } {org.pump > 0 ? org.pump : } {org.vehicle > 0 ? org.vehicle : } {org.sprayer > 0 ? org.sprayer : } {org.totalAssets.toLocaleString()}
)}
{/* 합계 */} {!loading && filtered.length > 0 && (
합계 ({filtered.length}개 기관) {[ { label: '방제선', value: filtered.reduce((s, o) => s + o.vessel, 0), unit: '척', active: true }, { label: '유회수기', value: filtered.reduce((s, o) => s + o.skimmer, 0), unit: '대', active: false }, { label: '이송펌프', value: filtered.reduce((s, o) => s + o.pump, 0), unit: '대', active: false }, { label: '방제차량', value: filtered.reduce((s, o) => s + o.vehicle, 0), unit: '대', active: false }, { label: '살포장치', value: filtered.reduce((s, o) => s + o.sprayer, 0), unit: '대', active: false }, { label: '총자산', value: filtered.reduce((s, o) => s + o.totalAssets, 0), unit: '', active: true }, ].map((t) => (
{t.label} {t.value.toLocaleString()}{t.unit}
))}
)} {/* 페이지네이션 */} {!loading && filtered.length > 0 && (
{(safePage - 1) * PAGE_SIZE + 1}–{Math.min(safePage * PAGE_SIZE, filtered.length)} / 전체 {filtered.length}개
{pageNumbers.map(p => ( ))}
)}
); } export default VesselMaterialsPanel;