- 퀵 메뉴에 '사용자 매뉴얼' 버튼 추가 (위성영상 아래) - UserManualPopup 컴포넌트 신규 생성 (77개 화면, 8개 챕터) - 각 화면별 스크린샷 이미지 77장 포함 (/public/manual/) - 라이트박스 이미지 확대, 전체 열기/닫기, 챕터 네비게이션
1617 lines
77 KiB
TypeScript
1617 lines
77 KiB
TypeScript
import { useState } from 'react';
|
|
|
|
interface UserManualPopupProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
interface InputItem {
|
|
label: string;
|
|
type: string;
|
|
required: boolean;
|
|
desc: string;
|
|
}
|
|
|
|
interface ScreenItem {
|
|
id: string;
|
|
name: string;
|
|
menuPath: string;
|
|
imageIndex: number;
|
|
overview: string;
|
|
description?: string;
|
|
procedure?: string[];
|
|
inputs?: InputItem[];
|
|
notes?: string[];
|
|
}
|
|
|
|
interface Chapter {
|
|
id: string;
|
|
number: string;
|
|
title: string;
|
|
subtitle: string;
|
|
screens: ScreenItem[];
|
|
}
|
|
|
|
const CHAPTERS: Chapter[] = [
|
|
{
|
|
id: 'ch01',
|
|
number: '01',
|
|
title: '유출유 확산예측',
|
|
subtitle: 'Oil Spill Dispersion Prediction',
|
|
screens: [
|
|
{
|
|
id: '001',
|
|
name: '직접입력',
|
|
menuPath: '유출유확산예측 > 유출유확산분석',
|
|
imageIndex: 1,
|
|
overview:
|
|
'해양 유출유 사고 발생 시 오염원 위치와 유출 조건을 직접 입력하여 확산 범위를 예측하는 주요 분석 화면이다. KOSPS·POSEIDON·OpenDrift·앙상블의 4종 수치 모델을 선택적으로 적용할 수 있다. 실시간 기상·해양 데이터와 연계하여 즉시 예측 결과를 지도에 표출한다.',
|
|
description:
|
|
'화면 좌측에 예측정보 입력 패널(사고명, 위치, 유종, 유출량, 예측 시간)이 위치한다. 중앙 지도에서 클릭하여 사고 발생 위치를 직접 지정하거나 위·경도 좌표를 수동 입력할 수 있다. 하단 타임라인 슬라이더로 시간 경과에 따른 확산 경과를 재생할 수 있다. 우측 \'분석 요약\' 패널에서 예측 면적, 이동거리, 이동 방향, 풍화 비율 등을 확인할 수 있다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'유출유확산예측 > 유출유확산분석\'을 클릭하여 화면을 이동한다.',
|
|
'좌측 패널에서 사고명, 날짜, 유종, 유출량, 예측 시간을 입력한다.',
|
|
'지도를 클릭하거나 좌표 입력란에 위·경도를 직접 입력하여 사고 위치를 지정한다.',
|
|
'적용할 확산 모델(KOSPS·POSEIDON·OpenDrift·앙상블) 체크박스를 선택한다.',
|
|
'\'확산예측 실행\' 버튼을 클릭하여 예측을 시작한다.',
|
|
'하단 타임라인 재생 버튼으로 시간별 확산 결과를 확인한다.',
|
|
],
|
|
inputs: [
|
|
{ label: '사고명', type: '텍스트', required: true, desc: '사고 식별 명칭' },
|
|
{ label: '날짜/시간', type: '날짜+시간', required: true, desc: '사고 발생 일시' },
|
|
{ label: '위도/경도', type: '숫자', required: true, desc: '지도 클릭 자동 입력 가능' },
|
|
{ label: '유종', type: '드롭다운', required: true, desc: '벙커C유·경유·연료유 등' },
|
|
{ label: '유출량', type: '숫자 kL', required: true, desc: '유출 유류 총량' },
|
|
{ label: '예측 시간', type: '숫자 h', required: true, desc: '확산 예측 기간' },
|
|
{ label: '적용 모델', type: '체크박스', required: true, desc: 'KOSPS·POSEIDON·OpenDrift·앙상블 중 선택' },
|
|
],
|
|
notes: [
|
|
'좌표 입력 후 반드시 \'적용\' 버튼을 클릭해야 지도에 반영된다.',
|
|
'앙상블 모델 선택 시 3개 모델이 동시 실행되어 계산 시간이 증가할 수 있다.',
|
|
'유출량이 0 이하이거나 예측 시간이 입력되지 않으면 실행 버튼이 비활성화된다.',
|
|
],
|
|
},
|
|
{
|
|
id: '002',
|
|
name: '모델종류별 확산예측 실행',
|
|
menuPath: '유출유확산예측 > 유출유확산분석',
|
|
imageIndex: 2,
|
|
overview:
|
|
'KOSPS·POSEIDON·OpenDrift 3개 모델의 예측 결과를 동시에 지도에 표출하여 모델 간 비교 분석을 지원한다. 모델별 입자 궤적(파란점·빨간점·하늘점)이 중첩 표시되어 확산 경향의 편차를 한눈에 파악할 수 있다.',
|
|
description:
|
|
'지도에 모델별 색상 구분 입자가 동시에 표시되며, 각 모델의 확산 방향과 범위 차이를 시각적으로 비교할 수 있다. 우측 \'분석 요약\' 패널에 예측 면적(km2), 이동거리(km), 방향, 이동속도(cm/s)가 표출된다. 풍화 상태 막대그래프(유출량·해면연소·자연분산·수중분산·잔류 비율)를 제공한다. \'다각형 분석수행\' 버튼으로 특정 구역 내 오염 면적을 별도 산정할 수 있다.',
|
|
procedure: [
|
|
'확산분석 화면에서 적용 모델 체크박스를 2개 이상 선택한다.',
|
|
'\'확산예측 실행\' 버튼을 클릭한다.',
|
|
'지도에서 각 모델의 입자 분포와 확산 범위를 비교한다.',
|
|
'우측 \'분석 요약\' 탭에서 모델별 정량 지표를 확인한다.',
|
|
'하단 타임라인으로 시간 경과별 확산 변화를 재생한다.',
|
|
],
|
|
notes: [
|
|
'여러 모델을 동시에 선택하면 계산 자원 소모가 증가하여 결과 표출까지 수 분이 소요될 수 있다.',
|
|
'모델별 예측 결과에 차이가 있을 경우, 앙상블 결과 또는 현장 데이터와 교차 검토하여 활용한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '003',
|
|
name: '사고정보 조회',
|
|
menuPath: '유출유확산예측 > 유출유확산분석',
|
|
imageIndex: 3,
|
|
overview:
|
|
'시스템에 등록된 실제 사고 정보를 불러와 확산분석에 자동 연동하는 기능이다. 사고코드·선박명·유종·유출량·발생 좌표 등이 자동으로 예측정보 입력란에 채워진다.',
|
|
description:
|
|
'좌측 \'사고정보\' 패널에 진행 중인 사고 목록이 표시된다. 사고를 선택하면 지도 중심이 해당 사고 발생 위치로 자동 이동하고 위치 핀이 표시된다. 사고 상태는 \'진행중(빨간 배지)\'과 \'종료(회색 배지)\'로 구분된다.',
|
|
procedure: [
|
|
'좌측 \'사고정보\' 패널의 펼침 버튼을 클릭하여 패널을 연다.',
|
|
'목록에서 분석할 사고를 클릭한다.',
|
|
'사고 정보가 예측정보 입력란에 자동으로 입력되었는지 확인한다.',
|
|
'필요 시 유출량·예측 시간 등 추가 정보를 수정한다.',
|
|
'\'확산예측 실행\'을 클릭하여 분석을 시작한다.',
|
|
],
|
|
notes: [
|
|
'\'진행중\' 상태의 사고만 실시간 확산예측과 연동된다.',
|
|
'사고 데이터는 관계 기관으로부터 등록된 정보를 기반으로 하며, 입력 오류 시 관리자에게 문의한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '004',
|
|
name: '영향 민감자원 레이어 중첩 표시',
|
|
menuPath: '유출유확산예측 > 유출유확산분석',
|
|
imageIndex: 4,
|
|
overview:
|
|
'유출유 확산 예측 결과와 해양 민감자원(환경생태·수산자원·관광지 등) 레이어를 지도에 동시 표출하는 기능이다. 잠재적 피해 자원을 사전에 파악하여 방제 우선순위 설정에 활용한다.',
|
|
description:
|
|
'좌측 하단 \'정보 레이어\' 패널에서 환경생태·사회경제·민감도평가·해경관할구역 등 대분류 토글을 제공한다. 각 항목 우측에 해당 레이어 데이터 건수가 표시된다. \'전체 켜기/끄기\' 버튼으로 모든 레이어를 일괄 제어할 수 있다.',
|
|
procedure: [
|
|
'좌측 하단 \'정보 레이어\' 패널을 열고 원하는 레이어 항목을 활성화한다.',
|
|
'확산예측 실행 후 지도에 표출된 오염 범위와 레이어 분포를 비교한다.',
|
|
'오염 영향이 우려되는 자원 레이어를 클릭하여 상세 정보를 확인한다.',
|
|
'필요한 레이어 조합을 선택하여 보고서용 지도 캡처에 활용한다.',
|
|
],
|
|
notes: [
|
|
'레이어 수가 많을 경우 지도 로딩 속도가 느려질 수 있으므로 필요한 레이어만 선택적으로 활성화한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '005',
|
|
name: 'AI자동추천 배치안 적용',
|
|
menuPath: '유출유확산예측 > 유출유확산분석 > 오일펜스 배치 가이드',
|
|
imageIndex: 5,
|
|
overview:
|
|
'확산예측 결과를 기반으로 AI(NSGA-II 알고리즘)가 최적 오일펜스 배치안을 자동으로 생성하는 기능이다. 최대 3개 방어선의 위치·방향·총 길이·차단율을 자동 계산하여 지도에 표출한다.',
|
|
description:
|
|
'\'오일펜스 배치 가이드\' 패널 상단 \'AI자동 추천\' 탭에서 배치 결과를 확인한다. 1차~3차 방어선별 배치 위치(좌표), 방향, 오일펜스 길이, 예상 차단율이 표시된다.',
|
|
procedure: [
|
|
'확산예측 실행 완료 후 좌측 \'오일펜스 배치 가이드\' 패널을 연다.',
|
|
'\'AI자동 추천\' 탭을 선택하여 추천 배치안을 확인한다.',
|
|
'방어선별 차단율 및 오일펜스 총 길이를 검토한다.',
|
|
'\'추천 배치안 적용하기\' 버튼을 클릭하여 지도에 표출한다.',
|
|
'현장 여건(조류·지형·자원 현황)을 고려하여 최종 배치안을 확정한다.',
|
|
],
|
|
inputs: [
|
|
{ label: '배치 수', type: '숫자', required: false, desc: '투입할 오일펜스 방어선 수' },
|
|
{ label: '최소 차단율', type: '숫자 %', required: false, desc: '목표 차단율' },
|
|
],
|
|
notes: [
|
|
'AI 추천 기능은 확산예측이 완료된 후에 활성화된다.',
|
|
'추천 배치안은 수치 모델 기반 결과로, 현장 조류 변동·수심·접근 가능성 등을 반드시 현장 담당자가 최종 확인해야 한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '006',
|
|
name: '이미지업로드',
|
|
menuPath: '유출유확산예측 > 유출유확산분석',
|
|
imageIndex: 6,
|
|
overview:
|
|
'위성·드론·항공기에서 촬영한 이미지를 업로드하여 실제 오염 현황과 모델 예측 결과를 비교 분석하는 기능이다.',
|
|
description:
|
|
'예측정보 입력 패널 상단 \'이미지 업로드\' 탭을 선택하면 파일 업로드 영역이 활성화된다. 업로드된 이미지는 지도에 자동으로 오버레이 표출된다.',
|
|
procedure: [
|
|
'예측정보 입력 패널에서 \'이미지 업로드\' 탭을 선택한다.',
|
|
'\'파일 선택\' 버튼을 클릭하거나 파일을 드래그하여 업로드한다.',
|
|
'업로드된 이미지가 지도에 올바르게 표출되는지 위치를 확인한다.',
|
|
'확산예측 결과와 이미지를 동시에 표출하여 비교한다.',
|
|
],
|
|
inputs: [
|
|
{ label: '업로드 파일', type: '파일 PNG/JPG', required: false, desc: '위성·드론·항공기 촬영 이미지' },
|
|
],
|
|
notes: [
|
|
'지원 파일 형식은 PNG, JPG이다.',
|
|
'업로드한 이미지의 좌표 기준(GCP)이 없을 경우 지도 상 위치 정확도가 낮을 수 있다.',
|
|
],
|
|
},
|
|
{
|
|
id: '007',
|
|
name: '분석 목록',
|
|
menuPath: '유출유확산예측',
|
|
imageIndex: 7,
|
|
overview:
|
|
'완료 또는 진행 중인 유출유 확산예측 분석 이력을 목록 형식으로 조회하는 화면이다. 사고명·날짜·유종·유출량·모델 상태 등을 한눈에 파악할 수 있다.',
|
|
description:
|
|
'목록에는 번호·사고명·사고일시·예측시간·유종·유출량·모델별 상태(KOSPS·POSEIDON·OPENDRIFT)·담당자 등이 표시된다. 모델 상태는 \'완료(녹색 배지)\'와 \'대기(회색 배지)\'로 구분된다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'유출유확산예측\'을 클릭하여 분석 목록 화면으로 이동한다.',
|
|
'검색창에 사고명을 입력하거나 페이지를 이동하여 원하는 분석을 찾는다.',
|
|
'사고명 링크를 클릭하여 해당 분석 결과 상세 화면으로 이동한다.',
|
|
'신규 분석이 필요한 경우 우측 상단 \'+ 새 분석\' 버튼을 클릭한다.',
|
|
],
|
|
notes: [
|
|
'분석 결과 삭제 시 복구가 불가하므로, 삭제 전 보고서 출력 또는 데이터 저장 여부를 확인한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '008',
|
|
name: '분석 조회(test001)',
|
|
menuPath: '유출유확산예측 > 분석 목록',
|
|
imageIndex: 8,
|
|
overview:
|
|
'분석 목록에서 특정 분석 건을 선택하면 해당 확산분석 결과 화면으로 이동하여 상세 내용을 조회한다. 이전 분석 결과를 재검토하거나 조건 변경 후 재분석할 수 있다.',
|
|
procedure: [
|
|
'분석 목록에서 조회할 사고명 링크를 클릭한다.',
|
|
'로드된 분석 정보를 확인한다.',
|
|
'필요 시 입력 조건을 수정하고 \'확산예측 실행\'을 다시 클릭하여 재분석한다.',
|
|
'결과를 보고서로 출력하거나 저장한다.',
|
|
],
|
|
notes: [
|
|
'기존 분석에서 모델 조건 변경 후 재실행 시 이전 결과가 덮어써질 수 있으므로, 원본 결과를 먼저 저장한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '009',
|
|
name: '유출유확산모델 이론 - 시스템 개요',
|
|
menuPath: '유출유확산예측 > 유출유확산모델 이론',
|
|
imageIndex: 9,
|
|
overview:
|
|
'Wing 시스템에 탑재된 유출유 확산 수치 모델의 개요와 운용 체계를 안내하는 이론 화면이다. KOSPS·POSEIDON·OpenDrift 3종 모델의 특징·비교·데이터 흐름을 확인할 수 있다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'유출유확산예측 > 유출유확산모델 이론\'을 클릭한다.',
|
|
'\'시스템 개요\' 탭을 선택하여 전체 모델 체계를 확인한다.',
|
|
'각 탭(KOSPS·POSEIDON·OpenDrift 등)을 클릭하여 상세 이론을 열람한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '010',
|
|
name: '유출유확산모델 이론 - KOSPS',
|
|
menuPath: '유출유확산예측 > 유출유확산모델 이론',
|
|
imageIndex: 10,
|
|
overview:
|
|
'한국해양과학기술원(KIOST)이 개발한 KOSPS 모델의 상세 이론을 안내한다. 국내 연안 특성에 최적화된 조류예측(CHARRY) 및 풍류 경험식 적용 원리를 설명한다.',
|
|
notes: [
|
|
'KOSPS는 국내 연안 중심으로 검증된 모델로, 원해·심해 적용 시 정확도 제한이 있을 수 있다.',
|
|
],
|
|
},
|
|
{
|
|
id: '011',
|
|
name: '유출유확산모델 이론 - POSEIDON',
|
|
menuPath: '유출유확산예측 > 유출유확산모델 이론',
|
|
imageIndex: 11,
|
|
overview:
|
|
'입자추적 최적화 예측 시스템 POSEIDON의 이론 및 MOHID 3D 해양순환모델 적용 원리를 안내한다. GA·DE·HS·PSO 등 4종 최적화 알고리즘을 통한 파라미터 자동 최적화 방법을 설명한다.',
|
|
},
|
|
{
|
|
id: '012',
|
|
name: '유출유확산모델 이론 - OpenDrift',
|
|
menuPath: '유출유확산예측 > 유출유확산모델 이론',
|
|
imageIndex: 12,
|
|
overview:
|
|
'노르웨이 MET Norway가 개발한 오픈소스 라그랑지안 확산 프레임워크 OpenDrift의 이론을 안내한다. NEMO·ROMS·HYCOM·Copernicus CMEMS 등 다양한 해양 예보 모델과 연동 가능한 범용성을 설명한다.',
|
|
},
|
|
{
|
|
id: '013',
|
|
name: '유출유확산모델 이론 - 입자추적법',
|
|
menuPath: '유출유확산예측 > 유출유확산모델 이론',
|
|
imageIndex: 13,
|
|
overview:
|
|
'유출유 수치 모델에 적용되는 라그랑지안 입자추적법(Lagrangian Particle Tracking Method)의 수학적 이론과 수식을 안내한다.',
|
|
},
|
|
{
|
|
id: '014',
|
|
name: '유출유확산모델 이론 - 풍화 프로세스',
|
|
menuPath: '유출유확산예측 > 유출유확산모델 이론',
|
|
imageIndex: 14,
|
|
overview:
|
|
'유출유가 시간 경과에 따라 겪는 풍화(Weathering) 프로세스(증발·유화·자연분산 등)의 이론과 타임라인을 안내한다.',
|
|
notes: [
|
|
'풍화 속도는 유종·기온·해수온·파랑 조건에 따라 크게 달라질 수 있으므로 참고용으로 활용한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '015',
|
|
name: '유출유확산모델 이론 - 해양환경 입력',
|
|
menuPath: '유출유확산예측 > 유출유확산모델 이론',
|
|
imageIndex: 15,
|
|
overview:
|
|
'유출유 확산 수치 모델에 사용되는 해양환경 입력 데이터(기상·해류·조류·해수면 온도) 체계를 안내한다. KMA RDAPS·ECMWF·NIFS ROMS·HYCOM·TPXO9 등 데이터 소스 설명.',
|
|
},
|
|
{
|
|
id: '016',
|
|
name: '유출유확산모델 이론 - 모델 검증',
|
|
menuPath: '유출유확산예측 > 유출유확산모델 이론',
|
|
imageIndex: 16,
|
|
overview:
|
|
'유출유 확산 모델의 정확도 검증 사례(2007 허베이스피리트, 2014 무이산 등)와 RMSE·Skill Score 통계 지표를 안내한다.',
|
|
},
|
|
{
|
|
id: '017',
|
|
name: '유출유확산모델 이론 - 앙상블',
|
|
menuPath: '유출유확산예측 > 유출유확산모델 이론',
|
|
imageIndex: 17,
|
|
overview:
|
|
'KOSPS·POSEIDON·OpenDrift 3종 모델의 결과를 통합하는 앙상블 예측 방법론과 가중평균 알고리즘을 안내한다. 최악 시나리오(Worst Case) 산출 방법도 설명한다.',
|
|
},
|
|
{
|
|
id: '018',
|
|
name: '유출유확산모델 이론 - 발전 방향',
|
|
menuPath: '유출유확산예측 > 유출유확산모델 이론',
|
|
imageIndex: 18,
|
|
overview:
|
|
'현재 유출유 확산 모델의 한계와 향후 개선 방향(4단계 로드맵)을 안내한다.',
|
|
},
|
|
{
|
|
id: '019',
|
|
name: '오일펜스 배치 알고리즘 이론 - 개요',
|
|
menuPath: '유출유확산예측 > 오일펜스 배치 알고리즘 이론',
|
|
imageIndex: 19,
|
|
overview:
|
|
'오일펜스 최적 배치 알고리즘의 전체 개요와 최적화 목표(차단 면적 최대화·도달시간 최소화·자원 효율화)를 안내한다.',
|
|
},
|
|
{
|
|
id: '020',
|
|
name: '오일펜스 배치 알고리즘 이론 - 배치 이론',
|
|
menuPath: '유출유확산예측 > 오일펜스 배치 알고리즘 이론',
|
|
imageIndex: 20,
|
|
overview:
|
|
'차단 효율 함수(E(theta, U))와 V형·U형·J형 배치 형태별 이론적 차단 원리를 안내한다. 다단계 차단선 배치 시 총 차단 효율 산출 공식도 포함한다.',
|
|
},
|
|
{
|
|
id: '021',
|
|
name: '오일펜스 배치 알고리즘 이론 - 최적화 알고리즘',
|
|
menuPath: '유출유확산예측 > 오일펜스 배치 알고리즘 이론',
|
|
imageIndex: 21,
|
|
overview:
|
|
'NSGA-II(Non-dominated Sorting Genetic Algorithm II) 기반 다목적 최적화 알고리즘의 원리와 보조 알고리즘(PSO·Greedy) 비교를 안내한다.',
|
|
},
|
|
{
|
|
id: '022',
|
|
name: '오일펜스 배치 알고리즘 이론 - 유체역학 모델',
|
|
menuPath: '유출유확산예측 > 오일펜스 배치 알고리즘 이론',
|
|
imageIndex: 22,
|
|
overview:
|
|
'오일펜스 차단 성능 평가에 사용되는 유체역학 모델의 이론을 안내한다.',
|
|
},
|
|
{
|
|
id: '023',
|
|
name: '오일펜스 배치 알고리즘 이론 - 현장 적용',
|
|
menuPath: '유출유확산예측 > 오일펜스 배치 알고리즘 이론',
|
|
imageIndex: 23,
|
|
overview:
|
|
'오일펜스 배치 알고리즘의 실제 사고 현장 적용 사례와 현장 운용 시 고려사항을 안내한다.',
|
|
},
|
|
{
|
|
id: '024',
|
|
name: '오일펜스 배치 알고리즘 이론 - 참고문헌',
|
|
menuPath: '유출유확산예측 > 오일펜스 배치 알고리즘 이론',
|
|
imageIndex: 24,
|
|
overview:
|
|
'오일펜스 배치 알고리즘 이론에 사용된 학술 논문·기술 보고서·국제 기준 등 참고문헌 목록을 안내한다.',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'ch02',
|
|
number: '02',
|
|
title: 'HNS·대기확산',
|
|
subtitle: 'HNS Atmospheric Dispersion',
|
|
screens: [
|
|
{
|
|
id: '025',
|
|
name: '대기확산예측 실행',
|
|
menuPath: 'HNS대기확산 > 대기확산분석',
|
|
imageIndex: 25,
|
|
overview:
|
|
'HNS(위험유해물질) 해상 유출 시 대기 확산 범위, 위험 농도 구역, 영향 인구를 예측하는 핵심 분석 화면이다. ALOHA(EPA) 또는 이문진박사모델 두 가지 알고리즘 중 선택하여 가우시안 Plume/Puff 모델을 적용한다.',
|
|
description:
|
|
'화면 좌측에 사고 기본정보·물질 및 유출 조건·기상조건 입력 패널이 위치한다. 중앙 지도에 AEGL-1~3 위험 구역이 색상별 Plume 형태로 표출된다. 우측 \'예측 결과\' 패널에 최대 농도(ppm), AEGL-1 영향 면적, 위험 등급 등이 표시된다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'HNS대기확산 > 대기확산분석\'을 클릭한다.',
|
|
'사고 기본정보(사고명·날짜·시간·위치)를 입력한다.',
|
|
'HNS 물질 종류 및 누출 방식·유출량을 선택·입력한다.',
|
|
'기상조건(풍향·풍속·기온·대기안정도·예측 시간)을 입력한다.',
|
|
'적용 알고리즘(ALOHA/이문진박사모델)을 선택한다.',
|
|
'\'확산예측 실행\' 버튼을 클릭한다.',
|
|
'지도의 위험 구역 Plume과 우측 예측 결과 패널을 확인한다.',
|
|
],
|
|
inputs: [
|
|
{ label: '사고명', type: '텍스트', required: true, desc: '사고 식별 명칭' },
|
|
{ label: '사고 일시', type: '날짜+시간', required: true, desc: '사고 발생 일시' },
|
|
{ label: '위도/경도', type: '숫자', required: true, desc: '사고 발생 지점 좌표' },
|
|
{ label: 'HNS 물질', type: '드롭다운', required: true, desc: 'AEGL/ERPG 기준값 자동 로드' },
|
|
{ label: '누출 방식', type: '라디오', required: true, desc: '순간 유출 또는 지속 유출' },
|
|
{ label: '유출량', type: '숫자', required: true, desc: 'ton 또는 g/s 단위' },
|
|
{ label: '풍향', type: '숫자', required: true, desc: '0~360도' },
|
|
{ label: '풍속', type: '숫자 m/s', required: true, desc: '최소 0.5 m/s 이상' },
|
|
{ label: '기온', type: '숫자', required: true, desc: '현재 기온' },
|
|
{ label: '대기안정도', type: '선택 A~F', required: true, desc: 'Pasquill-Gifford 분류' },
|
|
{ label: '예측 시간', type: '숫자 h', required: true, desc: '확산 예측 기간' },
|
|
{ label: '적용 알고리즘', type: '라디오', required: true, desc: 'ALOHA 또는 이문진박사모델' },
|
|
],
|
|
notes: [
|
|
'풍속은 최소 0.5 m/s 이상 입력해야 하며 0 입력 시 오류 발생.',
|
|
'HNS 물질 선택 후 AEGL/ERPGs 기준값이 자동 로드되었는지 반드시 확인.',
|
|
],
|
|
},
|
|
{
|
|
id: '026',
|
|
name: 'HNS 분석 목록',
|
|
menuPath: 'HNS대기확산',
|
|
imageIndex: 26,
|
|
overview:
|
|
'완료된 HNS 대기확산 분석 이력을 목록 형식으로 조회하는 화면이다. AEGL-1~3 거리·위험 등급·피해반경·담당자 등 주요 결과 정보를 한눈에 파악할 수 있다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'HNS대기확산\'을 클릭하여 분석 목록으로 이동한다.',
|
|
'검색창에서 분석명을 검색하거나 목록을 스크롤하여 원하는 분석을 찾는다.',
|
|
'분석명 링크를 클릭하여 해당 분석 결과 지도 화면으로 이동한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '027',
|
|
name: '시나리오 상세',
|
|
menuPath: 'HNS대기확산 > 시나리오 관리',
|
|
imageIndex: 27,
|
|
overview:
|
|
'단일 HNS 사고에 대해 여러 기상·유출 조건 시나리오(S-01~S-05 등)를 생성·비교 관리하는 화면이다. 시나리오별 위험도·위험 구역·대응 권고사항을 한눈에 비교할 수 있다.',
|
|
description:
|
|
'시나리오 목록 카드에 시나리오명·위험도 배지(CRITICAL/HIGH/MEDIUM/RESOLVED)·최대농도·IDLH 거리·ERPG-2 거리·영향인구 요약이 표시된다.',
|
|
procedure: [
|
|
'\'HNS대기확산 > 시나리오 관리\'를 클릭하여 이동한다.',
|
|
'원하는 사고 건을 선택하면 시나리오 목록이 표시된다.',
|
|
'시나리오 카드를 클릭하여 상세 위험 구역과 대응 권고사항을 확인한다.',
|
|
'\'비교 차트\' 탭으로 이동하여 시나리오 간 시간별 변화를 비교한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '028',
|
|
name: '비교 차트',
|
|
menuPath: 'HNS대기확산 > 시나리오 관리',
|
|
imageIndex: 28,
|
|
overview:
|
|
'생성된 여러 HNS 시나리오의 최대 지표면 농도·위험 반경·영향 인구를 시간별 변화 차트로 비교하는 화면이다.',
|
|
description:
|
|
'상단은 시나리오별 최대 지표면 농도(ppm) 시간별 변화 그래프. 중단에 위험 반경(km)과 영향 인구(명) 변화 차트. 하단에 피대농도·IDLH 거리·ERPG-2 반경·영향인구·풍향/풍속·위험등급 항목별 비교표가 제공된다.',
|
|
},
|
|
{
|
|
id: '029',
|
|
name: '확산범위 오버레이',
|
|
menuPath: 'HNS대기확산 > 시나리오 관리',
|
|
imageIndex: 29,
|
|
overview:
|
|
'HNS 대기확산 시나리오의 시간대별(T+0h~T+4h) 확산 범위를 지도에 오버레이 표출하는 화면이다.',
|
|
procedure: [
|
|
'시나리오 관리 화면에서 \'확산범위 오버레이\' 탭을 클릭한다.',
|
|
'지도에서 시간대별 확산 범위를 확인한다.',
|
|
'슬라이더를 이동하여 원하는 시간대의 확산 상태를 조회한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '030',
|
|
name: '신규 시나리오',
|
|
menuPath: 'HNS대기확산 > 시나리오 관리',
|
|
imageIndex: 30,
|
|
overview:
|
|
'기상·유출 조건을 변경하여 새로운 HNS 대기확산 시나리오를 생성하는 입력 모달 화면이다.',
|
|
procedure: [
|
|
'시나리오 관리 화면 우측 상단 \'신규 시나리오\' 버튼을 클릭한다.',
|
|
'시나리오명과 기준 시각을 입력한다.',
|
|
'HNS 물질·누출 구분·유출량을 입력한다.',
|
|
'기상조건(풍향·풍속·기온·대기안정도)을 입력한다.',
|
|
'\'시나리오 생성 및 예측 실행\'을 클릭한다.',
|
|
],
|
|
inputs: [
|
|
{ label: '시나리오명', type: '텍스트', required: true, desc: '시나리오 식별 명칭' },
|
|
{ label: 'HNS 물질', type: '드롭다운', required: true, desc: '유출 물질 선택' },
|
|
{ label: '누출 구분', type: '라디오', required: true, desc: '순간/지속' },
|
|
{ label: '유출량', type: '숫자', required: true, desc: 'ton 또는 g/s' },
|
|
{ label: '풍향/풍속', type: '숫자', required: true, desc: '각도와 m/s' },
|
|
{ label: '기온', type: '숫자', required: true, desc: '섭씨' },
|
|
{ label: '대기안정도', type: '선택 A~F', required: true, desc: 'Pasquill-Gifford' },
|
|
],
|
|
},
|
|
{
|
|
id: '031',
|
|
name: 'HNS 대응매뉴얼',
|
|
menuPath: 'HNS대기확산',
|
|
imageIndex: 31,
|
|
overview:
|
|
'해양 HNS 사고 대응 절차를 정리한 Marine HNS Response Manual(Bonn Agreement·HELCOM·REMPEC 기반)을 시스템 내에서 직접 열람하는 화면이다. 8개 챕터. SEBC 거동 분류 5유형 카드.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'HNS대기확산 > HNS 대응매뉴얼\'을 클릭한다.',
|
|
'원하는 챕터 카드를 클릭하여 세부 내용을 확인한다.',
|
|
'하단 SEBC 분류 카드를 클릭하여 거동 유형별 대응 방법을 확인한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '032',
|
|
name: 'HNS 확산모델 이론 - 시스템 개요',
|
|
menuPath: 'HNS대기확산 > 확산모델 이론',
|
|
imageIndex: 32,
|
|
overview:
|
|
'Wing에 탑재된 HNS 대기확산 모델(WRF-Chem·Gaussian Plume/Puff·ROMS 연동)의 전체 체계와 처리 흐름을 안내한다.',
|
|
},
|
|
{
|
|
id: '033',
|
|
name: 'HNS 확산모델 이론 - 가우시안 모델',
|
|
menuPath: 'HNS대기확산 > 확산모델 이론',
|
|
imageIndex: 33,
|
|
overview:
|
|
'HNS 대기확산 예측에 적용되는 Gaussian Plume·Puff 모델의 수학적 이론과 Pasquill-Gifford 대기안정도 분류 체계를 안내한다.',
|
|
},
|
|
{
|
|
id: '034',
|
|
name: 'HNS 확산모델 이론 - 물질별 시나리오',
|
|
menuPath: 'HNS대기확산 > 확산모델 이론',
|
|
imageIndex: 34,
|
|
overview:
|
|
'주요 HNS 물질(NH3·CH3OH·H2·LNG 등)의 물질 특성 및 대기확산 시나리오 적용 기준을 안내한다.',
|
|
},
|
|
{
|
|
id: '035',
|
|
name: 'HNS 확산모델 이론 - 해양환경 보정',
|
|
menuPath: 'HNS대기확산 > 확산모델 이론',
|
|
imageIndex: 35,
|
|
overview:
|
|
'해양 환경 특성(해풍·육풍 순환·해수면 거칠기·SST 영향·MABL 구조)에 따른 대기확산 모델 보정 인자를 안내한다.',
|
|
},
|
|
{
|
|
id: '036',
|
|
name: 'HNS 확산모델 이론 - 모델 검증',
|
|
menuPath: 'HNS대기확산 > 확산모델 이론',
|
|
imageIndex: 36,
|
|
overview:
|
|
'HNS 대기확산 모델의 실측값 대비 예측 정확도 검증 결과(ALOHA·이문진박사모델·실측값 3종 비교)를 안내한다.',
|
|
},
|
|
{
|
|
id: '037',
|
|
name: 'HNS 확산모델 이론 - 실시간 비교',
|
|
menuPath: 'HNS대기확산 > 확산모델 이론',
|
|
imageIndex: 37,
|
|
overview:
|
|
'동일 조건에서 ALOHA와 이문진박사모델의 예측 결과를 실시간으로 비교하는 시뮬레이션 화면이다.',
|
|
},
|
|
{
|
|
id: '038',
|
|
name: 'HNS 확산모델 이론 - WRF-Chem 발전',
|
|
menuPath: 'HNS대기확산 > 확산모델 이론',
|
|
imageIndex: 38,
|
|
overview:
|
|
'WRF-Chem 기상-화학 연계 모델 및 ROMS 해양확산 수치모의 검증 결과와 고도화 방향을 안내한다.',
|
|
},
|
|
{
|
|
id: '039',
|
|
name: 'HNS물질정보 - SEBC 거동분류',
|
|
menuPath: 'HNS대기확산 > HNS물질정보',
|
|
imageIndex: 39,
|
|
overview:
|
|
'HNS 물질의 해양 거동 특성을 SEBC 기준으로 분류한 정보를 제공한다. G(기체)·E(증발)·F(부유)·D(용해)·S(침강) 5가지 기본 유형과 복합 거동 유형을 안내한다.',
|
|
},
|
|
{
|
|
id: '040',
|
|
name: 'HNS물질정보 - 주요 물질 특성',
|
|
menuPath: 'HNS대기확산 > HNS물질정보',
|
|
imageIndex: 40,
|
|
overview:
|
|
'NH3·CH4·H2·페놀·톨루엔 등 주요 HNS 물질의 상세 물리화학적 특성을 카드 형식으로 제공하는 화면이다.',
|
|
},
|
|
{
|
|
id: '041',
|
|
name: 'HNS물질정보 - 위험도 기준',
|
|
menuPath: 'HNS대기확산 > HNS물질정보',
|
|
imageIndex: 41,
|
|
overview:
|
|
'HNS 물질의 위험도 평가에 사용되는 AEGL·ERPG·IDLH·TWA 등 주요 독성 기준값 체계를 안내한다.',
|
|
},
|
|
{
|
|
id: '042',
|
|
name: 'HNS물질정보 - 물질 상세검색',
|
|
menuPath: 'HNS대기확산 > HNS물질정보',
|
|
imageIndex: 42,
|
|
overview:
|
|
'HNS 6,500여 종의 물질 데이터베이스에서 CAS 번호·물질명·거동 분류 조건으로 물질을 검색하는 화면이다.',
|
|
inputs: [
|
|
{ label: '검색어', type: '텍스트', required: true, desc: 'CAS 번호 또는 물질명' },
|
|
{ label: '거동 분류', type: '체크박스', required: false, desc: 'SEBC 유형 필터' },
|
|
],
|
|
notes: [
|
|
'CAS 번호 입력 시 하이픈(-) 포함 정확한 형식으로 입력해야 검색된다.',
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'ch03',
|
|
number: '03',
|
|
title: '긴급구난',
|
|
subtitle: 'Emergency Rescue',
|
|
screens: [
|
|
{
|
|
id: '043',
|
|
name: '긴급구난예측',
|
|
menuPath: '긴급구난',
|
|
imageIndex: 43,
|
|
overview:
|
|
'해상 조난자·표류 선박·표류 물체의 위치를 예측하는 긴급구난(SAR) 분석 화면이다. 해류·바람·파랑 데이터 기반 라그랑지안 표류 궤적 시뮬레이션을 수행한다.',
|
|
description:
|
|
'화면 좌측에 사고 발생 위치·일시·표류체 종류·예측 시간·기상조건 입력 패널이 위치한다. 중앙 지도에 표류 예측 궤적과 탐색 구역(최우선·일반)이 표출된다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'긴급구난\'을 클릭한다.',
|
|
'사고 발생 위치(위경도)와 일시를 입력한다.',
|
|
'표류체 종류(선박/사람/컨테이너 등)를 선택한다.',
|
|
'예측 시간과 기상 조건을 입력한다.',
|
|
'\'예측 실행\' 버튼을 클릭하여 표류 궤적과 탐색 구역을 확인한다.',
|
|
],
|
|
inputs: [
|
|
{ label: '사고 위치', type: '숫자', required: true, desc: '위·경도' },
|
|
{ label: '사고 일시', type: '날짜+시간', required: true, desc: '발생 일시' },
|
|
{ label: '표류체 종류', type: '드롭다운', required: true, desc: '선박·사람·컨테이너·구명정 등' },
|
|
{ label: '예측 시간', type: '숫자 h', required: true, desc: '표류 예측 기간' },
|
|
{ label: '기상조건', type: '숫자', required: false, desc: '미입력 시 실시간 자동 적용' },
|
|
],
|
|
notes: [
|
|
'표류체 종류 선택이 부적절한 경우 예측 정확도가 낮아질 수 있다.',
|
|
'신속한 대응을 위해 입력 완료 즉시 실행하고 기상 변화 시 재분석을 수행한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '044',
|
|
name: '긴급구난 목록',
|
|
menuPath: '긴급구난',
|
|
imageIndex: 44,
|
|
overview:
|
|
'완료 또는 진행 중인 긴급구난 예측 분석 이력을 목록 형식으로 조회하는 화면이다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'긴급구난\'을 클릭하면 목록 화면이 표시된다.',
|
|
'원하는 분석을 검색하거나 목록에서 선택한다.',
|
|
'사고명 링크를 클릭하여 상세 분석 결과로 이동한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '045',
|
|
name: '시나리오 상세',
|
|
menuPath: '긴급구난 > 시나리오 관리',
|
|
imageIndex: 45,
|
|
overview:
|
|
'긴급구난 사고에 대해 기상·해양 조건을 달리 설정한 복수 시나리오를 생성·비교 관리하는 화면이다.',
|
|
procedure: [
|
|
'\'긴급구난 > 시나리오 관리\'를 클릭하여 이동한다.',
|
|
'생성된 시나리오 카드를 확인하거나 \'신규 시나리오\' 버튼으로 추가 생성한다.',
|
|
'시나리오 카드를 클릭하여 상세 내용을 확인한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '046',
|
|
name: '비교 차트',
|
|
menuPath: '긴급구난 > 시나리오 관리',
|
|
imageIndex: 46,
|
|
overview:
|
|
'긴급구난 시나리오별 탐색 면적·표류 속도·최대 이동거리를 시간별 변화 차트로 비교하는 화면이다.',
|
|
},
|
|
{
|
|
id: '047',
|
|
name: '지도 오버레이',
|
|
menuPath: '긴급구난 > 시나리오 관리',
|
|
imageIndex: 47,
|
|
overview:
|
|
'긴급구난 시나리오의 시간대별 표류 예측 궤적 및 탐색 구역을 지도에 오버레이 표출하는 화면이다.',
|
|
},
|
|
{
|
|
id: '048',
|
|
name: '긴급구난모델 이론',
|
|
menuPath: '긴급구난',
|
|
imageIndex: 48,
|
|
overview:
|
|
'긴급구난 표류 예측에 적용되는 라그랑지안 표류 모델·탐색 구역(Search Area) 산출 방법·IMO SAR 기준 적용 이론을 안내한다.',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'ch04',
|
|
number: '04',
|
|
title: '보고자료',
|
|
subtitle: 'Reports',
|
|
screens: [
|
|
{
|
|
id: '049',
|
|
name: '보고서 목록',
|
|
menuPath: '보고자료',
|
|
imageIndex: 49,
|
|
overview:
|
|
'시스템에서 생성된 모든 사고 대응 보고서를 목록 형식으로 관리하는 화면이다. 보고서명·사고명·생성일시·유형·작성자·상태가 표시된다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'보고자료\'를 클릭하여 보고서 목록으로 이동한다.',
|
|
'검색창에서 원하는 보고서를 검색하거나 목록에서 선택한다.',
|
|
'보고서명을 클릭하여 미리보기하거나 PDF를 다운로드한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '050',
|
|
name: '초기보고서',
|
|
menuPath: '보고자료 > 표준보고서 템플릿',
|
|
imageIndex: 50,
|
|
overview:
|
|
'사고 발생 초기 지휘부 보고를 위한 표준 보고서 템플릿을 제공한다. 확산예측 결과와 사고 기본정보가 자동 연동된다.',
|
|
procedure: [
|
|
'보고자료 메뉴에서 \'표준보고서 템플릿\'을 클릭한다.',
|
|
'좌측 사이드바에서 \'초기보고서\'를 선택한다.',
|
|
'자동 입력된 항목을 검토하고 필요 내용을 추가 입력한다.',
|
|
'\'저장\' 또는 \'PDF 출력\' 버튼을 활용한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '051',
|
|
name: '지휘부 보고',
|
|
menuPath: '보고자료 > 표준보고서 템플릿',
|
|
imageIndex: 51,
|
|
overview:
|
|
'사고 지휘부 보고용 표준 보고서 템플릿. 방제 대응 현황·자원 투입 현황·향후 계획이 포함된다.',
|
|
},
|
|
{
|
|
id: '052',
|
|
name: '예측보고서',
|
|
menuPath: '보고자료 > 표준보고서 템플릿',
|
|
imageIndex: 52,
|
|
overview:
|
|
'확산예측 결과를 포함한 예측보고서 템플릿. 모델 종류·예측 시간·주요 결과가 자동 연동된다.',
|
|
},
|
|
{
|
|
id: '053',
|
|
name: '종합보고서',
|
|
menuPath: '보고자료 > 표준보고서 템플릿',
|
|
imageIndex: 53,
|
|
overview:
|
|
'사고 종료 후 방제 대응 전 과정을 정리하는 종합보고서. 사고개요·대응경과·방제실적·환경영향평가·교훈이 포함된다.',
|
|
},
|
|
{
|
|
id: '054',
|
|
name: '유출유 보고',
|
|
menuPath: '보고자료 > 표준보고서 템플릿',
|
|
imageIndex: 54,
|
|
overview:
|
|
'유출유 사고 전용 표준 보고서 템플릿. 확산예측·오일펜스 배치·풍화 상태가 자동 연동된다.',
|
|
},
|
|
{
|
|
id: '055',
|
|
name: '유출유 확산예측 보고서 생성',
|
|
menuPath: '보고자료 > 보고서 생성',
|
|
imageIndex: 55,
|
|
overview:
|
|
'유출유 확산예측 분석 결과를 기반으로 보고서를 자동 생성하는 화면이다.',
|
|
procedure: [
|
|
'보고자료 메뉴에서 \'보고서 생성\'을 클릭한다.',
|
|
'\'유출유 확산예측\' 유형을 선택한다.',
|
|
'연동할 분석 결과를 드롭다운에서 선택한다.',
|
|
'보고 대상을 선택한다.',
|
|
'\'보고서 생성\' 버튼을 클릭한다.',
|
|
],
|
|
inputs: [
|
|
{ label: '분석 결과', type: '드롭다운', required: true, desc: '연동할 분석 건 선택' },
|
|
{ label: '보고 대상', type: '체크박스', required: true, desc: '지휘부·현장·외부 기관' },
|
|
],
|
|
},
|
|
{
|
|
id: '056',
|
|
name: 'HNS 대기확산 보고서 생성',
|
|
menuPath: '보고자료 > 보고서 생성',
|
|
imageIndex: 56,
|
|
overview:
|
|
'HNS 대기확산 분석 결과를 기반으로 보고서를 자동 생성하는 화면이다.',
|
|
},
|
|
{
|
|
id: '057',
|
|
name: '긴급구난 보고서 생성',
|
|
menuPath: '보고자료 > 보고서 생성',
|
|
imageIndex: 57,
|
|
overview:
|
|
'긴급구난 예측 분석 결과를 기반으로 구조 현장 지휘부용 보고서를 자동 생성하는 화면이다.',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'ch05',
|
|
number: '05',
|
|
title: '항공탐색',
|
|
subtitle: 'Aerial Surveillance',
|
|
screens: [
|
|
{
|
|
id: '058',
|
|
name: '영상사진관리',
|
|
menuPath: '항공탐색',
|
|
imageIndex: 58,
|
|
overview:
|
|
'드론·항공기·위성에서 수집된 영상 및 사진을 통합 관리하는 화면이다. 촬영 일시·위치·기기 유형별 분류 조회와 유출유 면적분석 연계를 지원한다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'항공탐색 > 영상사진관리\'를 클릭한다.',
|
|
'목록에서 원하는 영상/사진을 선택하거나 지도 마커를 클릭하여 확인한다.',
|
|
'신규 파일 업로드 시 \'파일 업로드\' 버튼을 클릭한다.',
|
|
],
|
|
inputs: [
|
|
{ label: '업로드 파일', type: '파일', required: false, desc: 'JPG·PNG·GeoTIFF·KMZ' },
|
|
{ label: '촬영 일시', type: '날짜+시간', required: false, desc: '촬영 시점' },
|
|
{ label: '장비 유형', type: '드롭다운', required: false, desc: '드론·항공기·위성·CCTV' },
|
|
],
|
|
notes: [
|
|
'50MB 초과 파일은 업로드 전 압축하거나 관리자에게 문의한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '059',
|
|
name: '유출유면적분석',
|
|
menuPath: '항공탐색',
|
|
imageIndex: 59,
|
|
overview:
|
|
'항공·위성 이미지를 기반으로 유출유 오염 면적을 AI 자동 산출하는 화면이다. 자동 추출된 오염 경계선을 수동 편집하여 정밀 면적을 산정할 수 있다.',
|
|
procedure: [
|
|
'영상사진관리에서 분석 대상 이미지를 선택하고 \'면적 분석\' 버튼을 클릭한다.',
|
|
'\'AI 자동 분석 실행\' 버튼을 클릭하여 오염 경계선을 추출한다.',
|
|
'폴리곤 경계선을 검토하고 필요 시 편집 도구로 수동 조정한다.',
|
|
'최종 면적·둘레·중심 좌표를 확인하고 저장한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '060',
|
|
name: '실시간드론',
|
|
menuPath: '항공탐색',
|
|
imageIndex: 60,
|
|
overview:
|
|
'현장 드론에서 실시간 전송되는 영상 스트리밍을 시스템 내에서 직접 모니터링하는 화면이다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'항공탐색 > 실시간 드론\'을 클릭한다.',
|
|
'연결된 드론 목록에서 모니터링할 드론을 선택한다.',
|
|
'실시간 영상을 확인하고 필요 시 스냅샷을 저장한다.',
|
|
],
|
|
notes: [
|
|
'드론 스트리밍은 네트워크 연결 상태에 따라 화질 및 지연이 달라질 수 있다.',
|
|
],
|
|
},
|
|
{
|
|
id: '061',
|
|
name: '오염선박 3D분석',
|
|
menuPath: '항공탐색',
|
|
imageIndex: 61,
|
|
overview:
|
|
'항공·위성 이미지를 기반으로 오염 선박의 3D 모델을 생성하고 오염 분포를 입체적으로 분석하는 화면이다.',
|
|
},
|
|
{
|
|
id: '062',
|
|
name: '위성요청',
|
|
menuPath: '항공탐색',
|
|
imageIndex: 62,
|
|
overview:
|
|
'SAR·광학 위성 촬영 요청 및 수신 결과를 관리하는 화면이다.',
|
|
inputs: [
|
|
{ label: '요청 위치', type: '숫자', required: true, desc: '위·경도' },
|
|
{ label: '촬영 희망 일시', type: '날짜+시간', required: true, desc: '촬영 필요 일시' },
|
|
{ label: '위성 종류', type: '라디오', required: true, desc: 'SAR 또는 광학' },
|
|
{ label: '해상도', type: '드롭다운', required: false, desc: '요청 이미지 해상도' },
|
|
],
|
|
notes: [
|
|
'위성 촬영 가능 여부는 궤도 조건에 따라 결정되며 요청이 항상 수용되지 않을 수 있다.',
|
|
],
|
|
},
|
|
{
|
|
id: '063',
|
|
name: 'CCTV 조회',
|
|
menuPath: '항공탐색',
|
|
imageIndex: 63,
|
|
overview:
|
|
'해안·항만 CCTV의 실시간 영상을 조회하는 화면이다. 사고 인근 CCTV를 지도에서 선택하여 스트리밍으로 확인할 수 있다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'항공탐색 > CCTV조회\'를 클릭한다.',
|
|
'지도에서 확인할 CCTV 마커를 클릭한다.',
|
|
'실시간 영상을 확인하고 필요 시 스냅샷을 저장한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '064',
|
|
name: '항공탐색 이론 - 개요',
|
|
menuPath: '항공탐색 > 항공탐색 이론',
|
|
imageIndex: 64,
|
|
overview:
|
|
'Wing 항공탐색 기능에 적용되는 원격탐사·ESI 방제지도·면적 산정·확산예측 연계 이론의 전체 개요를 안내한다.',
|
|
},
|
|
{
|
|
id: '065',
|
|
name: '항공탐색 이론 - 탐지 장비',
|
|
menuPath: '항공탐색 > 항공탐색 이론',
|
|
imageIndex: 65,
|
|
overview:
|
|
'해양 오염 항공 탐지에 사용되는 주요 장비(SAR·적외선 카메라·UV 센서·광학 카메라)의 특성과 적용 원리를 안내한다.',
|
|
},
|
|
{
|
|
id: '066',
|
|
name: '항공탐색 이론 - 원격탐사',
|
|
menuPath: '항공탐색 > 항공탐색 이론',
|
|
imageIndex: 66,
|
|
overview:
|
|
'위성 및 항공 원격탐사(Remote Sensing)의 기본 원리와 해양 오염 탐지 적용 방법을 안내한다.',
|
|
},
|
|
{
|
|
id: '067',
|
|
name: '항공탐색 이론 - ESI 방제지도',
|
|
menuPath: '항공탐색 > 항공탐색 이론',
|
|
imageIndex: 67,
|
|
overview:
|
|
'환경민감지수(ESI) 방제지도의 해안선 민감도 등급 분류 체계와 방제 우선순위 결정 방법을 안내한다.',
|
|
},
|
|
{
|
|
id: '068',
|
|
name: '항공탐색 이론 - 면적 산정',
|
|
menuPath: '항공탐색 > 항공탐색 이론',
|
|
imageIndex: 68,
|
|
overview:
|
|
'항공·위성 이미지 기반 유출유 면적 산정에 적용되는 AI 알고리즘(객체 탐지·영상 분할)의 원리를 안내한다.',
|
|
},
|
|
{
|
|
id: '069',
|
|
name: '항공탐색 이론 - 확산예측 연계',
|
|
menuPath: '항공탐색 > 항공탐색 이론',
|
|
imageIndex: 69,
|
|
overview:
|
|
'항공 탐색 결과(실측 면적·위치)와 유출유 확산예측 모델 보정 연계 방법을 안내한다.',
|
|
},
|
|
{
|
|
id: '070',
|
|
name: '항공탐색 이론 - 논문 특허',
|
|
menuPath: '항공탐색 > 항공탐색 이론',
|
|
imageIndex: 70,
|
|
overview:
|
|
'Wing 항공탐색 기능의 기반이 되는 관련 논문과 특허 목록을 안내한다.',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'ch06',
|
|
number: '06',
|
|
title: '게시판',
|
|
subtitle: 'Bulletin Board',
|
|
screens: [
|
|
{
|
|
id: '071',
|
|
name: '전체 게시판',
|
|
menuPath: '게시판',
|
|
imageIndex: 71,
|
|
overview:
|
|
'공지사항·자료실·Q&A·해경매뉴얼 게시물을 통합 조회하는 전체 게시판 화면이다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'게시판\'을 클릭하여 전체 게시판으로 이동한다.',
|
|
'유형 필터 탭을 선택하거나 검색창에 키워드를 입력한다.',
|
|
'원하는 게시물 제목을 클릭하여 상세 내용을 확인한다.',
|
|
],
|
|
},
|
|
{
|
|
id: '072',
|
|
name: '공지사항',
|
|
menuPath: '게시판',
|
|
imageIndex: 72,
|
|
overview:
|
|
'시스템 공지·업데이트·장애 안내 등 운영 공지사항을 게시하는 화면이다. 관리자만 등록·수정 가능.',
|
|
},
|
|
{
|
|
id: '073',
|
|
name: '자료실',
|
|
menuPath: '게시판',
|
|
imageIndex: 73,
|
|
overview:
|
|
'매뉴얼·지침서·참고자료 등 업무 관련 문서 파일을 공유하는 게시판 화면이다.',
|
|
},
|
|
{
|
|
id: '074',
|
|
name: 'QNA',
|
|
menuPath: '게시판',
|
|
imageIndex: 74,
|
|
overview:
|
|
'시스템 사용 관련 질문을 등록하고 답변을 확인하는 Q&A 게시판 화면이다.',
|
|
inputs: [
|
|
{ label: '제목', type: '텍스트', required: true, desc: '질문 제목' },
|
|
{ label: '카테고리', type: '드롭다운', required: true, desc: '기능문의·오류신고·개선요청' },
|
|
{ label: '내용', type: '텍스트', required: true, desc: '질문 내용' },
|
|
{ label: '첨부 파일', type: '파일', required: false, desc: '스크린샷 등' },
|
|
],
|
|
},
|
|
{
|
|
id: '075',
|
|
name: '해경 매뉴얼',
|
|
menuPath: '게시판',
|
|
imageIndex: 75,
|
|
overview:
|
|
'해양경찰청 방제·대응 매뉴얼을 시스템 내에서 바로 조회할 수 있는 화면이다. 관리자만 등록·수정 가능.',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'ch07',
|
|
number: '07',
|
|
title: '기상정보',
|
|
subtitle: 'Weather',
|
|
screens: [
|
|
{
|
|
id: '076',
|
|
name: '기상정보',
|
|
menuPath: '기상정보',
|
|
imageIndex: 76,
|
|
overview:
|
|
'현재 사고 지역 주변의 실시간 기상 정보(풍향·풍속·기온·파고·시정 등)를 조회하는 화면이다. 기상 데이터는 확산예측 모델 입력으로 자동 연동된다.',
|
|
description:
|
|
'사고 위치 기준 반경 내 기상 관측소 목록과 최신 관측값이 표시된다. 시계열 그래프로 과거 24시간 기상 변화를 확인할 수 있다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'기상정보\'를 클릭하여 이동한다.',
|
|
'지도에서 관측소 마커를 클릭하거나 목록에서 관측소를 선택한다.',
|
|
'최신 기상값과 시계열 그래프를 확인한다.',
|
|
],
|
|
notes: [
|
|
'극한 기상 조건에서는 확산예측 정확도가 낮아질 수 있으므로 현장 기상 관측값과 병행 확인한다.',
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'ch08',
|
|
number: '08',
|
|
title: '통합조회',
|
|
subtitle: 'Integrated Search',
|
|
screens: [
|
|
{
|
|
id: '077',
|
|
name: '통합조회',
|
|
menuPath: '통합조회',
|
|
imageIndex: 77,
|
|
overview:
|
|
'Wing 시스템 전체 분석 이력(유출유·HNS·긴급구난·보고서)을 통합 조회하는 화면이다. 사고명·날짜·분석 유형·담당자 등 복합 조건으로 검색하고 결과를 지도와 목록으로 표출한다.',
|
|
description:
|
|
'화면 좌측에 날짜 범위·분석 유형·담당자·사고 지역 복합 필터 패널이 위치한다. 중앙 지도에 검색 결과 사고지점 마커가 표출된다. Excel·CSV 내보내기를 지원한다.',
|
|
procedure: [
|
|
'상단 메뉴에서 \'통합조회\'를 클릭하여 이동한다.',
|
|
'날짜 범위·분석 유형 등 필터 조건을 설정한다.',
|
|
'\'검색\' 버튼을 클릭하여 결과를 조회한다.',
|
|
'지도 마커 또는 목록 항목을 클릭하여 해당 분석 결과 상세 화면으로 이동한다.',
|
|
'Excel·CSV 내보내기 버튼으로 이력 자료를 저장한다.',
|
|
],
|
|
inputs: [
|
|
{ label: '날짜 범위', type: '날짜', required: false, desc: '시작·종료 날짜' },
|
|
{ label: '분석 유형', type: '체크박스', required: false, desc: '유출유·HNS·긴급구난·보고서' },
|
|
{ label: '담당자', type: '텍스트', required: false, desc: '이름 필터' },
|
|
{ label: '사고 지역', type: '텍스트', required: false, desc: '지역명 필터' },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
const UserManualPopup = ({ isOpen, onClose }: UserManualPopupProps) => {
|
|
const [selectedChapterId, setSelectedChapterId] = useState<string>('ch01');
|
|
const [expandedScreenIds, setExpandedScreenIds] = useState<Set<string>>(new Set());
|
|
const [lightboxSrc, setLightboxSrc] = useState<string | null>(null);
|
|
|
|
if (!isOpen) return null;
|
|
|
|
const selectedChapter = CHAPTERS.find((ch) => ch.id === selectedChapterId) ?? CHAPTERS[0];
|
|
|
|
const toggleScreen = (screenId: string) => {
|
|
setExpandedScreenIds((prev) => {
|
|
const next = new Set(prev);
|
|
if (next.has(screenId)) {
|
|
next.delete(screenId);
|
|
} else {
|
|
next.add(screenId);
|
|
}
|
|
return next;
|
|
});
|
|
};
|
|
|
|
const expandAll = () => {
|
|
setExpandedScreenIds(new Set(selectedChapter.screens.map((s) => s.id)));
|
|
};
|
|
|
|
const collapseAll = () => {
|
|
setExpandedScreenIds(new Set());
|
|
};
|
|
|
|
const allExpanded =
|
|
selectedChapter.screens.length > 0 &&
|
|
selectedChapter.screens.every((s) => expandedScreenIds.has(s.id));
|
|
|
|
return (
|
|
<>
|
|
<div
|
|
className='fixed inset-0 z-[9999] flex items-center justify-center'
|
|
style={{ background: 'rgba(0,0,0,0.65)' }}
|
|
onClick={(e) => {
|
|
if (e.target === e.currentTarget) onClose();
|
|
}}
|
|
>
|
|
<div
|
|
className='flex flex-col rounded-lg overflow-hidden'
|
|
style={{
|
|
width: '90vw',
|
|
height: '85vh',
|
|
background: '#0f1729',
|
|
border: '1px solid #1e2a45',
|
|
boxShadow: '0 24px 64px rgba(0,0,0,0.5)',
|
|
}}
|
|
>
|
|
{/* Header */}
|
|
<div
|
|
className='flex items-center justify-between px-6 py-4 flex-shrink-0'
|
|
style={{
|
|
background: '#0b1120',
|
|
borderBottom: '1px solid #1e2a45',
|
|
}}
|
|
>
|
|
<div className='flex items-center gap-3'>
|
|
<span
|
|
className='font-bold text-[15px]'
|
|
style={{ color: '#e2e8f0' }}
|
|
>
|
|
Wing 사용자 매뉴얼
|
|
</span>
|
|
<span
|
|
className='text-[11px] px-2 py-0.5 rounded font-mono'
|
|
style={{
|
|
background: 'rgba(6,182,212,0.12)',
|
|
color: '#06b6d4',
|
|
border: '1px solid rgba(6,182,212,0.25)',
|
|
}}
|
|
>
|
|
v0.5
|
|
</span>
|
|
</div>
|
|
<button
|
|
onClick={onClose}
|
|
className='flex items-center justify-center w-7 h-7 rounded text-[13px] font-semibold transition-colors'
|
|
style={{ color: '#94a3b8', background: 'transparent' }}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.background = '#1a2540';
|
|
e.currentTarget.style.color = '#e2e8f0';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.background = 'transparent';
|
|
e.currentTarget.style.color = '#94a3b8';
|
|
}}
|
|
>
|
|
X
|
|
</button>
|
|
</div>
|
|
|
|
{/* Body */}
|
|
<div className='flex flex-1 overflow-hidden'>
|
|
{/* Left Sidebar */}
|
|
<div
|
|
className='flex-shrink-0 overflow-y-auto py-3'
|
|
style={{
|
|
width: '240px',
|
|
background: '#0b1120',
|
|
borderRight: '1px solid #1e2a45',
|
|
}}
|
|
>
|
|
{CHAPTERS.map((chapter) => {
|
|
const isActive = chapter.id === selectedChapterId;
|
|
return (
|
|
<button
|
|
key={chapter.id}
|
|
onClick={() => {
|
|
setSelectedChapterId(chapter.id);
|
|
setExpandedScreenIds(new Set());
|
|
}}
|
|
className='w-full text-left px-4 py-3 transition-colors'
|
|
style={{
|
|
background: isActive ? 'rgba(6,182,212,0.08)' : 'transparent',
|
|
borderLeft: isActive ? '2px solid #06b6d4' : '2px solid transparent',
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
if (!isActive) {
|
|
e.currentTarget.style.background = '#1a2540';
|
|
}
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
if (!isActive) {
|
|
e.currentTarget.style.background = 'transparent';
|
|
}
|
|
}}
|
|
>
|
|
<div className='flex items-center gap-2.5'>
|
|
<span
|
|
className='flex-shrink-0 w-7 h-7 rounded flex items-center justify-center text-[10px] font-bold font-mono'
|
|
style={{
|
|
background: isActive ? 'rgba(6,182,212,0.18)' : 'rgba(255,255,255,0.05)',
|
|
color: isActive ? '#06b6d4' : '#64748b',
|
|
border: isActive ? '1px solid rgba(6,182,212,0.3)' : '1px solid #1e2a45',
|
|
}}
|
|
>
|
|
{chapter.number}
|
|
</span>
|
|
<div className='min-w-0'>
|
|
<div
|
|
className='text-[12px] font-medium leading-tight truncate'
|
|
style={{ color: isActive ? '#06b6d4' : '#cbd5e1' }}
|
|
>
|
|
{chapter.title}
|
|
</div>
|
|
<div
|
|
className='text-[10px] leading-tight mt-0.5 truncate'
|
|
style={{ color: '#475569' }}
|
|
>
|
|
{chapter.subtitle}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* Right Content */}
|
|
<div className='flex-1 overflow-y-auto p-6'>
|
|
{/* Chapter heading */}
|
|
<div className='mb-5 pb-4' style={{ borderBottom: '1px solid #1e2a45' }}>
|
|
<div className='flex items-center justify-between'>
|
|
<div className='flex items-center gap-3'>
|
|
<span
|
|
className='text-[11px] font-mono px-2 py-0.5 rounded font-bold'
|
|
style={{
|
|
background: 'rgba(6,182,212,0.12)',
|
|
color: '#06b6d4',
|
|
border: '1px solid rgba(6,182,212,0.2)',
|
|
}}
|
|
>
|
|
CH {selectedChapter.number}
|
|
</span>
|
|
<h2 className='text-[16px] font-semibold' style={{ color: '#e2e8f0' }}>
|
|
{selectedChapter.title}
|
|
</h2>
|
|
<span className='text-[12px]' style={{ color: '#475569' }}>
|
|
{selectedChapter.subtitle}
|
|
</span>
|
|
</div>
|
|
<div className='flex items-center gap-2'>
|
|
<span className='text-[11px] mr-1' style={{ color: '#64748b' }}>
|
|
{selectedChapter.screens.length}개 화면
|
|
</span>
|
|
<button
|
|
onClick={allExpanded ? collapseAll : expandAll}
|
|
className='text-[11px] px-3 py-1 rounded transition-colors'
|
|
style={{
|
|
background: 'rgba(6,182,212,0.08)',
|
|
color: '#06b6d4',
|
|
border: '1px solid rgba(6,182,212,0.2)',
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.background = 'rgba(6,182,212,0.16)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.background = 'rgba(6,182,212,0.08)';
|
|
}}
|
|
>
|
|
{allExpanded ? '전체 닫기' : '전체 열기'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Screen cards */}
|
|
<div className='flex flex-col gap-2'>
|
|
{selectedChapter.screens.map((screen) => {
|
|
const isExpanded = expandedScreenIds.has(screen.id);
|
|
const imageSrc = `/manual/image${screen.imageIndex}.png`;
|
|
return (
|
|
<div
|
|
key={screen.id}
|
|
className='rounded-lg overflow-hidden'
|
|
style={{
|
|
background: '#141d33',
|
|
border: '1px solid #1e2a45',
|
|
}}
|
|
>
|
|
{/* Screen header (toggle) */}
|
|
<button
|
|
onClick={() => toggleScreen(screen.id)}
|
|
className='w-full text-left flex items-center gap-3 px-4 py-3 transition-colors'
|
|
style={{ background: 'transparent' }}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.background = '#1a2540';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.background = 'transparent';
|
|
}}
|
|
>
|
|
<span
|
|
className='flex-shrink-0 text-[10px] font-mono font-bold px-1.5 py-0.5 rounded'
|
|
style={{
|
|
background: 'rgba(6,182,212,0.1)',
|
|
color: '#06b6d4',
|
|
border: '1px solid rgba(6,182,212,0.2)',
|
|
minWidth: '36px',
|
|
textAlign: 'center',
|
|
}}
|
|
>
|
|
{screen.id}
|
|
</span>
|
|
<span
|
|
className='flex-1 text-[13px] font-medium'
|
|
style={{ color: '#cbd5e1' }}
|
|
>
|
|
{screen.name}
|
|
</span>
|
|
<span
|
|
className='flex-shrink-0 text-[10px] font-mono'
|
|
style={{
|
|
color: '#475569',
|
|
transition: 'transform 0.2s',
|
|
display: 'inline-block',
|
|
transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
}}
|
|
>
|
|
v
|
|
</span>
|
|
</button>
|
|
|
|
{/* Screen detail (expanded) */}
|
|
{isExpanded && (
|
|
<div
|
|
className='px-4 pb-5'
|
|
style={{ borderTop: '1px solid #1e2a45' }}
|
|
>
|
|
{/* Screenshot image */}
|
|
<div className='mt-4 mb-4'>
|
|
<img
|
|
src={imageSrc}
|
|
alt={screen.name}
|
|
loading='lazy'
|
|
onClick={() => setLightboxSrc(imageSrc)}
|
|
style={{
|
|
width: '100%',
|
|
borderRadius: '6px',
|
|
border: '1px solid #1e2a45',
|
|
cursor: 'zoom-in',
|
|
display: 'block',
|
|
}}
|
|
/>
|
|
<p
|
|
className='mt-1 text-[10px] text-right'
|
|
style={{ color: '#475569' }}
|
|
>
|
|
이미지를 클릭하면 크게 볼 수 있다
|
|
</p>
|
|
</div>
|
|
|
|
{/* Menu path breadcrumb */}
|
|
<div
|
|
className='mb-3 text-[11px] font-mono px-2 py-1 rounded inline-block'
|
|
style={{
|
|
background: 'rgba(71,85,105,0.15)',
|
|
color: '#64748b',
|
|
border: '1px solid #1e2a45',
|
|
}}
|
|
>
|
|
{screen.menuPath}
|
|
</div>
|
|
|
|
{/* Overview */}
|
|
<div className='mt-2'>
|
|
<p
|
|
className='text-[12px] leading-relaxed'
|
|
style={{ color: '#94a3b8' }}
|
|
>
|
|
{screen.overview}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Description */}
|
|
{screen.description && (
|
|
<div
|
|
className='mt-3 px-3 py-2.5 rounded'
|
|
style={{
|
|
background: 'rgba(30,42,69,0.6)',
|
|
border: '1px solid #1e2a45',
|
|
}}
|
|
>
|
|
<div
|
|
className='text-[11px] font-semibold mb-1.5 uppercase tracking-wide'
|
|
style={{ color: '#475569' }}
|
|
>
|
|
화면 설명
|
|
</div>
|
|
<p
|
|
className='text-[12px] leading-relaxed'
|
|
style={{ color: '#7f8ea3' }}
|
|
>
|
|
{screen.description}
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Procedure */}
|
|
{screen.procedure && screen.procedure.length > 0 && (
|
|
<div className='mt-4'>
|
|
<div
|
|
className='text-[11px] font-semibold mb-2 uppercase tracking-wide'
|
|
style={{ color: '#475569' }}
|
|
>
|
|
사용 절차
|
|
</div>
|
|
<ol className='flex flex-col gap-1.5'>
|
|
{screen.procedure.map((step, idx) => (
|
|
<li key={idx} className='flex items-start gap-2.5'>
|
|
<span
|
|
className='flex-shrink-0 w-5 h-5 rounded-full flex items-center justify-center text-[10px] font-bold mt-0.5'
|
|
style={{
|
|
background: 'rgba(6,182,212,0.12)',
|
|
color: '#06b6d4',
|
|
border: '1px solid rgba(6,182,212,0.2)',
|
|
}}
|
|
>
|
|
{idx + 1}
|
|
</span>
|
|
<span
|
|
className='text-[12px] leading-relaxed'
|
|
style={{ color: '#94a3b8' }}
|
|
>
|
|
{step}
|
|
</span>
|
|
</li>
|
|
))}
|
|
</ol>
|
|
</div>
|
|
)}
|
|
|
|
{/* Inputs */}
|
|
{screen.inputs && screen.inputs.length > 0 && (
|
|
<div className='mt-4'>
|
|
<div
|
|
className='text-[11px] font-semibold mb-2 uppercase tracking-wide'
|
|
style={{ color: '#475569' }}
|
|
>
|
|
입력 항목
|
|
</div>
|
|
<div
|
|
className='rounded overflow-hidden'
|
|
style={{ border: '1px solid #1e2a45' }}
|
|
>
|
|
<table className='w-full text-[12px]'>
|
|
<thead>
|
|
<tr style={{ background: '#0f1729' }}>
|
|
<th
|
|
className='text-left px-3 py-2 font-medium'
|
|
style={{ color: '#64748b', width: '22%' }}
|
|
>
|
|
항목
|
|
</th>
|
|
<th
|
|
className='text-left px-3 py-2 font-medium'
|
|
style={{ color: '#64748b', width: '18%' }}
|
|
>
|
|
유형
|
|
</th>
|
|
<th
|
|
className='text-left px-3 py-2 font-medium'
|
|
style={{ color: '#64748b', width: '12%' }}
|
|
>
|
|
필수
|
|
</th>
|
|
<th
|
|
className='text-left px-3 py-2 font-medium'
|
|
style={{ color: '#64748b' }}
|
|
>
|
|
설명
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{screen.inputs.map((input, idx) => (
|
|
<tr
|
|
key={idx}
|
|
style={{
|
|
borderTop: '1px solid #1e2a45',
|
|
background:
|
|
idx % 2 === 0
|
|
? 'transparent'
|
|
: 'rgba(255,255,255,0.01)',
|
|
}}
|
|
>
|
|
<td
|
|
className='px-3 py-2 font-medium'
|
|
style={{ color: '#cbd5e1' }}
|
|
>
|
|
{input.label}
|
|
</td>
|
|
<td
|
|
className='px-3 py-2'
|
|
style={{ color: '#64748b' }}
|
|
>
|
|
{input.type}
|
|
</td>
|
|
<td className='px-3 py-2'>
|
|
{input.required ? (
|
|
<span
|
|
className='text-[10px] font-bold px-1.5 py-0.5 rounded'
|
|
style={{
|
|
background: 'rgba(239,68,68,0.1)',
|
|
color: '#f87171',
|
|
border: '1px solid rgba(239,68,68,0.2)',
|
|
}}
|
|
>
|
|
필수
|
|
</span>
|
|
) : (
|
|
<span
|
|
className='text-[10px] px-1.5 py-0.5 rounded'
|
|
style={{
|
|
background: 'rgba(100,116,139,0.1)',
|
|
color: '#64748b',
|
|
border: '1px solid rgba(100,116,139,0.2)',
|
|
}}
|
|
>
|
|
선택
|
|
</span>
|
|
)}
|
|
</td>
|
|
<td
|
|
className='px-3 py-2'
|
|
style={{ color: '#7f8ea3' }}
|
|
>
|
|
{input.desc}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Notes */}
|
|
{screen.notes && screen.notes.length > 0 && (
|
|
<div className='mt-4'>
|
|
<div
|
|
className='text-[11px] font-semibold mb-2 uppercase tracking-wide'
|
|
style={{ color: '#475569' }}
|
|
>
|
|
유의사항
|
|
</div>
|
|
<ul className='flex flex-col gap-1.5'>
|
|
{screen.notes.map((note, idx) => (
|
|
<li key={idx} className='flex items-start gap-2'>
|
|
<span
|
|
className='flex-shrink-0 mt-1.5 w-1.5 h-1.5 rounded-full'
|
|
style={{ background: '#f59e0b' }}
|
|
/>
|
|
<span
|
|
className='text-[12px] leading-relaxed'
|
|
style={{ color: '#94a3b8' }}
|
|
>
|
|
{note}
|
|
</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Lightbox */}
|
|
{lightboxSrc !== null && (
|
|
<div
|
|
className='fixed inset-0 z-[10000] flex items-center justify-center'
|
|
style={{ background: 'rgba(0,0,0,0.88)' }}
|
|
onClick={() => setLightboxSrc(null)}
|
|
>
|
|
<div
|
|
className='relative'
|
|
style={{ maxWidth: '92vw', maxHeight: '90vh' }}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<img
|
|
src={lightboxSrc}
|
|
alt='확대 이미지'
|
|
style={{
|
|
maxWidth: '92vw',
|
|
maxHeight: '88vh',
|
|
borderRadius: '8px',
|
|
border: '1px solid #1e2a45',
|
|
display: 'block',
|
|
}}
|
|
/>
|
|
<button
|
|
onClick={() => setLightboxSrc(null)}
|
|
className='absolute top-2 right-2 w-8 h-8 rounded flex items-center justify-center text-[13px] font-bold'
|
|
style={{
|
|
background: 'rgba(15,23,41,0.85)',
|
|
color: '#94a3b8',
|
|
border: '1px solid #1e2a45',
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.color = '#e2e8f0';
|
|
e.currentTarget.style.background = '#1a2540';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.color = '#94a3b8';
|
|
e.currentTarget.style.background = 'rgba(15,23,41,0.85)';
|
|
}}
|
|
>
|
|
X
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default UserManualPopup;
|