import { useState, useEffect } from 'react' import type { Incident } from './IncidentsLeftPanel' import { fetchIncidentMedia } from '../services/incidentsApi' import type { MediaInfo } from '../services/incidentsApi' type MediaTab = 'all' | 'photo' | 'video' | 'satellite' | 'cctv' const MEDIA_TABS: { id: MediaTab; label: string; icon: string }[] = [ { id: 'all', label: '์ „์ฒด', icon: '' }, { id: 'photo', label: '์‚ฌ์ง„', icon: '๐Ÿ“ท' }, { id: 'video', label: '์˜์ƒ', icon: '๐ŸŽฌ' }, { id: 'satellite', label: '์œ„์„ฑ', icon: '๐Ÿ›ฐ' }, { id: 'cctv', label: 'CCTV', icon: '๐Ÿ“น' }, ] function str(obj: Record | null, key: string, fallback = 'โ€”'): string { if (!obj || obj[key] == null) return fallback; return String(obj[key]); } function num(obj: Record | null, key: string, fallback = 0): number { if (!obj || obj[key] == null) return fallback; return Number(obj[key]); } function bool(obj: Record | null, key: string): boolean { if (!obj || obj[key] == null) return false; return Boolean(obj[key]); } /* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• MediaModal โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */ export function MediaModal({ incident, onClose }: { incident: Incident; onClose: () => void }) { const [activeTab, setActiveTab] = useState('all') const [selectedCam, setSelectedCam] = useState(0) const [media, setMedia] = useState(null) useEffect(() => { fetchIncidentMedia(parseInt(incident.id)).then(setMedia); }, [incident.id]); // Timeline dots (UI constant) const timelineDots = [ { pct: 5, color: '#ef4444', label: incident.time }, { pct: 30, color: '#ef4444', label: '' }, { pct: 42, color: '#ef4444', label: '' }, { pct: 58, color: '#f59e0b', label: '' }, { pct: 78, color: '#ef4444', label: '' }, { pct: 95, color: '#6b7280', label: '' }, ] if (!media) { return (
{ if (e.target === e.currentTarget) onClose() }} style={{ position: 'fixed', inset: 0, zIndex: 10000, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.7)', backdropFilter: 'blur(6px)', }}>
ํ˜„์žฅ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...
) } const total = media.photoCnt + media.videoCnt + media.satCnt + media.cctvCnt const showPhoto = activeTab === 'all' || activeTab === 'photo' const showVideo = activeTab === 'all' || activeTab === 'video' const showSat = activeTab === 'all' || activeTab === 'satellite' const showCctv = activeTab === 'all' || activeTab === 'cctv' return (
{ if (e.target === e.currentTarget) onClose() }} style={{ position: 'fixed', inset: 0, zIndex: 10000, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.7)', backdropFilter: 'blur(6px)', }}>
{/* โ”€โ”€ Header โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */}
๐Ÿ“‹
ํ˜„์žฅ์ •๋ณด โ€” {incident.name}
{incident.name} ยท {incident.date} ยท ์‚ฌ์ง„ {media.photoCnt} / ์˜์ƒ {media.videoCnt} / ์œ„์„ฑ {media.satCnt} / CCTV {media.cctvCnt}
{/* Tabs */}
{MEDIA_TABS.map(t => ( ))}
{/* Upload */} {/* Close */} โœ•
{/* โ”€โ”€ Timeline โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */}
TIMELINE
{timelineDots.map((d, i) => (
))}
โ— ์ดˆ๊ธฐ โ— ๋Œ€์‘ โ— ์ข…๋ฃŒ
{/* โ”€โ”€ 2x2 Grid Content โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */}
{/* โ”€โ”€ Q1: ํ˜„์žฅ์‚ฌ์ง„ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */} {showPhoto && (
{/* Section header */}
๐Ÿ“ท ํ˜„์žฅ์‚ฌ์ง„ โ€” {str(media.photoMeta, 'title', 'ํ˜„์žฅ ์‚ฌ์ง„')}
{/* Photo content */}
๐Ÿ“ท
{incident.name.replace('์œ ๋ฅ˜์œ ์ถœ', '์œ ์ถœ ํ˜„์žฅ').replace('์˜ค์—ผ', 'ํ˜„์žฅ')} ํ•ด์ƒ ์‚ฌ์ง„
{str(media.photoMeta, 'date')} ยท {str(media.photoMeta, 'by')}
{/* Thumbnails */}
{Array.from({ length: Math.min(num(media.photoMeta, 'thumbCount'), 7) }).map((_, i) => (
๐Ÿ“ท
))}
๐Ÿ“ท ์‚ฌ์ง„ {num(media.photoMeta, 'thumbCount')}์žฅ ยท {str(media.photoMeta, 'stage')} ๐Ÿ”— R&D ์—ฐ๊ณ„
)} {/* โ”€โ”€ Q2: ๋“œ๋ก  ์˜์ƒ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */} {showVideo && (
๐ŸŽฌ ๋“œ๋ก  ์˜์ƒ โ€” {str(media.droneMeta, 'title', '๋“œ๋ก  ์˜์ƒ')}
โ— REC
๐ŸŽฌ
๋“œ๋ก  ํ•ญ๊ณต ์ดฌ์˜ ์˜์ƒ
{str(media.droneMeta, 'device')} ยท {str(media.droneMeta, 'alt')} ๊ณ ๋„
{/* Video controls */}
โฎ
โ–ถ
โญ 02:34 / {str(media.droneMeta, 'duration')}
๐ŸŽฌ ์˜์ƒ {num(media.droneMeta, 'videoCount')}๊ฑด ยท {str(media.droneMeta, 'stage')}
๐Ÿ“‚ ์ „์ฒด๋ณด๊ธฐ ๐Ÿ”— R&D ์—ฐ๊ณ„
)} {/* โ”€โ”€ Q3: ์œ„์„ฑ์˜์ƒ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */} {showSat && (
๐Ÿ›ฐ ์œ„์„ฑ์˜์ƒ โ€” {str(media.satMeta, 'title', '์œ„์„ฑ์˜์ƒ')}
{str(media.satMeta, 'detection') !== 'โ€”' && (
{str(media.satMeta, 'detection')}
๐Ÿ›ฐ
{str(media.satMeta, 'title', '์œ„์„ฑ์˜์ƒ')} ์œ„์„ฑ์˜์ƒ
{str(media.satMeta, 'date')} ยท ํ•ด์ƒ๋„ {str(media.satMeta, 'resolution')}
)} {str(media.satMeta, 'detection') === 'โ€”' && (
๐Ÿ›ฐ
์œ„์„ฑ์˜์ƒ ์—†์Œ
)}
{num(media.satMeta, 'thumbCount') > 0 && (
{Array.from({ length: num(media.satMeta, 'thumbCount') }).map((_, i) => (
๐Ÿ›ฐ
))}
)}
๐Ÿ›ฐ ์œ„์„ฑ {num(media.satMeta, 'thumbCount')}์žฅ ยท {str(media.satMeta, 'sensor')} ๐Ÿ” ํŽธ์ง‘/์ธก ๋น„๊ต
)} {/* โ”€โ”€ Q4: CCTV โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */} {showCctv && (
๐Ÿ“น CCTV โ€” {str(media.cctvMeta, 'title', 'CCTV')}
{bool(media.cctvMeta, 'live') && ( โ— LIVE )}
{bool(media.cctvMeta, 'live') && (
โ— LIVE {new Date().toLocaleTimeString('ko-KR', { hour12: false })}
)}
๐Ÿ“น
{str(media.cctvMeta, 'title', 'CCTV').replace('#', 'CCTV #')}
{str(media.cctvMeta, 'ptz')} ยท {str(media.cctvMeta, 'angle')} ยท {bool(media.cctvMeta, 'live') ? '์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ' : '๋…นํ™” ์˜์ƒ'}
{/* CAM buttons */}
{Array.from({ length: num(media.cctvMeta, 'camCount') }).map((_, i) => ( ))}
๐Ÿ“น CCTV {num(media.cctvMeta, 'camCount')}์ฑ„๋„ ยท {str(media.cctvMeta, 'location')}
๐Ÿ”ด ๋…นํ™”์˜์ƒ ๐ŸŽฅ PTZ
)}
{/* โ”€โ”€ Bottom Bar โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */}
๐Ÿ“ท ์‚ฌ์ง„ {media.photoCnt} ๐ŸŽฌ ์˜์ƒ {media.videoCnt} ๐Ÿ›ฐ ์œ„์„ฑ {media.satCnt} ๐Ÿ“น CCTV {media.cctvCnt} ๐Ÿ“Ž ์ด {total}๊ฑด
) } function NavBtn({ label }: { label: string }) { return ( ) } function BottomBtn({ icon, label, bg, bd, fg }: { icon: string; label: string; bg: string; bd: string; fg: string }) { return ( ) }