import { useState, useEffect, useRef, useCallback } from 'react'; import { screeningGuideApi, type ComplianceCategoryResponse, type ComplianceIndicatorResponse, } from '../../api/screeningGuideApi'; import { t } from '../../constants/screeningTexts'; interface ComplianceTabProps { lang: string; indicatorType?: 'SHIP' | 'COMPANY'; } type IndicatorType = 'SHIP' | 'COMPANY'; type CacheKey = string; const CAT_BADGE_COLORS: Record = { // SHIP 'SANCTIONS_SHIP_US_OFAC': { bg: '#e8eef5', text: '#1e3a5f' }, 'SANCTIONS_OWNERSHIP_US_OFAC': { bg: '#dbeafe', text: '#1d4ed8' }, 'SANCTIONS_SHIP_NON_US': { bg: '#d1fae5', text: '#065f46' }, 'SANCTIONS_OWNERSHIP_NON_US': { bg: '#ccfbf1', text: '#0f766e' }, 'SANCTIONS_FATF': { bg: '#ede9fe', text: '#6b21a8' }, 'SANCTIONS_OTHER': { bg: '#fee2e2', text: '#991b1b' }, 'PORT_CALLS': { bg: '#d1fae5', text: '#065f46' }, 'STS_ACTIVITY': { bg: '#ccfbf1', text: '#0f766e' }, 'SUSPICIOUS_BEHAVIOR': { bg: '#fef3c7', text: '#92400e' }, 'OWNERSHIP_SCREENING': { bg: '#e0f2fe', text: '#0c4a6e' }, 'COMPLIANCE_SCREENING_HISTORY': { bg: '#e5e7eb', text: '#374151' }, // COMPANY 'US_TREASURY_SANCTIONS': { bg: '#e8eef5', text: '#1e3a5f' }, 'NON_US_SANCTIONS': { bg: '#d1fae5', text: '#065f46' }, 'FATF_JURISDICTION': { bg: '#ede9fe', text: '#6b21a8' }, 'PARENT_COMPANY': { bg: '#fef3c7', text: '#92400e' }, 'OVERALL_COMPLIANCE_STATUS': { bg: '#dbeafe', text: '#1d4ed8' }, }; function getBadgeColor(categoryCode: string): { bg: string; text: string } { return CAT_BADGE_COLORS[categoryCode] ?? { bg: '#e5e7eb', text: '#374151' }; } export default function ComplianceTab({ lang, indicatorType: fixedType }: ComplianceTabProps) { const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [indicatorType, setIndicatorType] = useState(fixedType ?? 'SHIP'); const [expandedCategories, setExpandedCategories] = useState>(new Set()); const cache = useRef>(new Map()); const fetchData = useCallback((fetchLang: string, type: IndicatorType) => { return screeningGuideApi .getComplianceIndicators(fetchLang, type) .then((res) => { const data = res.data ?? []; cache.current.set(`${type}_${fetchLang}`, data); return data; }); }, []); useEffect(() => { setLoading(true); setError(null); setExpandedCategories(new Set()); cache.current.clear(); Promise.all([ fetchData('KO', indicatorType), fetchData('EN', indicatorType), ]) .then(() => { setCategories(cache.current.get(`${indicatorType}_${lang}`) ?? []); }) .catch((err: Error) => setError(err.message)) .finally(() => setLoading(false)); }, [indicatorType, fetchData]); useEffect(() => { const cached = cache.current.get(`${indicatorType}_${lang}`); if (cached) { setCategories(cached); } }, [lang, indicatorType]); const toggleCategory = (category: string) => { setExpandedCategories((prev) => { const next = new Set(prev); if (next.has(category)) { next.delete(category); } else { next.add(category); } return next; }); }; return (
{/* Ship / Company 토글 (fixedType이 없을 때만 표시) */} {!fixedType && (
{(['SHIP', 'COMPANY'] as const).map((type) => ( ))}
)} {loading && (
{t(lang, 'loading')}
)} {error && (
{t(lang, 'loadError')} {error}
)} {!loading && !error && (
{categories.map((cat) => { const isExpanded = expandedCategories.has(cat.categoryCode); const badge = getBadgeColor(cat.categoryCode); return (
{/* 아코디언 헤더 */} {/* 아코디언 콘텐츠 */} {isExpanded && (
{cat.indicators.map((ind) => ( ))}
)}
); })}
)}
); } function IndicatorCard({ indicator }: { indicator: ComplianceIndicatorResponse }) { return (
{indicator.fieldName}
{indicator.dataType && ( {indicator.dataType} )}
{indicator.description && (
{indicator.description}
)} {(indicator.conditionRed || indicator.conditionAmber || indicator.conditionGreen) && (
{indicator.conditionRed && (
🔴
{indicator.conditionRed}
)} {indicator.conditionAmber && (
🟡
{indicator.conditionAmber}
)} {indicator.conditionGreen && (
🟢
{indicator.conditionGreen}
)}
)}
); }