chore: publish 폴더 복원 및 그레이스풀 폴백 적용

- git 이력에서 publish 파일 45개 복원 (layouts, pages, components, scss)
- Sidebar.jsx: import.meta.glob 패턴으로 publish 폴더 없이도 빌드 가능
- App.jsx: lazy import에 catch 추가로 publish 누락 시 안내 메시지 표시
- .gitignore: src/publish/ 제외 해제 (git 추적 포함)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
LHT 2026-02-09 14:29:01 +09:00
부모 7522318ff9
커밋 e45be93e71
49개의 변경된 파일6774개의 추가작업 그리고 36개의 파일을 삭제

3
.gitignore vendored
파일 보기

@ -39,8 +39,7 @@ src/tracking/components/VesselListManager/
# 단, d.ts 타입 선언 파일은 필요시 포함 가능 # 단, d.ts 타입 선언 파일은 필요시 포함 가능
# !**/*.d.ts # !**/*.d.ts
# Publish 폴더 (퍼블리시 원본 참조용, 빌드/커밋 제외) # Publish 폴더 (퍼블리시 원본 포함, 없어도 빌드 가능)
src/publish/
nul nul
확인요청.txt 확인요청.txt

파일 보기

@ -8,7 +8,15 @@ import { ToastContainer } from './components/common/Toast';
// ( ) // ( )
// tree-shaking // tree-shaking
const PublishRouter = import.meta.env.DEV const PublishRouter = import.meta.env.DEV
? lazy(() => import('./publish')) ? lazy(() =>
import('./publish').catch(() => ({
default: () => (
<div style={{ color: '#fff', padding: '2rem' }}>
publish 폴더가 없습니다. 퍼블리시 파일을 추가하면 자동으로 활성화됩니다.
</div>
),
}))
)
: null; : null;
export default function App() { export default function App() {

파일 보기

@ -2,14 +2,18 @@ import { useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom'; import { useNavigate, useLocation } from 'react-router-dom';
import SideNav, { keyToPath, pathToKey } from './SideNav'; import SideNav, { keyToPath, pathToKey } from './SideNav';
// // ( import.meta.glob )
import Panel1Component from '../../publish/pages/Panel1Component'; const publishPanels = import.meta.glob('../../publish/pages/Panel*Component.jsx', { eager: true });
import Panel2Component from '../../publish/pages/Panel2Component'; const getPanel = (name) => publishPanels[`../../publish/pages/${name}.jsx`]?.default || null;
import Panel3Component from '../../publish/pages/Panel3Component';
import Panel4Component from '../../publish/pages/Panel4Component'; const Panel1Component = getPanel('Panel1Component');
import Panel5Component from '../../publish/pages/Panel5Component'; const Panel2Component = getPanel('Panel2Component');
import Panel6Component from '../../publish/pages/Panel6Component'; const Panel3Component = getPanel('Panel3Component');
import Panel8Component from '../../publish/pages/Panel8Component'; const Panel4Component = getPanel('Panel4Component');
const Panel5Component = getPanel('Panel5Component');
const Panel6Component = getPanel('Panel6Component');
const Panel8Component = getPanel('Panel8Component');
// DisplayComponent // DisplayComponent
import DisplayComponent from '../../component/wrap/side/DisplayComponent'; import DisplayComponent from '../../component/wrap/side/DisplayComponent';
// //
@ -55,32 +59,21 @@ export default function Sidebar() {
onToggle: handleTogglePanel, onToggle: handleTogglePanel,
}; };
// // ( null )
const renderPanel = () => { const renderPanel = () => {
switch (activeKey) { const panelMap = {
case 'gnb1': gnb1: DisplayComponent ? <DisplayComponent {...panelProps} initialTab="filter" /> : null,
return <DisplayComponent {...panelProps} initialTab="filter" />; gnb2: Panel2Component ? <Panel2Component {...panelProps} /> : null,
case 'gnb2': gnb3: Panel3Component ? <Panel3Component {...panelProps} /> : null,
return <Panel2Component {...panelProps} />; gnb4: Panel4Component ? <Panel4Component {...panelProps} /> : null,
case 'gnb3': gnb5: Panel5Component ? <Panel5Component {...panelProps} /> : null,
return <Panel3Component {...panelProps} />; gnb6: Panel6Component ? <Panel6Component {...panelProps} /> : null,
case 'gnb4': gnb7: <ReplayPage {...panelProps} />,
return <Panel4Component {...panelProps} />; gnb8: Panel8Component ? <Panel8Component {...panelProps} /> : null,
case 'gnb5': filter: DisplayComponent ? <DisplayComponent {...panelProps} initialTab="filter" /> : null,
return <Panel5Component {...panelProps} />; layer: DisplayComponent ? <DisplayComponent {...panelProps} initialTab="layer" /> : null,
case 'gnb6': };
return <Panel6Component {...panelProps} />; return panelMap[activeKey] || null;
case 'gnb7':
return <ReplayPage {...panelProps} />;
case 'gnb8':
return <Panel8Component {...panelProps} />;
case 'filter':
return <DisplayComponent {...panelProps} initialTab="filter" />;
case 'layer':
return <DisplayComponent {...panelProps} initialTab="layer" />;
default:
return null; //
}
}; };
return ( return (

파일 보기

@ -0,0 +1,134 @@
import { Route } from 'react-router-dom';
//
import WrapComponent from './layouts/WrapComponent';
import HeaderComponent from './layouts/HeaderComponent';
import SideComponent from './layouts/SideComponent';
import MainComponent from './layouts/MainComponent';
//
import Panel1Component from './pages/Panel1Component';
import Panel2Component from './pages/Panel2Component';
import Panel3Component from './pages/Panel3Component';
import Panel4Component from './pages/Panel4Component';
import Panel5Component from './pages/Panel5Component';
import Panel6Component from './pages/Panel6Component';
import Panel7Component from './pages/Panel7Component';
import Panel8Component from './pages/Panel8Component';
/**
* 퍼블리시 라우트 정의
* - /publish/* 하위에서 퍼블리시 파일들을 미리볼 있음
*/
const PublishRoutes = (
<>
{/* 기본 페이지 - 전체 레이아웃 미리보기 */}
<Route index element={<PublishHome />} />
{/* 개별 패널 미리보기 */}
<Route path="panel1/*" element={<Panel1Wrapper />} />
<Route path="panel2/*" element={<Panel2Wrapper />} />
<Route path="panel3/*" element={<Panel3Wrapper />} />
<Route path="panel4/*" element={<Panel4Wrapper />} />
<Route path="panel5/*" element={<Panel5Wrapper />} />
<Route path="panel6/*" element={<Panel6Wrapper />} />
<Route path="panel7/*" element={<Panel7Wrapper />} />
<Route path="panel8/*" element={<Panel8Wrapper />} />
{/* 전체 레이아웃 (원본 구조 그대로) */}
<Route path="full/*" element={<WrapComponent />} />
</>
);
//
function PublishHome() {
return (
<div className="publish-home">
<h1>퍼블리시 미리보기</h1>
<p>좌측 메뉴에서 확인할 페이지를 선택하세요.</p>
<div className="publish-info">
<h2>폴더 구조</h2>
<pre>
{`src/publish/
_incoming/ # 퍼블리시 파일 (원본)
layouts/ # 레이아웃 컴포넌트
pages/ # 페이지 컴포넌트
components/ # 공통 컴포넌트`}
</pre>
<h2>병합 방법</h2>
<ol>
<li> 퍼블리시 파일을 <code>_incoming/</code> 폴더에 복사</li>
<li>Claude에게 병합 요청</li>
<li>변경사항 확인 적용</li>
</ol>
</div>
</div>
);
}
//
function Panel1Wrapper() {
return (
<div className="panel-wrapper">
<Panel1Component isOpen={true} onToggle={() => {}} />
</div>
);
}
function Panel2Wrapper() {
return (
<div className="panel-wrapper">
<Panel2Component isOpen={true} onToggle={() => {}} />
</div>
);
}
function Panel3Wrapper() {
return (
<div className="panel-wrapper">
<Panel3Component isOpen={true} onToggle={() => {}} />
</div>
);
}
function Panel4Wrapper() {
return (
<div className="panel-wrapper">
<Panel4Component isOpen={true} onToggle={() => {}} />
</div>
);
}
function Panel5Wrapper() {
return (
<div className="panel-wrapper">
<Panel5Component isOpen={true} onToggle={() => {}} />
</div>
);
}
function Panel6Wrapper() {
return (
<div className="panel-wrapper">
<Panel6Component isOpen={true} onToggle={() => {}} />
</div>
);
}
function Panel7Wrapper() {
return (
<div className="panel-wrapper">
<Panel7Component isOpen={true} onToggle={() => {}} />
</div>
);
}
function Panel8Wrapper() {
return (
<div className="panel-wrapper">
<Panel8Component isOpen={true} onToggle={() => {}} />
</div>
);
}
export default PublishRoutes;

파일 보기

@ -0,0 +1,35 @@
import React, { useState } from 'react';
export default function FileUpload({ label = "파일 선택", inputId, maxLength = 25, placeholder = "선택된 파일 없음" }) {
const [fileName, setFileName] = useState('');
//
const truncateMiddle = (str, maxLen) => {
if (!str) return '';
if (str.length <= maxLen) return str;
const keep = Math.floor((maxLen - 3) / 2);
return str.slice(0, keep) + '...' + str.slice(str.length - keep);
};
const handleChange = (e) => {
const name = e.target.files[0]?.name || '';
setFileName(name);
};
return (
<div className="fileWrap">
<input
type="file"
id={inputId}
className="fileInput"
onChange={handleChange}
/>
<label htmlFor={inputId} className="fileLabel">
{label}
</label>
<span className="fileName">
{fileName ? truncateMiddle(fileName, maxLength) : placeholder}
</span>
</div>
);
}

파일 보기

@ -0,0 +1,24 @@
import { useState } from "react";
function Slider({ label = "", min = 0, max = 100, defaultValue = 50 }) {
const [value, setValue] = useState(defaultValue);
const percent = ((value - min) / (max - min)) * 100;
return (
<label className="rangeWrap">
<span className="rangeLabel">{label}</span>
<input
type="range"
min={min}
max={max}
value={value}
onChange={(e) => setValue(Number(e.target.value))}
style={{ "--percent": `${percent}%` }}
aria-label={label}
/>
</label>
);
}
export default Slider;

21
src/publish/index.jsx Normal file
파일 보기

@ -0,0 +1,21 @@
/**
* 퍼블리시 모듈 엔트리 포인트
* 개발 환경에서만 사용되며, 프로덕션 빌드 제외됨
*/
import { Routes, Route } from 'react-router-dom';
import PublishLayout from './layouts/PublishLayout';
import PublishRoutes from './PublishRoutes';
/**
* 퍼블리시 라우터 컴포넌트
* App.jsx에서 lazy loading으로 로드됨
*/
export default function PublishRouter() {
return (
<Routes>
<Route path="*" element={<PublishLayout />}>
{PublishRoutes}
</Route>
</Routes>
);
}

파일 보기

@ -0,0 +1,36 @@
import { Link } from "react-router-dom";
export default function HeaderComponent() {
return(
<header id="header">
<div className="logoArea"><Link to="/main" className="logo"><span className="blind">GIS 함정용</span></Link> <span className="logoTxt">GIS 함정용</span></div>
<aside>
<ul>
<li><Link to="/main" className="alram" title="알람"><i className="badge"></i><span className="blind">알람</span></Link></li>
<li className="setWrap">
<Link
to="/signal"
className="set"
title="설정"
><span className="blind">설정</span>
</Link>
<div className="setMenu">
<Link to="/signal">신호설정</Link>
<Link to="/signal/custom">맞춤설정</Link>
</div>
</li>
<li>
<Link
to="/mypage"
className="user"
title="마이페이지"
><span className="blind">마이페이지</span>
</Link>
</li>
</ul>
</aside>
</header>
)
}

파일 보기

@ -0,0 +1,11 @@
import { Outlet } from "react-router-dom";
import TopComponent from "../pages/TopComponent";
export default function MainComponent() {
return (
<main id="main">
<TopComponent />
<Outlet />
</main>
);
}

파일 보기

@ -0,0 +1,57 @@
import { Outlet, Link, useLocation } from 'react-router-dom';
/**
* 퍼블리시 레이아웃
* - 퍼블리시 파일들을 미리보기 위한 레이아웃
* - 상단에 네비게이션 제공
*/
export default function PublishLayout() {
const location = useLocation();
const currentPath = location.pathname;
const menuItems = [
{ path: '/publish', label: '메인', exact: true },
{ path: '/publish/panel1', label: 'Panel1 (선박)' },
{ path: '/publish/panel2', label: 'Panel2 (위성)' },
{ path: '/publish/panel3', label: 'Panel3 (기상)' },
{ path: '/publish/panel4', label: 'Panel4 (분석)' },
{ path: '/publish/panel5', label: 'Panel5 (타임라인)' },
{ path: '/publish/panel6', label: 'Panel6 (AI모드)' },
{ path: '/publish/panel7', label: 'Panel7 (리플레이)' },
{ path: '/publish/panel8', label: 'Panel8 (항적조회)' },
];
const isActive = (path, exact) => {
if (exact) return currentPath === path;
return currentPath.startsWith(path);
};
return (
<div className="publish-wrapper">
{/* 퍼블리시 네비게이션 */}
<nav className="publish-nav">
<div className="publish-nav-header">
<Link to="/"> 메인으로</Link>
<span className="publish-title">퍼블리시 미리보기</span>
</div>
<ul className="publish-menu">
{menuItems.map((item) => (
<li key={item.path}>
<Link
to={item.path}
className={isActive(item.path, item.exact) ? 'active' : ''}
>
{item.label}
</Link>
</li>
))}
</ul>
</nav>
{/* 퍼블리시 콘텐츠 */}
<div className="publish-content">
<Outlet />
</div>
</div>
);
}

파일 보기

@ -0,0 +1,94 @@
import { useState } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import NavComponent from "../pages/NavComponent";
import Panel1Component from "../pages/Panel1Component";
import Panel2Component from "../pages/Panel2Component";
import Panel3Component from "../pages/Panel3Component";
import Panel4Component from "../pages/Panel4Component";
import Panel5Component from "../pages/Panel5Component";
import Panel6Component from "../pages/Panel6Component";
import Panel7Component from "../pages/Panel7Component";
import Panel8Component from "../pages/Panel8Component";
import DisplayComponent from "../pages/DisplayComponent";
export default function SideComponent() {
const navigate = useNavigate();
//const location = useLocation();
//
const [activePanel, setActivePanel] = useState("gnb1");
//
const [isPanelOpen, setIsPanelOpen] = useState(true);
const handleTogglePanel = () => setIsPanelOpen(prev => !prev);
// Display
const [displayTab, setDisplayTab] = useState("filter");
/* =========================
Nav 클릭 패널 + 라우팅
========================= */
const handleChangePanel = (key) => {
setIsPanelOpen(true);
//setActivePanel(key); // navigate
switch (key) {
case "gnb8": //
setActivePanel("gnb8");
navigate("/track");
break;
case "gnb7": //
setActivePanel("gnb7");
navigate("/replay");
break;
case "filter": //
case "layer": //
setActivePanel(key);
setDisplayTab(key);
// /
navigate("/main");
break;
default:
setActivePanel(key);
navigate("/main");
break;
}
};
/* =========================
공통 props
========================= */
const panelProps = {
isOpen: isPanelOpen,
onToggle: handleTogglePanel,
};
return (
<section id="sidePanel">
<NavComponent
activeKey={activePanel}
onChange={handleChangePanel}
/>
<div className="sidePanelContent">
{activePanel === "gnb1" && <Panel1Component {...panelProps} />}
{activePanel === "gnb2" && <Panel2Component {...panelProps} />}
{activePanel === "gnb3" && <Panel3Component {...panelProps} />}
{activePanel === "gnb4" && <Panel4Component {...panelProps} />}
{activePanel === "gnb5" && <Panel5Component {...panelProps} />}
{activePanel === "gnb6" && <Panel6Component {...panelProps} />}
{activePanel === "gnb7" && <Panel7Component {...panelProps} />}
{activePanel === "gnb8" && <Panel8Component {...panelProps} />}
{(activePanel === "filter" || activePanel === "layer") && (
<DisplayComponent {...panelProps}
activeTab={displayTab}/>
)}
</div>
</section>
);
}

파일 보기

@ -0,0 +1,131 @@
import { useState } from "react"
export default function ToolComponent() {
const [isLegendOpen, setIsLegendOpen] = useState(false);
return(
<section id="tool">
{/* 툴바 */}
<div className="toolBar">
<ul className="toolItem space">
<li><button type="button" className="tool01">초기화</button></li>
<li><button type="button" className="tool02">선박통합</button></li>
<li><button type="button" className="tool03">구역설정</button></li>
</ul>
<ul className="toolItem mt30">
<li><button type="button" className="tool04">거리</button></li>
<li><button type="button" className="tool05">면적</button></li>
<li><button type="button" className="tool06">거리환</button></li>
</ul>
<ul className="toolItem space mt30">
<li><button type="button" className="tool07">인쇄</button></li>
<li><button type="button" className="tool08">다운로드</button></li>
</ul>
</div>
{/* 맵컨트롤 툴바 */}
<div className="control">
<ul className="toolItem zoom">
<li><button type="button" className="zoomin" title="확대"><span className="blind">확대</span></button></li>
<li className="num">7</li>
<li><button type="button" className="zoomout" title="축소"><span className="blind">축소</span></button></li>
</ul>
<ul className="toolItem space mt30">
<li><button
type="button"
className={`legend ${isLegendOpen ? "active" : ""}`}
onClick={() => setIsLegendOpen(prev => !prev)}
>
범례</button>
</li>
<li><button type="button" className="minimap">미니맵</button></li>
</ul>
</div>
{/* 범례 */}
{isLegendOpen && (
<div className="legendWrap">
<ul className="legendList">
<li className="legendItem">
<span className="legendLabel"><img src="/images/ico_legend_all.svg" alt="통합" />통합</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_china.svg" alt="중국어선" />중국어선</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_china_permit.svg" alt="중국어선허가" />중국어선허가</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_japan.svg" alt="일본어선" />일본어선</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_danger.svg" alt="위험물" />위험물</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_passenger.svg" alt="여객선" />여객선</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_vessel.svg" alt="함정" />함정</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_vessel_radar.svg" alt="함정-RADAR" />함정-RADAR</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_general.svg" alt="일반" />일반</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_vts_general.svg" alt="VTS-일반" />VTS-일반</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_vts_radar.svg" alt="VTS-RADAR" />VTS-RADAR</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_vpass.svg" alt="VPASS일반" />VPASS일반</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_enav_fishing.svg" alt="ENAV어선" />ENAV어선</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_enav_danger.svg" alt="ENAV위험물" />ENAV위험물</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_enav_cargo.svg" alt="ENAV화물선" />ENAV화물선</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_enav_government.svg" alt="ENAV관공선" />ENAV관공선</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_enav_general.svg" alt="ENAV일반" />ENAV일반</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_dmfhf.svg" alt="D-MF/HF" />D-MF/HF</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_aircraft.svg" alt="항공기" />항공기</span>
<span className="legendValue">0</span>
</li>
<li>
<span className="legendLabel"><img src="/images/ico_legend_nll.svg" alt="NLL" />NLL</span>
<span className="legendValue">0</span>
</li>
</ul>
</div>
)}
</section>
)
}

파일 보기

@ -0,0 +1,15 @@
import { Outlet } from "react-router-dom";
import HeaderComponent from "./HeaderComponent";
import SideComponent from "./SideComponent";
import ToolComponent from "./ToolComponent";
export default function WrapComponent() {
return (
<div id="wrap" className="wrap">
<HeaderComponent />
<SideComponent />
<Outlet /> {/* Main 영역 */}
<ToolComponent />
</div>
);
}

파일 보기

@ -0,0 +1,48 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
export default function Analysis1Component() {
const navigate = useNavigate();
return (
<section id="Analysis2Component">
{/* 위성 영상 등록 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill w46r">
<div className="puHeader">
<span className="title">관심 해역 설정</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody p0">
<div className="rowSB gap10">
<button type="button"
className="drawBtn"
onClick={() => navigate("/analysis/result")}
>
<i className="rect"></i>
사각형 그리기
</button>
<button type="button"
className="drawBtn"
onClick={() => navigate("/analysis/result")}
>
<i className="polygon"></i>
다각형 그리기
</button>
</div>
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,129 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
export default function Analysis2Component() {
const navigate = useNavigate();
return (
<section id="Analysis2Component">
{/* 위성 영상 등록 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill w61r">
<div className="puHeader">
<span className="title">관심 해역 설정</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody">
<div className="rowSB gap10 pb10">
<button type="button" className="drawBtn sm">사각형 그리기<i className="rect"></i></button>
<button type="button" className="drawBtn sm">다각형 그리기<i className="polygon"></i></button>
</div>
<table className="table">
<caption>관심 해역 설정 - 해상영역명, 설정 옵션, 좌표,영역 옵션,해상영역명 크기, 해상영역명 색상,윤곽선 굵기,윤곽선 종류,윤곽선 색상,채우기 색상 대한 내용을 등록하는 표입니다.</caption>
<colgroup>
<col style={{ width: '125px' }} />
<col style={{ width: '' }} />
<col style={{ width: '125px' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">해상영역명</th>
<td colSpan={3}><input type="text" placeholder="해상영역명" aria-label="해상영역명" /></td>
</tr>
<tr>
<th scope="row">설정 옵션</th>
<td colSpan={3}>
<div className="row">
<label class="checkbox checkL"><input type="checkbox" /><span>사용 여부</span></label>
<label class="checkbox checkL"><input type="checkbox" /><span>알림 여부</span></label>
<label class="checkbox checkL"><input type="checkbox" /><span>공유 여부</span></label>
</div>
</td>
</tr>
<tr>
<th scope="row">좌표</th>
<td colSpan={3}>[124,96891368166156, 36.37855817450263]<br />
[125,25105622872591, 36.37855817450263]<br />
[125,25105622872591, 36.37855817450263]<br />
[125,25105622872591, 36.37855817450263]<br />
[125,25105622872591, 36.37855817450263]
</td>
</tr>
<tr>
<th scope="row">영역 옵션</th>
<td colSpan={3}>
<div className="row">
<label class="checkbox checkL"><input type="checkbox" /><span>해상영역 표시</span></label>
<label class="checkbox checkL"><input type="checkbox" /><span>해상영역명 표시</span></label>
</div>
</td>
</tr>
<tr>
<th scope="row">해상영역명 크기</th>
<td>
<div className="numInput">
<input type="number" placeholder="0" min="" max="" aria-label="해상영역명 크기" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</td>
<th scope="row">해상영역명 색상</th>
<td><i className="colorBox" style={{ backgroundColor: "#000" }}></i></td>
</tr>
<tr>
<th scope="row">윤곽선 굵기 </th>
<td>
<div className="numInput">
<input type="number" placeholder="0" min="" max="" aria-label="윤곽선 굵기 " />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</td>
<th scope="row">윤곽선 종류 </th>
<td>
<select aria-label="윤곽선 종류 ">
<option value="">선택</option>
<option value="">실선</option>
<option value="">점선</option>
</select>
</td>
</tr>
<tr>
<th scope="row">윤곽선 색상 </th>
<td><i className="colorBox" style={{ backgroundColor: "#FF0000" }}></i></td>
<th scope="row">채우기 색상 </th>
<td><i className="colorBox" style={{ backgroundColor: "#7BEBB1" }}></i></td>
</tr>
</tbody>
</table>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button type="button" className="btn basic">저장</button>
<button
type="button"
className="btn dark"
onClick={() => navigate("/main")}
>
취소
</button>
</div>
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,198 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
export default function Analysis3Component() {
const navigate = useNavigate();
return (
<section id="Analysis2Component">
{/* 위성 영상 등록 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill w61r">
<div className="puHeader">
<span className="title">관심 해역 분석 등록</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody noSc">
<div className="analyRow">
{/* 지도캡쳐/테이블 영역 */}
<div className="reg">
<div className="mapCapture"></div>
<button type="button" className="btn btnMS basic icoCapture">지도캡쳐</button>
<table className="table">
<caption>관심 해역 분석 등록 - 제목, 상세 내역, 공유 여부,공유 그룹 대한 내용을 등록하는 표입니다.</caption>
<colgroup>
<col style={{ width: '30%' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">제목</th>
<td><input type="text" placeholder="제목" aria-label="제목" /></td>
</tr>
<tr>
<th scope="row">상세 내역</th>
<td>
<textarea placeholder="내용을 입력하세요" aria-label="상세내역"></textarea>
</td>
</tr>
<tr>
<th scope="row">공유 여부</th>
<td >
<div className="row">
<label class="radio radioL"> <input type="radio" name="share" /> <span>공유</span></label>
<label class="radio radioL"> <input type="radio" name="share" /> <span>공유 안함</span></label>
</div>
</td>
</tr>
<tr>
<th scope="row">공유 그룹 </th>
<td>
<select aria-label="윤곽선 종류 ">
<option value="">전체</option>
<option value="">부서</option>
</select>
</td>
</tr>
</tbody>
</table>
</div>
{/* 관심영역 체크박스 목록 -스크롤됨 */}
<div className="list" >
<div className="tit14">관심영역 목록</div>
<ul className="lineList rowSB">
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>진입진출 테스트</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>테스트 01</span>
</label>
<button type="button" className="btnMap"></button>
</li>
</ul>
</div>
</div>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button type="button" className="btn basic">저장</button>
<button
type="button"
className="btn dark"
onClick={() => navigate("/main")}
>
취소
</button>
</div>
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,235 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
export default function Analysis4Component() {
const navigate = useNavigate();
return (
<section id="Analysis2Component">
{/* 위성 영상 등록 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill w61r">
<div className="puHeader">
<div className="headerL">
<span className="title">350 대해구도</span>
<span className="subTxt">조회시간: 2026-07-00 17:15:13</span>
</div>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody noSc">
<div className="trenchRow">
{/* 지도캡쳐/테이블 영역 */}
<div className="list">
<div className="tit14">통항 선박</div>
<table className="table dataView">
<caption>통항 선박 - 선박 종류, 승선원, 위험물 운반, 공유 여부 그룹 대한 표입니다</caption>
<colgroup>
<col style={{ width: '135px' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">카고()</th>
<td>0</td>
</tr>
<tr>
<th scope="row">카고 승성원()</th>
<td>-</td>
</tr>
<tr>
<th scope="row">탱커수()</th>
<td></td>
</tr>
<tr>
<th scope="row">탱커 승선원()</th>
<td></td>
</tr>
<tr>
<th scope="row">위험물 운반석()</th>
<td></td>
</tr>
<tr>
<th scope="row">위험물 운반선 승선원()</th>
<td></td>
</tr>
<tr>
<th scope="row">위험물 ()</th>
<td></td>
</tr>
<tr>
<th scope="row">어선()</th>
<td></td>
</tr>
<tr>
<th scope="row">어선 승선원()</th>
<td></td>
</tr>
<tr>
<th scope="row">기타 어선()</th>
<td></td>
</tr>
<tr>
<th scope="row">기타 어선 승선원()</th>
<td></td>
</tr>
<tr>
<th scope="row">여객선()</th>
<td></td>
</tr>
<tr>
<th scope="row">유도선()</th>
<td></td>
</tr>
<tr>
<th scope="row">유도선 승선원()</th>
<td></td>
</tr>
<tr>
<th scope="row">기타 선박()</th>
<td></td>
</tr>
<tr>
<th scope="row">기타 선박 승선원()</th>
<td></td>
</tr>
<tr>
<th scope="row">함정수()</th>
<td></td>
</tr>
</tbody>
</table>
</div>
{/* 관심영역 체크박스 목록 -스크롤됨 */}
<div className="list" >
<div className="tit14">신호별</div>
<table className="table dataView">
<caption>신호별 - 제목, 상세 내역, 공유 여부,공유 그룹 대한 내용을 등록하는 표입니다.</caption>
<colgroup>
<col style={{ width: '135px' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">AIS</th>
<td>0</td>
</tr>
<tr>
<th scope="row">V-PASS</th>
<td>-</td>
</tr>
<tr>
<th scope="row">VHF</th>
<td></td>
</tr>
<tr>
<th scope="row">MFHF</th>
<td></td>
</tr>
</tbody>
</table>
<div className="tit14">E-NAV</div>
<table className="table dataView">
<caption>E-NAV - 여객선, 어선, 카고, 관공선, 기타 선박과 공유 정보 대한 표입니다.</caption>
<colgroup>
<col style={{ width: '135px' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">E-NAV 여객선()</th>
<td>0</td>
</tr>
<tr>
<th scope="row">E-NAV 어선()</th>
<td>-</td>
</tr>
<tr>
<th scope="row">E-NAV 카고()</th>
<td></td>
</tr>
<tr>
<th scope="row">E-NAV 관공선()</th>
<td></td>
</tr>
<tr>
<th scope="row">E-NAV 기타()</th>
<td></td>
</tr>
</tbody>
</table>
<div className="tit14">기상정보</div>
<table className="table dataView">
<caption>기상정보 - 유향, 유속, 유의 파고, 파향, 파주기, 풍속, 풍향 나타내는 표입니다</caption>
<colgroup>
<col style={{ width: '135px' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">유향</th>
<td>0</td>
</tr>
<tr>
<th scope="row">유속</th>
<td>-</td>
</tr>
<tr>
<th scope="row">유의 파고</th>
<td>0.5(m)</td>
</tr>
<tr>
<th scope="row">파향</th>
<td>
<div className="rowR gap5">
<img src="/images/ico_dir_arrow.svg" alt="파향" className="arrowDirect"
style={{ transform: 'rotate(350deg)' }}
/>
350(°)
</div>
</td>
</tr>
<tr>
<th scope="row">파주기</th>
<td>3.7(s)</td>
</tr>
<tr>
<th scope="row">풍속</th>
<td>9.2(m/s)</td>
</tr>
<tr>
<th scope="row">풍향</th>
<td>
<div className="rowR gap5">
<img src="/images/ico_dir_arrow.svg" alt="풍향" className="arrowDirect"
style={{ transform: 'rotate(45deg)' }}
/>
45(°)
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div className="puFooter">
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,355 @@
import { useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import Slider from '../components/Slider';
export default function DisplayComponent({ isOpen, onToggle, activeTab: externalTab }) {
const navigate = useNavigate();
//
const [opacity, setOpacity] = useState(70);
//
const [isAccordionOpen1, setIsAccordionOpen1] = useState(true); //
const [isAccordionOpen2, setIsAccordionOpen2] = useState(true); //
const [isAccordionOpen3, setIsAccordionOpen3] = useState(true); //
const [isAccordionOpen4, setIsAccordionOpen4] = useState(false); //
const toggleAccordion1 = () => setIsAccordionOpen1(prev => !prev);
const toggleAccordion2 = () => setIsAccordionOpen2(prev => !prev);
const toggleAccordion3 = () => setIsAccordionOpen3(prev => !prev);
const toggleAccordion4 = () => setIsAccordionOpen4(prev => !prev);
//
const [activeTab, setActiveTab] = useState('filter');
useEffect(() => {
if (externalTab) {
setActiveTab(externalTab);
}
}, [externalTab]);
const tabs = [
{ id: 'filter', label: '필터' },
{ id: 'layer', label: '레이어' },
];
return (
<aside className={`slidePanel ${!isOpen ? 'is-closed' : ''}`}>
{/* 탭 버튼 */}
<div className="tabBox p0">
<div className="tabDefault borderLess">
{tabs.map(tab => (
<button
key={tab.id}
type="button"
className={activeTab === tab.id ? 'on' : ''}
onClick={() => setActiveTab(tab.id)}
>
{tab.label}
</button>
))}
</div>
</div>
{/* 탭 콘텐츠 01 */}
<div className={`tabWrap scrollY ${activeTab === 'filter' ? 'is-active' : ''}`}>
<div className="tabWrapInner">
<div className="tabWrapCnt">
{/* 스위치그룹 01 */}
<div className="switchGroup">
<div className="sgHeader">
<div className="colL">
<span>신호</span>
<label className="switch"> <input type="checkbox" aria-label="신호"/> <span></span></label>
</div>
<button
type="button"
className={`toggleBtn ${isAccordionOpen1 ? 'is-open' : ''}`}
aria-expanded={isAccordionOpen1}
onClick={toggleAccordion1}
></button>
</div>
{/* 여기서부터 토글 */}
<div className={`switchBox ${isAccordionOpen1 ? 'is-open' : ''}`}>
<ul className="switchList">
<li>
<span>AIS</span>
<label className="switch sm"> <input type="checkbox" aria-label="AIS" /> <span></span></label>
</li>
<li>
<span>V-PASS</span>
<label className="switch sm"> <input type="checkbox" aria-label="V-PASS" /> <span></span></label>
</li>
<li>
<span>VTS_AIS</span>
<label className="switch sm"> <input type="checkbox" aria-label="VTS_AIS" /> <span></span></label>
</li>
<li>
<span>D_MF_HF</span>
<label className="switch sm"> <input type="checkbox" aria-label="D_MF_HF" /> <span></span></label>
</li>
<li>
<span>VTS_RADAR</span>
<label className="switch sm"> <input type="checkbox" aria-label="VTS_RADAR" /> <span></span></label>
</li>
</ul>
</div>
{/* 여기까지 */}
</div>
{/* 스위치그룹 02 */}
<div className="switchGroup">
<div className="sgHeader">
<div className="colL">
<span>선종/기종</span>
<label className="switch"> <input type="checkbox" aria-label="선종/기종" /> <span></span></label>
</div>
<button
type="button"
className={`toggleBtn ${isAccordionOpen2 ? 'is-open' : ''}`}
aria-expanded={isAccordionOpen2}
onClick={toggleAccordion2}
></button>
</div>
{/* 여기서부터 토글 */}
<div className={`switchBox ${isAccordionOpen2 ? 'is-open' : ''}`}>
<ul className="switchList">
<li>
<span>어선</span>
<label className="switch sm"> <input type="checkbox" aria-label="어선" /> <span></span></label>
</li>
<li>
<span>여객선</span>
<label className="switch sm"> <input type="checkbox" aria-label="여객선" /> <span></span></label>
</li>
<li>
<span>화물선</span>
<label className="switch sm"> <input type="checkbox" aria-label="화물선" /> <span></span></label>
</li>
<li>
<span>유조선</span>
<label className="switch sm"> <input type="checkbox" aria-label="유조선" /> <span></span></label>
</li>
<li>
<span>관공선</span>
<label className="switch sm"> <input type="checkbox" aria-label="관공선" /> <span></span></label>
</li>
<li>
<span>함정</span>
<label className="switch sm"> <input type="checkbox" aria-label="함정" /> <span></span></label>
</li>
<li>
<span>항공기</span>
<label className="switch sm"> <input type="checkbox" aria-label="항공기" /> <span></span></label>
</li>
<li>
<span>기타</span>
<label className="switch sm"> <input type="checkbox" aria-label="기타" /> <span></span></label>
</li>
</ul>
</div>
{/* 여기까지 */}
</div>
{/* 스위치그룹 03 */}
<div className="switchGroup">
<div className="sgHeader">
<div className="colL">
<span>국적</span>
<label className="switch"> <input type="checkbox" aria-label="국적" /> <span></span></label>
</div>
<button
type="button"
className={`toggleBtn ${isAccordionOpen3 ? 'is-open' : ''}`}
aria-expanded={isAccordionOpen3}
onClick={toggleAccordion3}
></button>
</div>
{/* 여기서부터 토글 */}
<div className={`switchBox ${isAccordionOpen3 ? 'is-open' : ''}`}>
<ul className="switchList">
<li>
<span>한국</span>
<label className="switch sm"> <input type="checkbox" aria-label="한국" /> <span></span></label>
</li>
<li>
<span>중국</span>
<label className="switch sm"> <input type="checkbox" aria-label="중국" /> <span></span></label>
</li>
<li>
<span>일본</span>
<label className="switch sm"> <input type="checkbox" aria-label="일본" /> <span></span></label>
</li>
<li>
<span>북한</span>
<label className="switch sm"> <input type="checkbox" aria-label="북한" /> <span></span></label>
</li>
<li>
<span>기타</span>
<label className="switch sm"> <input type="checkbox" aria-label="기타" /> <span></span></label>
</li>
</ul>
</div>
{/* 여기까지 */}
</div>
{/* 스위치그룹 04 */}
<div className="switchGroup">
<div className="sgHeader">
<div className="colL">
<span>AI 모드</span>
<label className="switch"> <input type="checkbox" aria-label="AI 모드" /> <span></span></label>
</div>
<button
type="button"
className={`toggleBtn ${isAccordionOpen4 ? 'is-open' : ''}`}
aria-expanded={isAccordionOpen4}
onClick={toggleAccordion4}
></button>
</div>
{/* 여기서부터 토글 */}
<div className={`switchBox ${isAccordionOpen4 ? 'is-open' : ''}`}>
<ul className="switchList">
<li>
<span>MMSI 변조</span>
<label className="switch sm"> <input type="checkbox" aria-label="MMSI 변조" /> <span></span></label>
</li>
<li>
<span>중국 허가선박</span>
<label className="switch sm"> <input type="checkbox" aria-label="중국 허가선박" /> <span></span></label>
</li>
<li>
<span>관공선</span>
<label className="switch sm"> <input type="checkbox" aria-label="관공선" /> <span></span></label>
</li>
<li>
<span>비정상 접촉</span>
<label className="switch sm"> <input type="checkbox" aria-label="비정상 접촉" /> <span></span></label>
</li>
<li>
<span>비정상 선박</span>
<label className="switch sm"> <input type="checkbox" aria-label="비정상 선박" /> <span></span></label>
</li>
<li>
<span>북한선박</span>
<label className="switch sm"> <input type="checkbox" aria-label="북한선박" /> <span></span></label>
</li>
</ul>
</div>
{/* 여기까지 */}
</div>
{/* 스위치그룹 05 */}
<div className="switchGroup">
<div className="sgHeader">
<div className="colL">
<span>다크시그널</span>
</div>
<label className="switch"> <input type="checkbox" aria-label="다크시그널" /> <span></span></label>
</div>
</div>
{/* 스위치그룹 06 */}
<div className="switchGroup">
<div className="sgHeader">
<div className="colL">
<span>위험물</span>
</div>
<label className="switch"> <input type="checkbox" aria-label="위험물" /> <span></span></label>
</div>
</div>
{/* 스위치그룹 07 */}
<div className="switchGroup">
<div className="sgHeader">
<div className="colL">
<i className="favship"></i>
<span>관심선박</span>
</div>
<label className="switch"> <input type="checkbox" aria-label="관심선박" /> <span></span></label>
</div>
</div>
</div>
{/* 버튼영역 */}
<div className="btnBox">
<button type="button" className="btn btnLine">저장</button>
</div>
</div>
</div>
{/* 탭 콘텐츠 02 */}
<div className={`tabWrap ${activeTab === 'layer' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">레이어</div>
</div>
<div className="tabBtm noLine">
<div className="tabBtmInner">
<ul className="lineList tabBtmCnt">
<li className="rowSB">
<label className="checkbox checkL">
<input type="checkbox" />
<span>배경지도</span>
</label>
<div className="row">
<span>투명도 조절</span>
<div>
<Slider label="투명도 조절" />
</div>
</div>
</li>
<li className="p0">
<ul className="optionList">
<li>
<span>전자해도</span>
<label className="radio"> <input type="radio" name="map" aria-label="전자해도" /> <span></span></label>
</li>
<li>
<span>일반지도</span>
<label className="radio"> <input type="radio" name="map" aria-label="일반지도" /> <span></span></label>
</li>
<li>
<span>영상지도</span>
<label className="radio"> <input type="radio" name="map" aria-label="영상지도" /> <span></span></label>
</li>
</ul>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>해경관할구역</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>검문검색위치</span>
</label>
</li>
</ul>
<div className='btnBox'>
<button
type="button"
className="btn btnLine w15r"
onClick={() => navigate("/layer/register")}
>레이어 등록</button>
</div>
</div>
</div>
</div>
{/* 사이드패널 토글버튼 */}
<button
type="button"
className="toogle"
aria-expanded={isOpen}
onClick={onToggle}
>
<span className="blind">
{isOpen ? '패널 접기' : '패널 열기'}
</span>
</button>
</aside>
);
}

파일 보기

@ -0,0 +1,4 @@
export default function EmptyMain() {
return null; //
}

파일 보기

@ -0,0 +1,91 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
import FileUpload from '../components/FileUpload';
export default function LayerComponent() {
const navigate = useNavigate();
return (
<section id="LayerComponent">
{/* 레이어등록 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill">
<div className="puHeader">
<span className="title">레이어 등록</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody">
<table className="table">
<caption>레이어등록 - 레이어명, 첨부파일, 공유설정 대한 내용을 나타내는 표입니다.</caption>
<colgroup>
<col style={{ width: '30%' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">레이어명 <span className="required">*</span></th>
<td><input type="text" placeholder="" aria-label="레이어명" /></td>
</tr>
<tr>
<th scope="row">첨부파일 <span className="required">*</span></th>
<td>
<div className="rowC">
<FileUpload
label="파일 선택"
inputId="layerFile"
maxLength={35}
placeholder="선택된 파일 없음"
/>
<span className="helpTxt">geojson 파일을 첨부해 주세요. </span>
</div>
</td>
</tr>
<tr>
<th scope="row">공유설정</th>
<td>
<div className="row flx1">
<label className="checkbox checkL w10r">
<input type="checkbox" />
<span>공유 여부</span>
</label>
<label className="">
<span className="blind">공유설정</span>
<select>
<option value="">전체</option>
<option value="">부서</option>
<option value="">개인</option>
<option value="">개인 & 부서</option>
</select>
</label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button type="button" className="btn basic">저장</button>
<button
type="button"
className="btn dark"
onClick={() => navigate("/main")}
>
취소
</button>
</div>
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,208 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
export default function MyPageComponent() {
const navigate = useNavigate();
//
// null | "password" | "cert"
const [subPopup, setSubPopup] = useState(null);
return (
<section id="MyPageComponent">
{/* 내 정보 조회 */}
<div className="popupUtillWrap">
<div className="popupUtill">
<div className="puHeader">
<span className="title"> 정보 조회</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody">
<table className="table">
<caption>
정보 조회 - 아이디, 비밀번호, 이름, 이메일, 직급, 상세소속, 공인인증서 삭제
</caption>
<colgroup>
<col style={{ width: "30%" }} />
<col />
</colgroup>
<tbody>
<tr>
<th scope="row">아이디</th>
<td>admin222</td>
</tr>
<tr>
<th scope="row">비밀번호</th>
<td>
<button
type="button"
className="btn btnM deep flx0"
onClick={() => setSubPopup("password")}
>
비밀번호 변경
</button>
</td>
</tr>
<tr>
<th scope="row">이름</th>
<td>ADMIN</td>
</tr>
<tr>
<th scope="row">이메일</th>
<td>123@korea.kr</td>
</tr>
<tr>
<th scope="row">직급</th>
<td>경감</td>
</tr>
<tr>
<th scope="row">상세소속</th>
<td></td>
</tr>
<tr>
<th scope="row">공인인증서 삭제</th>
<td>
<button
type="button"
className="btn btnM deep flx0"
onClick={() => setSubPopup("cert")}
>
공인인증서 삭제
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button type="button" className="btn basic">저장</button>
<button type="button" className="btn dark">초기화</button>
</div>
</div>
</div>
</div>
{/* 딤 + 서브 팝업 */}
{subPopup && (
<div className="popupDim">
{/* 비밀번호 변경 */}
{subPopup === "password" && (
<div className="popupUtill">
<div className="puHeader">
<span className="title">비밀번호 수정</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => setSubPopup(null)}
/>
</div>
<div className="puBody">
<table className="table">
<caption>
비밀번호 수정 - 현재 비밀번호, 비밀번호, 비밀번호 확인
</caption>
<colgroup>
<col style={{ width: "30%" }} />
<col />
</colgroup>
<tbody>
<tr>
<th scope="row">현재 비밀번호</th>
<td>
<input type="password" aria-label="현재 비밀번호" />
</td>
</tr>
<tr>
<th scope="row"> 비밀번호</th>
<td>
<input type="password" aria-label="새 비밀번호" />
</td>
</tr>
<tr>
<th scope="row"> 비밀번호 확인</th>
<td>
<input type="password" aria-label="새 비밀번호 확인" />
</td>
</tr>
</tbody>
</table>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button
type="button"
className="btn basic"
onClick={() => setSubPopup(null)}
>
수정
</button>
<button
type="button"
className="btn dark"
onClick={() => setSubPopup(null)}
>
취소
</button>
</div>
</div>
</div>
)}
{/* 공인인증서 삭제 */}
{subPopup === "cert" && (
<div className="popupUtill cert">
<div className="puHeader">
<span className="title">공인인증서 삭제</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => setSubPopup(null)}
/>
</div>
<div className="puBody">
<div className="puTxtBox">
공인인증서를 삭제 하시겠습니까?
</div>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button
type="button"
className="btn basic"
onClick={() => setSubPopup(null)}
>
삭제
</button>
<button
type="button"
className="btn dark"
onClick={() => setSubPopup(null)}
>
취소
</button>
</div>
</div>
</div>
)}
</div>
)}
</section>
);
}

파일 보기

@ -0,0 +1,71 @@
export default function NavComponent({ activeKey, onChange }) {
const gnbList = [
{ key: 'gnb1', class: 'gnb1', label: '선박' },
{ key: 'gnb2', class: 'gnb2', label: '위성' },
{ key: 'gnb3', class: 'gnb3', label: '기상' },
{ key: 'gnb4', class: 'gnb4', label: '분석' },
{ key: 'gnb5', class: 'gnb5', label: '타임라인' },
{ key: 'gnb6', class: 'gnb6', label: 'AI모드' },
{ key: 'gnb7', class: 'gnb7', label: '리플레이' },
{ key: 'gnb8', class: 'gnb8', label: '항적조회' },
];
const sideList = [
{ key: 'filter', class: 'filter', label: '필터' },
{ key: 'layer', class: 'layer', label: '레이어' },
];
return(
<nav id="nav">
{/* <ul className="gnb">
<li><button type="button" className="gnb1 active" title="선박" aria-label="선박"></button></li>
<li><button type="button" className="gnb2" title="위성" aria-label="위성"></button></li>
<li><button type="button" className="gnb3" title="기상" aria-label="기상"></button></li>
<li><button type="button" className="gnb4" title="분석" aria-label="분석"></button></li>
<li><button type="button" className="gnb5" title="타임라인" aria-label="타임라인"></button></li>
<li><button type="button" className="gnb6" title="AI모드" aria-label="AI모드"></button></li>
<li><button type="button" className="gnb7" title="리플레이" aria-label="리플레이"></button></li>
<li><button type="button" className="gnb8" title="항적조회" aria-label="항적조회"><</button></li>
</ul>
<ul className="side">
<li><button type="button" className="filter" title="필터" aria-label="필터"></button></li>
<li><button type="button" className="layer" title="레이어" aria-label="레이어"></button></li>
</ul> */}
<ul className="gnb">
{gnbList.map(item => (
<li key={item.key}>
<button
type="button"
className={`${item.class} ${activeKey === item.key ? 'active' : ''}`}
onClick={() => onChange(item.key)}
aria-label={item.label}
title={item.label}
>
<span className="blind">{item.label}</span>
</button>
</li>
))}
</ul>
<ul className="side">
{sideList.map(item => (
<li key={item.key}>
<button
type="button"
className={`${item.class} ${activeKey === item.key ? 'active' : ''}`}
onClick={() => onChange(item.key)}
aria-label={item.label}
title={item.label}
>
<span className="blind">{item.label}</span>
</button>
</li>
))}
</ul>
</nav>
)
}

파일 보기

@ -0,0 +1,727 @@
import { useState, useEffect } from 'react';
import { Link } from "react-router-dom";
import Panel1DetailComponent from './Panel1DetailComponent';
export default function Panel1Component({ isOpen, onToggle }) {
//
const [view, setView] = useState('list'); // list | detail
//
const [isAccordionOpen1, setIsAccordionOpen1] = useState(false); //
const [isAccordionOpen2, setIsAccordionOpen2] = useState(false); //
const toggleAccordion1 = () => setIsAccordionOpen1(prev => !prev);
const toggleAccordion2 = () => setIsAccordionOpen2(prev => !prev);
//
const [activeTab, setActiveTab] = useState('ship01');
const tabs = [
{ id: 'ship01', label: '선박검색' },
{ id: 'ship02', label: '허가선박' },
{ id: 'ship03', label: '제재단속' },
{ id: 'ship04', label: '침몰선박' },
{ id: 'ship05', label: '선박입출항' },
{ id: 'ship06', label: '관심선박' }
];
return (
<aside className={`slidePanel ${!isOpen ? 'is-closed' : ''}`}>
{/* 👉 상세 화면일 때 */}
{view === 'detail' ? (
<Panel1DetailComponent
isOpen={isOpen}
onToggle={onToggle}
onBack={() => setView('list')}
/>
) : (
<>
{/* ===== 목록 화면 ===== */}
{/* 탭 버튼 */}
<div className="tabBox">
<div className="tabDefault">
{tabs.map(tab => (
<button
key={tab.id}
type="button"
className={activeTab === tab.id ? 'on' : ''}
onClick={() => setActiveTab(tab.id)}
>
{tab.label}
</button>
))}
</div>
</div>
{/* 탭 콘텐츠 01 */}
<div className={`tabWrap ${activeTab === 'ship01' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">선박 검색</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>선종</span>
<select>
<option value="">전체</option>
<option value="">어선</option>
<option value="">함정</option>
<option value="">여객선</option>
<option value="">카고</option>
<option value="">탱커</option>
<option value="">관공선</option>
<option value="">기타</option>
<option value="">낚시어선</option>
</select>
</label>
<label>
<span>국적</span>
<select>
<option value="">전체</option>
<option value="">한국</option>
<option value="">미국</option>
<option value="">중국</option>
<option value="">일본</option>
<option value="">북한</option>
<option value="">기타</option>
</select>
</label>
</li>
<li>
<label>
<span>타겟ID</span>
<input type="text" placeholder="타겟ID" />
</label>
<label>
<span>선박명</span>
<input type="text" placeholder="선박명" />
</label>
</li>
{/* 아코디언 1 */}
<div className={`accordion ${isAccordionOpen1 ? 'is-open' : ''}`}>
<li>
<label>
<span>위험물</span>
<input type="text" placeholder="타겟ID" />
</label>
<label className="checkbox">
<input type="checkbox" />
<span className="w70">MMSI / 호출부호 변경이력</span>
</label>
</li>
<li>
<label>
<span>승선원수</span>
<div className="labelRow">
<div className="numInput">
<input type="number" placeholder="최소" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
<span>-</span>
<div className="numInput">
<input type="number" placeholder="최대" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</div>
</label>
</li>
<li>
<label>
<span>너비(m)</span>
<div className="numInput">
<input type="number" placeholder="최소" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</label>
</li>
</div>
{/* 여기까지 아코디언1 */}
<li>
<button
type="button"
className={`btn btnS semi btnToggle ${isAccordionOpen1 ? 'is-open' : ''}`}
aria-expanded={isAccordionOpen1}
onClick={toggleAccordion1}
>
상세검색
{isAccordionOpen1 ? ' 닫기' : ' 열기'}
</button>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
{/* <div className="schbox mtb24">
<ul>
<li>
<input type="text" className="schInput" placeholder="대표검도" />
</li>
<li>
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div> */}
</div>
<div className="tabBtm">
<ul className="colList line">
<li>
<Link to="/ship" className="active">
<i className="cicle default"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
<li>
<Link to="/ship" className="">
<i className="cicle default"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
<li>
<Link to="/ship" className="">
<i className="cicle red"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
<li>
<Link to="/ship" className="">
<i className="cicle orng"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
</ul>
</div>
</div>
{/* 탭 콘텐츠 02 */}
<div className={`tabWrap ${activeTab === 'ship02' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">허가선박</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>타겟 ID</span>
<input type="text" placeholder="타겟 ID" />
</label>
</li>
<li>
<label>
<span>선박명</span>
<input type="text" placeholder="선박명" />
</label>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm">
<div className="detailWrap">
{/* 선박정보 박스 */}
<ul className="detailBox">
<li className="dbHeader">
<div className="headerL">
<span className="name">ZHELINGYU29801</span>
<span className="type">Fishing</span>
</div>
<div className="headerR">
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
<span className="num">412</span>
<button
type="button"
className="icoArrow"
aria-label="상세보기"
onClick={() => setView('detail')}
></button>
</div>
</li>
<li>
<span className="label">타겟 ID</span>
<span className="value">412417712</span>
</li>
<li>
<span className="label">주정박항</span>
<span className="value">zhelingyu29801</span>
</li>
<li>
<span className="label">어획할당량</span>
<span className="value">100(ton)</span>
</li>
<li>
<span className="label">조업수역구역</span>
<span className="value">, </span>
</li>
</ul>
</div>
</div>
</div>
{/* 탭 콘텐츠 03 */}
<div className={`tabWrap ${activeTab === 'ship03' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">제재단속</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>제재 유형</span>
<select>
<option value="">전체</option>
<option value="">고래포획 의심</option>
<option value="">UN 제재</option>
<option value="">위반행위 규제 정보</option>
<option value="">불법 선박</option>
<option value="">음주 운항 이력</option>
<option value="">다잡아 처분 선박</option>
<option value="">어획량 위반</option>
<option value="">조업 일지 위반</option>
<option value="">망목 내경 미준수</option>
<option value="">입출역 미통보</option>
<option value="">선박서류 미비치</option>
<option value="">어구위반</option>
<option value="">허가 /표지판 위반</option>
<option value="">어획물 전재 위반</option>
<option value="">선원수첩 신분증명서 위반</option>
<option value="">정선 명령 위반</option>
<option value="">어구 설치 조업수역 이탈</option>
<option value="">어획물 운반선 체크포인트 제도 위반</option>
<option value="">포획 채취 금지 체장 위반 어획물 포획</option>
<option value="">조업수역 위반</option>
<option value="">조업 기간 위반</option>
<option value="">어창 용적 위반</option>
<option value="">어창 용적 위반</option>
<option value="">메모</option>
</select>
</label>
</li>
<li>
<label>
<span>선박명</span>
<input type="text" placeholder="선박명" />
</label>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm">
<ul className="colList line">
<li>
<Link to="/ship" className="active">
<i className="cicle default"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
<li>
<Link to="/ship" className="">
<i className="cicle default"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
</ul>
</div>
</div>
{/* 탭 콘텐츠 04 */}
<div className={`tabWrap ${activeTab === 'ship04' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">침몰선박</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>선박명</span>
<input type="text" placeholder="선박명" />
</label>
</li>
<li>
<label>
<span>사고기간</span>
<div className='labelRow'>
<input type="text" className="dateInput" placeholder="연도-월-일" />
<span>-</span>
<input type="text"className="dateInput" placeholder="연도-월-일" /></div>
</label>
</li>
<li>
<label>
<span>사고내용</span>
<input type="text" placeholder="사고내용" />
</label>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm">
<ul className="colList line">
<li>
<Link to="/ship" className="active">
<i className="cicle default"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
<li>
<Link to="/ship" className="">
<i className="cicle default"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
</ul>
</div>
</div>
{/* 탭 콘텐츠 05 */}
<div className={`tabWrap ${activeTab === 'ship05' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">선박입출항</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>출항일시</span>
<input type="text" className="dateInput" placeholder="연도-월-일 - -:-" />
</label>
<label>
<span>~ 입항일시</span>
<input type="text" className="dateInput" placeholder="연도-월-일 - -:-" />
</label>
</li>
<li>
<label>
<span>PMS<br/>출항항구</span>
<select>
<option value="">전체</option>
</select>
</label>
<label>
<span>PMS<br/>입항항구</span>
<select>
<option value="">전체</option>
</select>
</label>
</li>
<li>
<label>
<span>SIE<br/>출항항구</span>
<select>
<option value="">전체</option>
</select>
</label>
<label>
<span>SIE<br/>입항항구</span>
<select>
<option value="">전체</option>
</select>
</label>
</li>
<li>
<label>
<span>타겟ID</span>
<input type="text" placeholder="타겟ID" />
</label>
<label>
<span>선박명</span>
<input type="text" placeholder="선박명" />
</label>
</li>
{/* 여기부터 아코디언 */}
<div className={`accordion ${isAccordionOpen2 ? 'is-open' : ''}`}>
<li>
<label>
<span>낚시여부</span>
<select>
<option value="">전체</option>
<option value="">미선택</option>
<option value="">선택</option>
</select>
</label>
</li>
<li>
<label>
<span>최대<br/>적재톤수</span>
<input type="text" placeholder="0" />
</label>
<label>
<span>최소<br/>적재톤수</span>
<input type="text" placeholder="0" />
</label>
</li>
<li>
<label>
<span>최대<br/>승선원</span>
<input type="text" placeholder="0" />
</label>
<label>
<span>최소<br/>승선원</span>
<input type="text" placeholder="0" />
</label>
</li>
<li>
<label>
<span>최대<br/>승객수</span>
<input type="text" placeholder="0" />
</label>
<label>
<span>최소<br/>승객수</span>
<input type="text" placeholder="0" />
</label>
</li>
<li>
<label>
<span>선종</span>
<select>
<option value="">전체</option>
<option value="">어선</option>
<option value="">함정</option>
<option value="">여객선</option>
<option value="">카고</option>
<option value="">탱커</option>
<option value="">관공선</option>
<option value="">기타</option>
<option value="">낚시어선</option>
</select>
</label>
<label>
<span>국적</span>
<select>
<option value="">전체</option>
<option value="">한국</option>
<option value="">미국</option>
<option value="">중국</option>
<option value="">일본</option>
<option value="">북한</option>
<option value="">기타</option>
</select>
</label>
</li>
</div>
{/* 여기까지 아코디언 */}
<li>
<button
type="button"
className={`btn btnS semi btnToggle ${isAccordionOpen2 ? 'is-open' : ''}`}
aria-expanded={isAccordionOpen2}
onClick={toggleAccordion2}
>
상세검색
{isAccordionOpen2 ? ' 닫기' : ' 열기'}
</button>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm">
<ul className="colList line">
<li>
<Link to="/ship" className="active">
<i className="cicle default"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
<li>
<Link to="/ship" className="">
<i className="cicle default"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
</ul>
</div>
</div>
{/* 탭 콘텐츠 06 */}
<div className={`tabWrap ${activeTab === 'ship06' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">관심선박</div>
<div className="formGroup">
<ul className="lagelW12">
<li>
<label>
<span>관심사유 지정사유</span>
<select>
<option value="">전체</option>
<option value="">불법조업의심</option>
<option value="">불법포경의심</option>
<option value="">MMSI 신호 임의 변경</option>
<option value="">제재 선박 의심</option>
<option value="">북한 선박 의심</option>
<option value="">기타</option>
</select>
</label>
</li>
<li>
<label>
<span>타겟 ID</span>
<input type="text" placeholder="타겟 ID" />
</label>
</li>
<li>
<label>
<span>선박명</span>
<input type="text" placeholder="선박명" />
</label>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm">
<ul className="colList line">
<li>
<Link to="/ship" className="active">
<i className="cicle default"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
<li>
<Link to="/ship" className="">
<i className="cicle default"></i>
<span>0001</span>
<span>1511함A-05</span>
<span>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
</span>
<span>(AIS)</span>
<span className="legend">
<img src="/images/legend_ship_pink.svg" alt="선박" className="legendShip" />
</span>
</Link>
</li>
</ul>
</div>
</div>
{/* 사이드패널 토글버튼 */}
<button
type="button"
className="toogle"
aria-expanded={isOpen}
onClick={onToggle}
>
<span className="blind">
{isOpen ? '패널 접기' : '패널 열기'}
</span>
</button>
{/* 여기까지 전체목록 페이지 */}
</>
)}
</aside>
);
}

파일 보기

@ -0,0 +1,112 @@
import { useState, useEffect } from 'react';
import { Link } from "react-router-dom";
export default function Panel1DetailComponent({ isOpen, onToggle, onBack }) {
//
const [activeTab, setActiveTab] = useState('ship02');
const tabs = [
{ id: 'ship01', label: '선박검색' },
{ id: 'ship02', label: '허가선박' },
{ id: 'ship03', label: '제재단속' },
{ id: 'ship04', label: '침몰선박' },
{ id: 'ship05', label: '선박입출항' },
{ id: 'ship06', label: '관심선박' }
];
return (
<>
{/* <aside className={`slidePanel ${!isOpen ? 'is-closed' : ''}`}> */}
{/* 탭 버튼 */}
<div className="tabBox">
<div className="tabDefault">
{tabs.map(tab => (
<button
key={tab.id}
type="button"
className={activeTab === tab.id ? 'on' : ''}
onClick={() => setActiveTab(tab.id)}
>
{tab.label}
</button>
))}
</div>
</div>
{/* 탭 콘텐츠 01 */}
<div className={`tabWrap ${activeTab === 'ship02' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">
<button
type="button"
className="prevBtn"
aria-label="이전"
onClick={onBack}
/>
ZHELINGYU29801
</div>
</div>
<div className="tabBtm noLine">
<table className="table">
<caption>선박상세설명 - 타겟 ID, 국가, 주정박항,선종,조업수역 구역,어획 할당량(ton),조업 기간,신호 출처 대한 내용을 나타내는 표입니다.</caption>
<colgroup>
<col style={{ width: '125px' }} />
<col style={{ width: '' }} />
<col style={{ width: '125px' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">타겟 ID</th>
<td>412417712</td>
<th scope="row">국가</th>
<td>412</td>
</tr>
<tr>
<th scope="row">주정박항</th>
<td>zhelingyu29801</td>
<th scope="row">선종</th>
<td>Fishing</td>
</tr>
<tr>
<th scope="row">조업수역 구역</th>
<td></td>
<th scope="row">어획 할당량(ton)</th>
<td></td>
</tr>
<tr>
<th scope="row">조업 기간 1</th>
<td colSpan={3}>2024/01/01 - 2024/04/15</td>
</tr>
<tr>
<th scope="row">조업 기간 2</th>
<td colSpan={3}>2024/10/16 - 2024/12/31</td>
</tr>
<tr>
<th scope="row">신호 출처</th>
<td colSpan={3}>VTS_AIS</td>
</tr>
</tbody>
</table>
</div>
</div>
{/* 사이드패널 토글버튼 */}
<button
type="button"
className="toogle"
aria-expanded={isOpen}
onClick={onToggle}
>
<span className="blind">
{isOpen ? '패널 접기' : '패널 열기'}
</span>
</button>
{/* </aside> */}
</>
);
}

파일 보기

@ -0,0 +1,420 @@
import { useState } from 'react';
import { Link, useNavigate } from "react-router-dom";
import Slider from '../components/Slider';
export default function Panel2Component({ isOpen, onToggle }) {
const navigate = useNavigate();
//
const [isAccordionOpen1, setIsAccordionOpen1] = useState(false); //
const [isAccordionOpen2, setIsAccordionOpen2] = useState(false); //
const toggleAccordion1 = () => setIsAccordionOpen1(prev => !prev);
const toggleAccordion2 = () => setIsAccordionOpen2(prev => !prev);
//
const [activeTab, setActiveTab] = useState('ship01');
const tabs = [
{ id: 'ship01', label: '위성영상 관리' },
{ id: 'ship02', label: '위성사업자 관리' },
{ id: 'ship03', label: '위성 관리' },
];
return (
<aside className={`slidePanel ${!isOpen ? 'is-closed' : ''}`}>
{/* 탭 버튼 */}
<div className="tabBox">
<div className="tabDefault">
{tabs.map(tab => (
<button
key={tab.id}
type="button"
className={activeTab === tab.id ? 'on' : ''}
onClick={() => setActiveTab(tab.id)}
>
{tab.label}
</button>
))}
</div>
</div>
{/* 탭 콘텐츠 01 */}
<div className={`tabWrap ${activeTab === 'ship01' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">위성영상 관리</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>영상 촬영일</span>
<div class="labelRow">
<input class="dateInput" placeholder="연도-월-일" type="text" />
<span>-</span>
<input class="dateInput" placeholder="연도-월-일" type="text" />
</div>
</label>
</li>
{/* 아코디언 1 */}
<div className={`accordion pt8 ${isAccordionOpen1 ? 'is-open' : ''}`}>
<li>
<label>
<span>영상 종류</span>
<select>
<option value="">전체</option>
<option value="">VIRS</option>
<option value="">ICEYE_SAR</option>
<option value="">광학</option>
<option value="">예약</option>
<option value="">RF</option>
</select>
</label>
<label>
<span>영상 출처</span>
<select>
<option value="">전체</option>
<option value="">국내/자동</option>
<option value="">국내/수동</option>
<option value="">국외/수동</option>
<option value="">기타</option>
</select>
</label>
</li>
<li>
<label>
<span>위성 궤도</span>
<select>
<option value="">전체</option>
<option value="">저궤도</option>
<option value="">중궤도</option>
<option value="">정지궤도</option>
<option value="">기타</option>
</select>
</label>
<label>
<span>주기</span>
<select>
<option value="">전체</option>
<option value="">0</option>
<option value="">10</option>
<option value="">30</option>
<option value="">60</option>
</select>
</label>
</li>
</div>
{/* 여기까지 아코디언1 */}
<li>
<label>
<span>위성영상명</span>
<input type="text" placeholder="위성영상명" />
</label>
</li>
<li>
<button
type="button"
className={`btn btnS semi btnToggle ${isAccordionOpen1 ? 'is-open' : ''}`}
aria-expanded={isAccordionOpen1}
onClick={toggleAccordion1}
>
상세검색
{isAccordionOpen1 ? ' 닫기' : ' 열기'}
</button>
</li>
<li className="fgBtn rowSB">
<>
<div className="row gap10">
<span>투명도</span>
<div>
<Slider label="투명도 조절" />
</div>
</div>
<div className="row gap10">
<span>밝기</span>
<div>
<Slider label="밝기 조절" />
</div>
</div>
</>
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm noSc">
<div className="tabBtmInner">
{/* 스크롤영역 */}
<div className="tabBtmCnt">
<div className="detailWrap">
{/* 위성정보 박스 */}
<ul className="detailBox stretch">
<li className="dbHeader">
<div className="headerL item2">
<span className="name">업로드 테스트</span>
<span className="type">2025-09-25 16:09:00</span>
</div>
</li>
<li>
<ul className="dbList">
<li>
<span className="label">위성명</span>
<span className="value">VIRS</span>
</li>
<li>
<span className="label">위성영상파일</span>
<span className="value">mda:fae19b9b-e99e-4794- a305-bce6d537ac36_remark_ resized_2022_0102_1709_npp_DNB_750</span>
</li>
<li>
<span className="label">위성명</span>
<span className="value">VIRS</span>
</li>
<li>
<span className="label">영상 출처</span>
<span className="value">VIRS</span>
</li>
</ul>
<div className="btnArea">
<button type="button" className="btnEdit"></button>
<button type="button" className="btnDel" onClick={() => navigate("/satellite/delete")}></button>
<button type="button" className="btnMap"></button>
</div>
</li>
</ul>
{/* 위성정보 박스 */}
<ul className="detailBox stretch">
<li className="dbHeader">
<div className="headerL item2">
<span className="name">업로드 테스트</span>
<span className="type">2025-09-25 16:09:00</span>
</div>
</li>
<li>
<ul className="dbList">
<li>
<span className="label">위성명</span>
<span className="value">VIRS</span>
</li>
<li>
<span className="label">위성영상파일</span>
<span className="value">mda:fae19b9b-e99e-4794- a305-bce6d537ac36_remark_ resized_2022_0102_1709_npp_DNB_750</span>
</li>
<li>
<span className="label">위성명</span>
<span className="value">VIRS</span>
</li>
<li>
<span className="label">영상 출처</span>
<span className="value">VIRS</span>
</li>
</ul>
<div className="btnArea">
<button type="button" className="btnEdit"></button>
<button type="button" className="btnDel"></button>
<button type="button" className="btnMap"></button>
</div>
</li>
</ul>
</div>
</div>
{/* 하단버튼 영역 */}
<div class="btnBox rowSB">
<button type="button" class="btn btnLine">위성영상 폴더 업로드</button>
<button type="button" class="btn btnLine" onClick={() => navigate("/satellite/add")}>위성영상 등록</button>
</div>
</div>
</div>
</div>
{/* 탭 콘텐츠 02 */}
<div className={`tabWrap ${activeTab === 'ship02' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">위성사업자 관리</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>사업자 분류</span>
<select>
<option value="">전체</option>
<option value="">국가</option>
<option value="">연구기관</option>
<option value="">민간사업자</option>
<option value="">기타</option>
</select>
</label>
<label>
<span>사업자명</span>
<input type="text" placeholder="사업자명" />
</label>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm noSc">
<div className="tabBtmInner">
{/* 스크롤영역 */}
<div className="tabBtmCnt">
<div className="detailWrap">
{/* 위성정보 박스 */}
<ul className="detailBox">
<li className="dbHeader">
<div className="headerL item1">
<span className="name">Test 01</span>
</div>
</li>
<li>
<span className="label">사업자 분류</span>
<span className="value">국가</span>
</li>
<li>
<span className="label">국가</span>
<span className="value">대한민국</span>
</li>
<li>
<span className="label">소재지</span>
<span className="value">test</span>
</li>
</ul>
</div>
</div>
{/* 하단버튼 영역 */}
<div class="btnBox">
<button
type="button"
class="btn btnLine"
onClick={() => navigate("/satellite/provider")}
>
등록
</button>
</div>
</div>
</div>
</div>
{/* 탭 콘텐츠 03 */}
<div className={`tabWrap ${activeTab === 'ship03' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">위성 관리</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>사업자 분류</span>
<select>
<option value="">전체</option>
<option value="">국가</option>
<option value="">연구기관</option>
<option value="">민간사업자</option>
<option value="">기타</option>
</select>
</label>
<label>
<span>센서 타입</span>
<select>
<option value="">전체</option>
<option value="">광학</option>
<option value="">SAR</option>
<option value="">RF</option>
<option value="">기타</option>
</select>
</label>
</li>
<li>
<label>
<span>위성명</span>
<input type="text" placeholder="위성명" />
</label>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm noSc">
<div className="tabBtmInner">
{/* 스크롤영역 */}
<div className="tabBtmCnt">
<div className="detailWrap">
{/* 위성정보 박스 */}
<ul className="detailBox">
<li>
<span className="label">사업자명</span>
<span className="value">국토지리정보원</span>
</li>
<li>
<span className="label">위성명</span>
<span className="value">국가</span>
</li>
<li>
<span className="label">센서 타입</span>
<span className="value">test</span>
</li>
<li>
<span className="label">촬영 해상도</span>
<span className="value"></span>
</li>
</ul>
{/* 위성정보 박스 */}
<ul className="detailBox">
<li>
<span className="label">사업자명</span>
<span className="value">국토지리정보원</span>
</li>
<li>
<span className="label">위성명</span>
<span className="value">국가</span>
</li>
<li>
<span className="label">센서 타입</span>
<span className="value">test</span>
</li>
<li>
<span className="label">촬영 해상도</span>
<span className="value"></span>
</li>
</ul>
</div>
</div>
{/* 하단버튼 영역 */}
<div class="btnBox">
<button
type="button"
class="btn btnLine"
onClick={() => navigate("/satellite/manage")}
>
등록
</button>
</div>
</div>
</div>
</div>
{/* 사이드패널 토글버튼 */}
<button
type="button"
className="toogle"
aria-expanded={isOpen}
onClick={onToggle}
>
<span className="blind">
{isOpen ? '패널 접기' : '패널 열기'}
</span>
</button>
</aside>
);
}

파일 보기

@ -0,0 +1,322 @@
import { useState } from 'react';
import { Link } from "react-router-dom";
export default function Panel3Component({ isOpen, onToggle }) {
//
const [activeTab, setActiveTab] = useState('weather01');
const tabs = [
{ id: 'weather01', label: '기상특보' },
{ id: 'weather02', label: '태풍정보' },
{ id: 'weather03', label: '조위관측' },
{ id: 'weather04', label: '조석정보' },
{ id: 'weather05', label: '항공기상' },
];
return (
<aside className={`slidePanel ${!isOpen ? 'is-closed' : ''}`}>
{/* 탭 버튼 */}
<div className="tabBox">
<div className="tabDefault">
{tabs.map(tab => (
<button
key={tab.id}
type="button"
className={activeTab === tab.id ? 'on' : ''}
onClick={() => setActiveTab(tab.id)}
>
{tab.label}
</button>
))}
</div>
</div>
{/* 탭 콘텐츠 01 */}
<div className={`tabWrap ${activeTab === 'weather01' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">기상특보</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>일자</span>
<div className='labelRow'>
<input type="text" className="dateInput" placeholder="연도-월-일" />
<span>-</span>
<input type="text"className="dateInput" placeholder="연도-월-일" /></div>
</label>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm">
<ul className="colList lineSB">
<li>
<Link to="/weather" className="">
<span className="title">1. 폭풍주의: 남해</span>
<span className="meta">시작일시 00:00:00 / 발효일시 00:00:00</span>
</Link>
</li>
<li>
<Link to="/weather" className="">
<span className="title">2. 폭풍주의: 서해</span>
<span className="meta">시작일시 00:00:00 / 발효일시 00:00:00</span>
</Link>
</li>
<li>
<Link to="/weather" className="">
<span className="title">3. 폭풍주의: 동해</span>
<span className="meta">시작일시 00:00:00 / 발효일시 00:00:00</span>
</Link>
</li>
</ul>
</div>
</div>
{/* 탭 콘텐츠 02 */}
<div className={`tabWrap ${activeTab === 'weather02' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">태풍정보</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>연도</span>
<select>
<option value="">선택</option>
</select>
</label>
<label>
<span></span>
<select>
<option value="">선택</option>
</select>
</label>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm">
<ul className="colList lineSB">
<li>
<Link to="/weather" className="">
<span className="title">1. 폭풍주의: 남해</span>
<span className="meta">시작일시 00:00:00 / 발효일시 00:00:00</span>
</Link>
</li>
<li>
<Link to="/weather" className="">
<span className="title">2. 폭풍주의: 서해</span>
<span className="meta">시작일시 00:00:00 / 발효일시 00:00:00</span>
</Link>
</li>
<li>
<Link to="/weather" className="">
<span className="title">3. 폭풍주의: 동해</span>
<span className="meta">시작일시 00:00:00 / 발효일시 00:00:00</span>
</Link>
</li>
</ul>
</div>
</div>
{/* 탭 콘텐츠 03 */}
<div className={`tabWrap ${activeTab === 'weather03' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">조위관측</div>
<div className="legend">
<span className="legendTitle">조위관측 범례</span>
<ul className="legendList">
<li><img src="/images/ico_obsTide.svg" alt="조위관측소" />조위관측소</li>
<li><img src="/images/ico_obsOcean.svg" alt="해양관측소" />해양관측소</li>
<li><img src="/images/ico_obsBuoy.svg" alt="해양관측부이" />해양관측부이</li>
<li><img src="/images/ico_obsCurrent.svg" alt="해수유동관측소" />해수유동관측소</li>
<li><img src="/images/ico_obsScience.svg" alt="해양과학기지" />해양과학기지</li>
</ul>
</div>
</div>
<div className="tabBtm">
<ul className="lineList">
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>조위관측소</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>해양관측소</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>해양관측부이</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>해수유동관측측소</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>해양과학기지</span>
</label>
</li>
</ul>
</div>
</div>
{/* 탭 콘텐츠 04 */}
<div className={`tabWrap ${activeTab === 'weather04' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">조석정보</div>
<div className="legend">
<span className="legendTitle">조위관측 범례</span>
<ul className="legendList">
<li><img src="/images/ico_obsTide.svg" alt="조위관측소" />조위관측소</li>
<li><img src="/images/ico_obsSunrise.svg" alt="일출몰관측지역" />일출몰관측지역</li>
</ul>
</div>
</div>
<div className="tabBtm">
<ul className="lineList">
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>조위관측소</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>일출몰관측지역</span>
</label>
</li>
</ul>
</div>
</div>
{/* 탭 콘텐츠 05 */}
<div className={`tabWrap ${activeTab === 'weather05' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">항공기상</div>
</div>
<div className="tabBtm noLine">
<ul className="lineList">
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>전체</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>양양공항(RKNY) </span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>김포공항(RKSS)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>인천공항(RKSI)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>청주공항(RKTU)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>포항공항(RKTH)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>대구공항(RKTN)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>울산공항(RKPU)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>김해공항(RKPK)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>광주공항(RKJJ)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>사천공항(RKPS)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>무안공항(RKJB)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>여수공항(RKYJ)</span>
</label>
</li>
<li>
<label className="checkbox checkL">
<input type="checkbox" />
<span>제주공항(RKPC)</span>
</label>
</li>
</ul>
</div>
</div>
{/* 사이드패널 토글버튼 */}
<button
type="button"
className="toogle"
aria-expanded={isOpen}
onClick={onToggle}
>
<span className="blind">
{isOpen ? '패널 접기' : '패널 열기'}
</span>
</button>
</aside>
);
}

파일 보기

@ -0,0 +1,521 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
export default function Panel4Component({ isOpen, onToggle }) {
const navigate = useNavigate();
//
const [isAccordionOpen1, setIsAccordionOpen1] = useState(false); //
const toggleAccordion1 = () => setIsAccordionOpen1(prev => !prev);
//
const [activeTab, setActiveTab] = useState('analysis01');
const tabs = [
{ id: 'analysis01', label: '관심 해역' },
{ id: 'analysis02', label: '해역 분석' },
{ id: 'analysis03', label: '해역 진입 선박' },
{ id: 'analysis04', label: '해구 분석' },
];
return (
<aside className={`slidePanel ${!isOpen ? 'is-closed' : ''}`}>
{/* 탭 버튼 */}
<div className="tabBox">
<div className="tabDefault">
{tabs.map(tab => (
<button
key={tab.id}
type="button"
className={activeTab === tab.id ? 'on' : ''}
onClick={() => setActiveTab(tab.id)}
>
{tab.label}
</button>
))}
</div>
</div>
{/* 탭 콘텐츠 01 */}
<div className={`tabWrap ${activeTab === 'analysis01' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">관심 해역</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>영역명</span>
<input type="text" placeholder="" />
</label>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm noSc">
<div className="tabBtmInner">
{/* 스크롤 영역 */}
<div className="tabBtmCnt">
<span>데이터가 없습니다.</span>
{/* <ul className="colList lineSB">
<li>
<Link to="/" className="">
<span className="title">1. 폭풍주의: 남해</span>
<span className="meta">시작일시 00:00:00 / 발효일시 00:00:00</span>
</Link>
</li>
</ul> */}
</div>
{/* 하단고정버튼 */}
<div className="btnBox">
<button
type="button"
class="btn btnLine"
onClick={() => navigate("/analysis/area")}
>등록</button>
</div>
</div>
</div>
</div>
{/* 탭 콘텐츠 02 */}
<div className={`tabWrap ${activeTab === 'analysis02' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">해역 분석</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>조회기간</span>
<div class="labelRow">
<input class="dateInput" placeholder="연도-월-일" type="text" />
<span>-</span>
<input class="dateInput" placeholder="연도-월-일" type="text" />
</div>
</label>
</li>
<li>
<label>
<span>제목</span>
<input type="text" placeholder="" />
</label>
</li>
<li className="fgBtn">
<button
type="button"
className="schBtn"
>검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm noSc">
<div className="tabBtmInner">
{/* 스크롤 영역 */}
<div className="tabBtmCnt">
<span>데이터가 없습니다.</span>
</div>
{/* 하단고정버튼 */}
<div className="btnBox">
<button
type="button"
class="btn btnLine"
onClick={() => navigate("/analysis/register")}
>등록</button>
</div>
</div>
</div>
</div>
{/* 탭 콘텐츠 03 */}
<div className={`tabWrap ${activeTab === 'analysis03' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">해역 진입 선박</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>진입 일시</span>
<div class="labelRow">
<input class="dateInput" placeholder="연도-월-일" type="text" />
<span>-</span>
<input class="dateInput" placeholder="연도-월-일" type="text" />
</div>
</label>
</li>
{/* 아코디언 1 */}
<div className={`accordion pt8 ${isAccordionOpen1 ? 'is-open' : ''}`}>
<li>
<label>
<span>국적</span>
<select>
<option value="">전체</option>
<option value="">한국</option>
<option value="">미국</option>
<option value="">중국</option>
<option value="">일본</option>
<option value="">북한</option>
<option value="">기타</option>
</select>
</label>
<label>
<span>선종</span>
<select>
<option value="">전체</option>
<option value="">어선</option>
<option value="">함정</option>
<option value="">여객선</option>
<option value="">카고</option>
<option value="">탱커</option>
<option value="">관공선</option>
<option value="">기타</option>
<option value="">낚시어선</option>
</select>
</label>
</li>
<li>
<label>
{/* 사용자가 등록한 관심해역리스트 */}
<span>관심 해역</span>
<select>
<option value="">전체</option>
</select>
</label>
<label>
<span>위험물</span>
<select>
<option value="">전체</option>
<option value="">고압</option>
<option value="">가연성/인화성</option>
<option value="">산화성</option>
<option value="">독성</option>
<option value="">방사성</option>
<option value="">기타</option>
</select>
</label>
</li>
</div>
{/* 여기까지 아코디언1 */}
<li>
<label>
<span>타겟ID</span>
<input type="text" placeholder="타겟ID" />
</label>
<label className="checkbox">
<input type="checkbox" />
<span className="w70">허가 선박 여부</span>
</label>
</li>
<li>
<label>
<span>선박명</span>
<input type="text" placeholder="선박명" />
</label>
</li>
<li>
<button
type="button"
className={`btn btnS semi btnToggle ${isAccordionOpen1 ? 'is-open' : ''}`}
aria-expanded={isAccordionOpen1}
onClick={toggleAccordion1}
>
상세검색
{isAccordionOpen1 ? ' 닫기' : ' 열기'}
</button>
</li>
<li className="fgBtn">
<button type="button" className="schBtn">검색</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm">
</div>
</div>
{/* 탭 콘텐츠 04 */}
<div className={`tabWrap ${activeTab === 'analysis04' ? 'is-active' : ''}`}>
<div className="tabTop">
<div className="title">해구 분석</div>
<div className="formGroup">
<ul>
<li>
<label>
<span>전체 통화량</span>
<div className="labelRow">
<div className="numInput">
<input type="number" placeholder="최소" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
<span>~</span>
<div className="numInput">
<input type="number" placeholder="최대" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</div>
</label>
</li>
<li>
<label>
<span>유의파고(m)</span>
<div className="labelRow">
<div className="numInput">
<input type="number" placeholder="최소" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
<span>~</span>
<div className="numInput">
<input type="number" placeholder="최대" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</div>
</label>
</li>
<li>
<label>
<span>파향(deg)</span>
<div className="labelRow">
<div className="numInput">
<input type="number" placeholder="최소" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
<span>~</span>
<div className="numInput">
<input type="number" placeholder="최대" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</div>
</label>
</li>
<li>
<label>
<span>파주기()</span>
<div className="labelRow">
<div className="numInput">
<input type="number" placeholder="최소" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
<span>~</span>
<div className="numInput">
<input type="number" placeholder="최대" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</div>
</label>
</li>
<li>
<label>
<span>풍속(m/s)</span>
<div className="labelRow">
<div className="numInput">
<input type="number" placeholder="최소" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
<span>~</span>
<div className="numInput">
<input type="number" placeholder="최대" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</div>
</label>
</li>
<li>
<label>
<span>풍향(deg)</span>
<div className="labelRow">
<div className="numInput">
<input type="number" placeholder="최소" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
<span>~</span>
<div className="numInput">
<input type="number" placeholder="최대" min="" max="" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</div>
</label>
</li>
<li className="fgBtn rowSB">
<span className="infoTxt">통화량 조회에 최대 30 소요될 있습니다.</span>
<button
type="button"
className="schBtn"
>
검색
</button>
</li>
</ul>
</div>
</div>
<div className="tabBtm">
<div className="detailWrap">
{/* 정보 박스 */}
<ul className="detailBox stretch">
<li className="dbHeader">
<div className="headerL item2">
<span className="name">대해구 1</span>
<span className="type">[131.5 42.5]</span>
</div>
</li>
<li>
<ul className="dbList twoCol">
<li>
<span className="label">전체 통화량</span>
<span className="value">0</span>
<span className="label"></span>
<span className="value noLine"></span>
</li>
<li>
<span className="label">유의 파고</span>
<span className="value">0.9(m)</span>
<span className="label">파향</span>
<span className="value">
<img src="/images/ico_dir_arrow.svg" alt="파향" className="arrowDirect"
style={{ transform: 'rotate(6deg)' }}
/>6(°C)
</span>
</li>
<li>
<span className="label">파주기</span>
<span className="value">4.0(s)</span>
<span className="label">풍속</span>
<span className="value">10.2(m/s)</span>
</li>
<li>
<span className="label">풍향</span>
<span className="value">
<img src="/images/ico_dir_arrow.svg" alt="풍향" className="arrowDirect"
style={{ transform: 'rotate(10deg)' }}
/>10(°)
</span>
<span className="label"></span>
<span className="value noLine"></span>
</li>
</ul>
<div className="btnArea w4r">
<button
type="button"
class="btnMap"
onClick={() => navigate("/analysis/trench")}
></button>
</div>
</li>
</ul>
{/* 정보 박스 */}
<ul className="detailBox stretch">
<li className="dbHeader">
<div className="headerL item2">
<span className="name">대해구 1</span>
<span className="type">[131.5 42.5]</span>
</div>
</li>
<li>
<ul className="dbList twoCol">
<li>
<span className="label">전체 통화량</span>
<span className="value">0</span>
<span className="label"></span>
<span className="value noLine"></span>
</li>
<li>
<span className="label">유의 파고</span>
<span className="value">0.9(m)</span>
<span className="label">파향</span>
<span className="value">
<img src="/images/ico_dir_arrow.svg" alt="파향" className="arrowDirect"
style={{ transform: 'rotate(6deg)' }}
/>6(°C)
</span>
</li>
<li>
<span className="label">파주기</span>
<span className="value">4.0(s)</span>
<span className="label">풍속</span>
<span className="value">10.2(m/s)</span>
</li>
<li>
<span className="label">풍향</span>
<span className="value">
<img src="/images/ico_dir_arrow.svg" alt="풍향" className="arrowDirect"
style={{ transform: 'rotate(10deg)' }}
/>10(°)
</span>
<span className="label"></span>
<span className="value noLine"></span>
</li>
</ul>
<div className="btnArea w4r">
<button type="button" className="btnMap"></button>
</div>
</li>
</ul>
</div>
</div>
</div>
{/* 사이드패널 토글버튼 */}
<button
type="button"
className="toogle"
aria-expanded={isOpen}
onClick={onToggle}
>
<span className="blind">
{isOpen ? '패널 접기' : '패널 열기'}
</span>
</button>
</aside>
);
}

파일 보기

@ -0,0 +1,7 @@
import { useState } from "react";
export default function Panel5Component() {
return (
<section></section>
);
}

파일 보기

@ -0,0 +1,77 @@
import { useState } from "react";
import { Link } from "react-router-dom";
export default function Panel6Component({ isOpen, onToggle }) {
return (
<aside className={`slidePanel ${!isOpen ? 'is-closed' : ''}`}>
<div className="panelHeader">
<h2 className="panelTitle">AI 분석 모드</h2>
</div>
<div className="panelBody">
<ul className="ai">
<li>
<Link to="/" className="on">
<div className="control"><i></i> ON</div>
<span className="title"><img src="/images/ico_ai_trackgap.svg" alt="소실항적" />소실항적</span>
<span className="desc">AIS 신호가 소실된 선박</span>
<span className="keywords">Signal Gap</span>
</Link>
</li>
<li>
<Link to="/" className="">
<div className="control"><i></i> OFF</div>
<span className="title"><img src="/images/ico_ai_route.svg" alt="항로예측" />항로예측</span>
<span className="desc">AI 기반 선박 항로 예측</span>
<span className="keywords">ML Pattern</span>
</Link>
</li>
<li>
<Link to="/" className="">
<div className="control"><i></i> OFF</div>
<span className="title"><img src="/images/ico_ai_shiptype.svg" alt="선종분석" />선종분석</span>
<span className="desc">선박 유형 자동 분류</span>
<span className="keywords">Auto Class</span>
</Link>
</li>
<li>
<Link to="/" className="on">
<div className="control"><i></i> ON</div>
<span className="title"><img src="/images/ico_ai_fishing.svg" alt="조업분석" />조업분석</span>
<span className="desc">구역별 위험도 평가</span>
<span className="keywords">Risk Score</span>
</Link>
</li>
<li>
<Link to="/" className="on">
<div className="control"><i></i> ON</div>
<span className="title"><img src="/images/ico_ai_risk.svg" alt="해역별 위험지수" />해역별 위험지수</span>
<span className="desc">구역별 위험도 평가</span>
<span className="keywords">Risk Score</span>
</Link>
</li>
</ul>
</div>
<div className="panelFooter">
<div className="btnWrap">
<button type="button" className="btn deep">전체 해제</button>
<button type="button" className="btn basic">설정 저장</button>
</div>
</div>
{/* 사이드패널 토글버튼 */}
<button
type="button"
className="toogle"
aria-expanded={isOpen}
onClick={onToggle}
>
<span className="blind">
{isOpen ? '패널 접기' : '패널 열기'}
</span>
</button>
</aside>
);
}

파일 보기

@ -0,0 +1,7 @@
import { useState } from "react";
export default function Panel7Component() {
return (
<section></section>
);
}

파일 보기

@ -0,0 +1,7 @@
import { useState } from "react";
export default function Panel8Component() {
return (
<section></section>
);
}

파일 보기

@ -0,0 +1,100 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
export default function ReplayComponent() {
const navigate = useNavigate();
const max = 100;
const [value, setValue] = useState(30);
const percent = (value / max) * 100;
return(
<section className="ReplayComponent">
<div className="replayWrap">
<button
type="button"
className="barCloseBtn"
aria-label="닫기"
onClick={() => navigate("/main")}
></button>
<div className="replayControlBar">
{/* 재생상태 컨트롤 */}
<div className="control">
<div className="ctrL">
<button
type="button"
className="playBtn"
aria-label="플레이"
></button>
<select className="controlSelect w8r" aria-label="재생속도 선택">
<option value="">2.0X</option>
</select>
</div>
<div className="playProgress">
<input
type="range"
className="playRange"
min="0"
max={max}
aria-label="재생 위치"
value={value}
onChange={(e) => setValue(Number(e.target.value))}
style={{
background: `linear-gradient(
to right,
#FF0000 0%,
#FF0000 ${percent}%,
#D7DBEC ${percent}%,
#D7DBEC 100%
)`
}}
/>
<div className="timeLabel">
2023-08-20&nbsp;&nbsp;10:15:30
</div>
</div>
<div className="ctrR">
<button
type="button"
className="captureBtn"
aria-label="캡쳐"
></button>
<button
type="button"
className="stopBtn"
aria-label="정지"
></button>
</div>
</div>
{/* 재생옵션 영역 */}
<div className="option">
<label>
<span>재생기간</span>
<select className="controlSelect w8r">
<option value="">어제</option>
</select>
<input placeholder="" type="text" className="controlInput w14r" /> ~
<input placeholder="" type="text" className="controlInput w14r" />
</label>
<label>
<span>표출정보</span>
<button
type="button"
className="trackRndBtn"
>항적</button>
</label>
</div>
</div>
</div>
</section>
)
}

파일 보기

@ -0,0 +1,189 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
import FileUpload from '../components/FileUpload';
export default function Satellite1Component() {
const navigate = useNavigate();
return (
<section id="Satellite1Component">
{/* 위성 영상 등록 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill w61r">
<div className="puHeader">
<span className="title">위성 영상 등록</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody">
<table className="table">
<caption>위성 영상 등록 - 사업자명/위성명, 영상 촬영일, 위성영상파일,CSV 파일,위성영상명, 영상전송 주기,영상 종류,위성 궤도,영상 출처,촬영 목적,촬영 모드,취득방법,구매가격, 대한 내용을 등록하는 표입니다.</caption>
<colgroup>
<col style={{ width: '125px' }} />
<col style={{ width: '' }} />
<col style={{ width: '125px' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">사업자명/위성명 <span className="required">*</span></th>
<td colSpan={3}>
<div className="row flex1">
<select aria-label="사업자명">
<option value="">BlackSky</option>
<option value="">ICEYE</option>
<option value="">VIIRS</option>
<option value="">hawkeye360</option>
<option value="">test1</option>
<option value="">국토지리정보원</option>
</select>
<input type="text" placeholder="" aria-label="위성명" />
</div>
</td>
</tr>
<tr>
<th scope="row">영상 촬영일 <span className="required">*</span></th>
<td colSpan={3}><input class="dateInput" placeholder="연도-월-일" type="text" aria-label="영상 촬영일" /></td>
</tr>
<tr>
<th scope="row">위성영상파일 <span className="required">*</span></th>
<td colSpan={3}>
<div className="rowC">
<FileUpload
label="파일 선택"
inputId="videoFile"
maxLength={40}
placeholder="선택된 파일 없음"
/>
</div>
</td>
</tr>
<tr>
<th scope="row">CSV 파일 <span className="required">*</span></th>
<td colSpan={3}>
<div className="rowC">
<FileUpload
label="파일 선택"
inputId="csvFile"
maxLength={45}
placeholder="선택된 파일 없음"
/>
</div>
</td>
</tr>
<tr>
<th scope="row">위성영상명 <span className="required">*</span></th>
<td colSpan={3}><input type="text" placeholder="" aria-label="위성영상명" /></td>
</tr>
<tr>
<th scope="row">영상전송 주기 </th>
<td colSpan={3}>
<select aria-label=">영상전송 주기">
<option value="">선택</option>
<option value="">0</option>
<option value="">10</option>
<option value="">30</option>
<option value="">60</option>
</select>
</td>
</tr>
<tr>
<th scope="row">영상 종류 </th>
<td colSpan={3}>
<div className="row">
<label className="radio radioL"> <input type="radio" name="type" /> <span>VIRS</span></label>
<label className="radio radioL"> <input type="radio" name="type" /> <span>ICEYE_SAR</span></label>
<label className="radio radioL"> <input type="radio" name="type" /> <span>광학</span></label>
<label className="radio radioL"> <input type="radio" name="type" /> <span>예약</span></label>
<label className="radio radioL"> <input type="radio" name="type" /> <span>RF</span></label>
</div>
</td>
</tr>
<tr>
<th scope="row">위성 궤도 </th>
<td>
<select aria-label="위성 궤도">
<option value="">선택</option>
<option value="">저궤도</option>
<option value="">중궤도</option>
<option value="">정지궤도</option>
<option value="">기타</option>
</select>
</td>
<th scope="row">영상 출처</th>
<td>
<select aria-label="영상 출처">
<option value="">선택</option>
<option value="">국내/자동</option>
<option value="">국내/수동</option>
<option value="">국외/수동</option>
<option value="">기타</option>
</select>
</td>
</tr>
<tr>
<th scope="row">촬영 목적 </th>
<td>
<input type="text" placeholder="촬영 목적" aria-label="촬영 목적"/>
</td>
<th scope="row">촬영 모드 </th>
<td>
<select aria-label="촬영 모드">
<option value="">선택</option>
<option value="">스핏모드</option>
<option value="">스트랩모드</option>
<option value="">기타</option>
</select>
</td>
</tr>
<tr>
<th scope="row">취득방법 </th>
<td>
<select aria-label="취득방법">
<option value="">선택</option>
<option value="">무료</option>
<option value="">개별구매</option>
<option value="">단가계약</option>
<option value="">연간계약</option>
<option value="">기타</option>
</select>
</td>
<th scope="row">구매가격 </th>
<td>
<div className="numInput">
<input type="number" placeholder="0" min="" max="" aria-label="구매가격" />
<div className="spin">
<button type="button" className="spinUp"><span className="blind">증가</span></button>
<button type="button" className="spinDown"><span className="blind">감소</span></button>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button type="button" className="btn basic">저장</button>
<button
type="button"
className="btn dark"
onClick={() => navigate("/main")}
>
취소
</button>
</div>
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,87 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
export default function Satellite2Component() {
const navigate = useNavigate();
return (
<section id="Satellite2Component">
{/* 위성 사업자 등록 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill">
<div className="puHeader">
<span className="title">위성 사업자 등록</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody">
<table className="table">
<caption>위성 사업자 등록 - 사업자 분류, 사업자명, 국가, 소재지, 상세내역 대한 내용을 등록하는 표입니다.</caption>
<colgroup>
<col style={{ width: '30%' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">사업자 분류 <span className="required">*</span></th>
<td>
<select aria-label="사업자 분류">
<option value="">전체</option>
<option value="">국가</option>
<option value="">연구기관</option>
<option value="">민간사업자</option>
<option value="">기타</option>
</select>
</td>
</tr>
<tr>
<th scope="row">사업자명 </th>
<td><input type="text" placeholder="사업자명" aria-label="사업자명" /></td>
</tr>
<tr>
<th scope="row">국가 <span className="required">*</span></th>
<td>
<select aria-label="국가">
<option value="">선택</option>
<option value="">대한민국</option>
<option value="">미국</option>
<option value="">일본</option>
<option value="">중국</option>
</select>
</td>
</tr>
<tr>
<th scope="row">소재지 </th>
<td><input type="text" placeholder="소재지" aria-label="소재지" /></td>
</tr>
<tr>
<th scope="row">상세내역 </th>
<td><textarea placeholder="내용을 입력하세요" aria-label="상세내역"></textarea></td>
</tr>
</tbody>
</table>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button type="button" className="btn basic">저장</button>
<button
type="button"
className="btn dark"
onClick={() => navigate("/main")}
>
취소
</button>
</div>
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,94 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
export default function Satellite3Component() {
const navigate = useNavigate();
return (
<section id="Satellite3Component">
{/* 위성 관리 등록 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill">
<div className="puHeader">
<span className="title">위성 관리 등록</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody">
<table className="table">
<caption>위성 관리 등록 - 사업자명, 위성명, 센서 타입, 촬영 해상도, 주파수, 상세내역 대한 내용을 등록하는 표입니다.</caption>
<colgroup>
<col style={{ width: '30%' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">사업자명 <span className="required">*</span></th>
<td>
<select aria-label="사업자명">
<option value="">전체</option>
<option value="">BlackSky</option>
<option value="">ICEYE</option>
<option value="">VIIRS</option>
<option value="">hawkeye360</option>
<option value="">test1</option>
<option value="">국토지리정보원</option>
</select>
</td>
</tr>
<tr>
<th scope="row">위성명 <span className="required">*</span></th>
<td><input type="text" placeholder="위성명" aria-label="위성명" /></td>
</tr>
<tr>
<th scope="row">센서 타입 </th>
<td>
<select aria-label="센서 타입">
<option value="">전체</option>
<option value="">광학</option>
<option value="">SAR</option>
<option value="">RF</option>
<option value="">기타</option>
</select>
</td>
</tr>
<tr>
<th scope="row">촬영 해상도 </th>
<td><input type="text" placeholder="촬영 해상도" aria-label="촬영 해상도" /></td>
</tr>
<tr>
<th scope="row">주파수 </th>
<td><input type="text" placeholder="주파수" aria-label="주파수" /></td>
</tr>
<tr>
<th scope="row">상세내역 </th>
<td><textarea placeholder="내용을 입력하세요" aria-label="상세내역"></textarea></td>
</tr>
</tbody>
</table>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button type="button" className="btn basic">저장</button>
<button
type="button"
className="btn dark"
onClick={() => navigate("/main")}
>
취소
</button>
</div>
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,50 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
export default function Satellite4Component() {
const navigate = useNavigate();
return (
<section id="Satellite4Component">
{/* 삭제 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill w46r">
<div className="puHeader">
<span className="title">삭제</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody">
<div className="puTxtBox">삭제 하시겠습니까?</div>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button
type="button"
className="btn basic"
oonClick={() => navigate("/main")}
>
삭제
</button>
<button
type="button"
className="btn dark"
onClick={() => navigate("/main")}
>
취소
</button>
</div>
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,169 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
export default function ShipComponent() {
const navigate = useNavigate();
//progress bar value
const [value, setValue] = useState(60);
//
const images = [
{ src: "/images/photo_ship_001.png", alt: "1511함A-05" },
{ src: "/images/photo_ship_002.png", alt: "1511함A-05" },
];
const [currentIndex, setCurrentIndex] = useState(0);
const handlePrev = () => {
if (currentIndex === 0) return;
setCurrentIndex(prev => prev - 1);
};
const handleNext = () => {
if (currentIndex === images.length - 1) return;
setCurrentIndex(prev => prev + 1);
};
return(
<section id="shipComponent">
{/* 배정보 팝업 */}
<div className="popupMap shipInfo">
{/* header */}
<div className="pmHeader">
<div className="rowL">
<i className="shipType"></i>
<img src="/images/flag_kor.svg" alt="대한민국" className="flagIcon" />
<span className="shipName">1511함A-05</span>
<span className="shipNum">13450135</span>
</div>
<button
type="button"
className="pmClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="pmGallery">
<button
type="button"
className={`navBtn prev ${currentIndex === 0 ? "disabled" : ""}`}
onClick={handlePrev}
disabled={currentIndex === 0}
>
<span className="blind">이전</span>
</button>
<button
type="button"
className={`navBtn next ${currentIndex === images.length - 1 ? "disabled" : ""}`}
onClick={handleNext}
disabled={currentIndex === images.length - 1}
>
<span className="blind">다음</span>
</button>
{/* 이미지 영역 */}
<div className="galleryView">
<img
className="galleryImg"
src={images[currentIndex].src}
alt={images[currentIndex].alt}
/>
</div>
</div>
{/* body */}
<div className="pmBody">
<div className="shipAction">
<div className="rowL">
<button type="button" className="detailBtn">상세정보</button>
<ul className="shipTypeIco">
<li>A</li>
<li>V</li>
<li>E</li>
<li>T</li>
<li>D</li>
<li>R</li>
</ul>
</div>
<button type="button" className="favBtn" aria-label="즐겨찾기"></button>
</div>
<div className="shipRoute">
<div
className="routeProgress"
style={{ "--progress": value }}
>
<progress max="100" value={value}>{value}%</progress>
<span className="routeShip"></span>
</div>
</div>
<ul className="shipStatus">
<li className="port">
<div className="rowL">
<span className="portLabel">출항지</span>
<span className="portName">서귀포해양경찰서</span>
</div>
<div className="rowR">
<span className="portLabel">입항지</span>
<span className="portName">하태도</span>
</div>
</li>
<li className="schedule">
<div className="rowL">
<span className="depart">출항일시</span>
<span className="scheduleDate">2024-11-23 11:23:00</span>
</div>
<div className="rowR">
<span className="arrive">입항일시</span>
<span className="scheduleDate">2024-11-23 11:23:00</span>
</div>
</li>
<li className="status">
<div className="statusItem">
<span className="statusLabel">선박상태</span>
<span className="statusValue">정박</span>
</div>
<div className="statusItem w13r">
<span className="statusLabel">속도/항로</span>
<span className="statusValue">4.2 kn / 13.3˚</span>
</div>
<div className="statusItem">
<span className="statusLabel">흘수</span>
<span className="statusValue">1.1m</span>
</div>
</li>
</ul>
{/* <ul className="shipSensor">
<li>
<span className="sensorLabel">AIS</span>
<span className="sensorValue"><i className="isNomal"></i>정상</span>
</li>
<li>
<span className="sensorLabel">RF</span>
<span className="sensorValue"><i className="isNomal"></i>정상</span>
</li>
<li>
<span className="sensorLabel">EO</span>
<span className="sensorValue"><i className="isNomal"></i>정상</span>
</li>
<li>
<span className="sensorLabel">SAR</span>
<span className="sensorValue"><i className="isOff"></i>비활성</span>
</li>
</ul> */}
<div className="btnWrap">
<button type="button" className="trackBtn">항적조회</button>
<button type="button" className="trackBtn">항로예측</button>
</div>
</div>
{/* footer */}
<div className="pmFooter">데이터 수신시간 : 2024-11-23 11:23:00</div>
</div>
</section>
)
}

파일 보기

@ -0,0 +1,58 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
export default function Signal1Component() {
const navigate = useNavigate();
return (
<section id="SignalComponent">
{/* 신호설정 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill">
<div className="puHeader">
<span className="title">신호설정</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody">
<table className="table">
<caption>신호설정 - 신호표출반경, 수신수기 설정 대한 내용을 나타내는 표입니다.</caption>
<colgroup>
<col style={{ width: '30%' }} />
<col style={{ width: '' }} />
</colgroup>
<tbody>
<tr>
<th scope="row">신호표출반경</th>
<td>
<select aria-label="신호표출반경">
<option value="">25NM</option>
</select>
</td>
</tr>
<tr scope="row">
<th>수신수기 설정</th>
<td><input type="text" placeholder="" aria-label="수신수기 설정" /></td>
</tr>
</tbody>
</table>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button type="button" className="btn basic">저장</button>
<button type="button" className="btn dark">초기화</button>
</div>
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,149 @@
import { useState } from 'react';
import { useNavigate } from "react-router-dom";
export default function Signal2Component() {
const navigate = useNavigate();
// (3, )
const [accordionOpen, setAccordionOpen] = useState({
signal1: true,
signal2: true,
signal3: true,
});
const toggleAccordion = (key) => {
setAccordionOpen((prev) => ({ ...prev, [key]: !prev[key] }));
};
return (
<section id="SignalComponent">
{/* 신호설정 팝업 */}
<div className="popupUtillWrap">
<div className="popupUtill">
<div className="puHeader">
<span className="title">맞춤 설정</span>
<button
type="button"
className="puClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
<div className="puBody">
{/* 아코디언그룹 01 */}
<div className="accordionWrap">
<div className="acdHeader">
<span className="title">NLL 고속 선박 탐지</span>
<button
type="button"
className={`toggleListBtn ${accordionOpen.signal1 ? 'open' : ''}`}
onClick={() => toggleAccordion('signal1')}
aria-expanded={accordionOpen.signal1}
aria-label={accordionOpen.signal1 ? '접기' : '펼치기'}
></button>
</div>
{/* 여기서부터 아코디언 */}
<div className={`acdListBox ${accordionOpen.signal1 ? 'open' : ''}`}>
<ul className="acdList input">
<li className="state">
<label className="radio radioL"> <input type="radio" name="state1" /> <span>사용</span></label>
<label className="radio radioL"> <input type="radio" name="state1" /> <span>미사용</span></label>
</li>
<li className="input">
<label>
<span>SOG 기준</span>
<input type="text" placeholder="" />
</label>
</li>
<li className="input">
<label>
<span>COG 기준</span>
<input type="text" placeholder="" />
</label>
</li>
<li className="input">
<label>
<span>유지시간()</span>
<input type="text" placeholder="" />
</label>
</li>
</ul>
</div>
{/* 여기까지 */}
</div>
{/* 아코디언그룹 02 */}
<div className="accordionWrap">
<div className="acdHeader">
<span className="title">특정 어업수역 탐지</span>
<button
type="button"
className={`toggleListBtn ${accordionOpen.signal2 ? 'open' : ''}`}
onClick={() => toggleAccordion('signal2')}
aria-expanded={accordionOpen.signal2}
aria-label={accordionOpen.signal2 ? '접기' : '펼치기'}
></button>
</div>
{/* 여기서부터 아코디언 */}
<div className={`acdListBox ${accordionOpen.signal2 ? 'open' : ''}`}>
<ul className="acdList check">
<li className="state">
<label className="radio radioL"> <input type="radio" name="state2" /> <span>사용</span></label>
<label className="radio radioL"> <input type="radio" name="state2" /> <span>미사용</span></label>
</li>
<li className="check">
<label class="checkbox checkL"><input type="checkbox" /><span>특정 어업수역 I</span></label>
</li>
<li className="check">
<label class="checkbox checkL"><input type="checkbox" /><span>특정 어업수역 II</span></label>
</li>
<li className="check">
<label class="checkbox checkL"><input type="checkbox" /><span>특정 어업수역 III</span></label>
</li>
<li className="check">
<label class="checkbox checkL"><input type="checkbox" /><span>특정 어업수역 IV</span></label>
</li>
</ul>
</div>
{/* 여기까지 */}
</div>
{/* 아코디언그룹 03 */}
<div className="accordionWrap">
<div className="acdHeader">
<span className="title">위험화물 식별</span>
<button
type="button"
className={`toggleListBtn ${accordionOpen.signal3 ? 'open' : ''}`}
onClick={() => toggleAccordion('signal3')}
aria-expanded={accordionOpen.signal3}
aria-label={accordionOpen.signal1 ? '접기' : '펼치기'}
></button>
</div>
{/* 여기서부터 아코디언 */}
<div className={`acdListBox ${accordionOpen.signal3 ? 'open' : ''}`}>
<ul className="acdList">
<li className="state">
<label className="radio radioL"> <input type="radio" name="state3" /> <span>사용</span></label>
<label className="radio radioL"> <input type="radio" name="state3" /> <span>미사용</span></label>
</li>
</ul>
</div>
{/* 여기까지 */}
</div>
</div>
<div className="puFooter">
<div className="popBtnWrap">
<button type="button" className="btn basic">저장</button>
<button type="button" className="btn dark">초기화</button>
</div>
</div>
</div>
</div>
</section>
);
}

파일 보기

@ -0,0 +1,59 @@
import { Link } from "react-router-dom";
export default function ToastComponent() {
return(
<section id="toastComponent">
{/* 지도상 배표식 */}
<div className="shipMapContainer">
<div className="shipMap shipCaution">
<Link to="/">
1511함A-05
<span className="status">12.5 kts | 45°</span>
</Link>
</div>
<div className="shipMap shipWarning">
<Link to="/">
1511함A-05
<span className="status">12.5 kts | 45°</span>
</Link>
</div>
<div className="shipMap shipDefault">
<Link to="/">
1511함A-05
<span className="status">12.5 kts | 45°</span>
</Link>
</div>
</div>
{/* 토스트팝업 */}
<div className="toastContainer">
<div className="toast toastCaution">
<span className="toastMsg">104 어업구역 비인가 선박</span>
<span className="toastR">
<button type="button" className="toastAction">위치보기</button>
<button type="button" className="toastClose" aria-label="닫기"></button>
</span>
</div>
<div className="toast toastCaution">
<span className="toastMsg">104 어업구역 비인가 선박</span>
<span className="toastR">
<button type="button" className="toastAction">위치보기</button>
<button type="button" className="toastClose" aria-label="닫기"></button>
</span>
</div>
<div className="toast toastWarining">
<span className="toastMsg">저속 이동 의심 선박</span>
<span className="toastR">
<button type="button" className="toastAction">위치보기</button>
<button type="button" className="toastClose" aria-label="닫기"></button>
</span>
</div>
</div>
</section>
)
}

파일 보기

@ -0,0 +1,21 @@
export default function TopComponent() {
return(
<section className="topBar">
<div className="locationInfo">
<ul>
<li><button type="button" className="map active"><span className="blind">지도</span></button></li>
<li className="divider"><span className="wgs">경도</span><span>129°</span> <span>38</span><span>31.071</span><span>E</span></li>
<li className="divider"><span className="wgs">위도</span><span>35° </span> <span>21</span><span>24.580</span><span>N</span></li>
<li><span className="kst">KST</span><span>2024-07-01()</span> <span>12:00:00</span></li>
<li><button type="button" className="set"><span className="blind">설정</span></button></li>
<li><button type="button" className="ship"><span className="blind">선박</span></button></li>
</ul>
</div>
<div className="topSchBox">
<input type="text" className="tschInput" placeholder="선박 위치 검색" />
<button type="button" className="mainSchBtn">검색</button>
</div>
</section>
)
}

파일 보기

@ -0,0 +1,41 @@
import { useNavigate } from "react-router-dom";
export default function TrackComponent() {
const navigate = useNavigate();
return(
<section className="TrackComponent">
<div className="trackWrap">
<button
type="button"
className="barCloseBtn"
aria-label="닫기"
onClick={() => navigate("/main")}
></button>
{/* 항적조회 검색바 */}
<div className="trackControlBar">
<label>
<span>시간선택</span>
<input placeholder="" type="text" className="controlInput w15r" aria-label="시작시간" /> ~
<input placeholder="" type="text" className="controlInput w15r" aria-label="종료시간" />
</label>
<label>
<span>재생주기</span>
<select className="controlSelect w16r">
<option value="">1</option>
</select>
</label>
<button
type="button"
className="schRndBtn"
>검색</button>
</div>
</div>
</section>
)
}

파일 보기

@ -0,0 +1,71 @@
import { useNavigate } from "react-router-dom";
export default function WeatherComponent() {
const navigate = useNavigate();
return(
<section id="WeatherComponent">
<div className="popupMap osbInfo">
{/* header */}
<div className="pmHeader">
<div className="rowL">
<span className="title">해양관측소</span>
</div>
<button
type="button"
className="pmClose"
aria-label="닫기"
onClick={() => navigate("/main")}
/>
</div>
{/* body */}
<div className="pmBody">
<ul className="osbStatus">
<li className="date">
2023.10.16 20:54
</li>
<li>
<span className="label">조위</span>
<span className="value">251(cm)</span>
</li>
<li>
<span className="label">수온</span>
<span className="value">19.6(°C)</span>
</li>
<li>
<span className="label">염분</span>
<span className="value">31.8(PSU)</span>
</li>
<li>
<span className="label">기온</span>
<span className="value">16.9(°C)</span>
</li>
<li>
<span className="label">기압</span>
<span className="value">1016.6(hPa)</span>
</li>
<li>
<span className="label">풍향</span>
<span className="value">315(deg)</span>
</li>
<li>
<span className="label">풍속</span>
<span className="value">7.1(m/s)</span>
</li>
<li>
<span className="label">유속방향</span>
<span className="value">-(deg)</span>
</li>
<li>
<span className="label">유속</span>
<span className="value">-(m/s)</span>
</li>
</ul>
</div>
</div>
</section>
)
}

파일 보기

@ -0,0 +1,109 @@
@charset "utf-8";
#wrap {
/* header */
#header {
width: 100%;
height: 4.4rem;
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
background: var(--secondary5);
.logoArea {
display: flex;
align-items: center;
.logo {
width: 4.3rem;
height: 4.4rem;
display: flex;
align-items: center;
justify-content: center;
background: url(../assets/images/logo.svg) no-repeat center / contain;
}
.logoTxt {
color: var(--white);
font-weight: var(--fw-bold);
}
}
aside {
ul {
display: flex;
li {
position: relative;
width: 3rem;
height: 3rem;
margin-right: .9rem;
&.setWrap:hover .setMenu {
display: block;
}
a {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-repeat: no-repeat;
background-position: center;
background-size: 3rem;
&.alram {
background-image: url(../assets/images/ico_alarm.svg);
}
&.set {
background-image: url(../assets/images/ico_set.svg);
}
&.user {
background-image: url(../assets/images/ico_user.svg);
}
}
}
}
}
.setMenu {
display: none;
position: absolute;
top: 2.8rem;
right: -1rem;
min-width: 22rem;
background-color:var(--secondary2) ;
padding: .6rem 0;
z-index: 100;
pointer-events: auto;
a {
display: block;
padding: .8rem 1.5rem;
font-size: var(--fs-m);
font-weight: var(--fw-bold);
&:hover {
background-color:var(--primary1);
}
}
}
.badge {
position: absolute;
top: .6rem;
right: .8rem;
display: block;
width: .5rem;
height: .6rem;
border-radius: 50%;
background-color: var(--alert);
}
}
}

파일 보기

@ -0,0 +1,557 @@
@charset "utf-8";
#wrap {
width: 100%;
height: 100%;
/* header */
#header {
width: 100%;
height: 4.4rem;
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
background: var(--secondary5);
.logoArea {
display: flex;
align-items: center;
.logo {
width: 4.3rem;
height: 4.4rem;
display: flex;
align-items: center;
justify-content: center;
background: url(../assets/images/logo.svg) no-repeat center / contain;
}
.logoTxt {
color: var(--white);
font-weight: var(--fw-bold);
}
}
aside {
ul {
display: flex;
li {
width: 3rem;
height: 3rem;
margin-right: .9rem;
a {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-repeat: no-repeat;
background-position: center;
background-size: 3rem;
&.alram {
background-image: url(../assets/images/ico_alarm.svg);
}
&.set {
background-image: url(../assets/images/ico_set.svg);
}
&.user {
background-image: url(../assets/images/ico_user.svg);
}
}
}
}
}
.badge {
position: absolute;
top: .6rem;
right: .8rem;
display: block;
width: .5rem;
height: .6rem;
border-radius: 50%;
background-color: var(--alert);
}
}
#sidePanel {
/* gnb */
#nav {
position: fixed;
top: 4.4rem;
left: 0;
z-index: 100;
width: 4.3rem;
height: calc(100% - 4.4rem);
display: flex;
flex-direction: column;
justify-content: space-between;
background: var(--secondary1);
border-right: 1px solid var(--tertiary2);
.gnb {
width: 4.2rem;
display: flex;
flex-direction: column;
li {
width: 100%;
height: 4.4rem;
display: flex;
align-items: center;
justify-content: center;
a {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-repeat: no-repeat;
background-position: center;
background-size: 3rem;
/* 공통 상태 */
&:hover,
&:active,
&.active {
background-color: var(--primary1);
}
/* gnb icons */
&.gnb1 {
background-image: url(../assets/images/ico_gnb01.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb01_on.svg);
}
}
&.gnb2 {
background-image: url(../assets/images/ico_gnb02.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb02_on.svg);
}
}
&.gnb3 {
background-image: url(../assets/images/ico_gnb03.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb03_on.svg);
}
}
&.gnb4 {
background-image: url(../assets/images/ico_gnb04.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb04_on.svg);
}
}
&.gnb5 {
background-image: url(../assets/images/ico_gnb05.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb05_on.svg);
}
}
&.gnb6 {
background-image: url(../assets/images/ico_gnb06.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb06_on.svg);
}
}
}
}
}
}
/* side-pannel */
.slidePanel {
position: fixed;
top: 4.4rem;
left: 4.3rem;
width: 54rem;
height: calc(100% - 4.4rem);
display: flex;
flex-direction: column;
background-color: var(--secondary2);
z-index: 99;
transform: translateX(0);
transition: transform .3s ease;
/* 닫힘 상태 */
&.is-closed {
transform: translateX(-100%);
.toogle::after {
transform: translate(-50%, -50%) rotate(180deg);
}
}
.tabBox {
flex-shrink: 0;
width: 100%;
height: 4.8rem;
padding: 0 1.9rem;
margin-top: 1.5rem;
}
.tabWrap {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
&.is-active {
display: flex;
}
.tabTop {
flex-shrink: 0;
width: 100%;
padding: 0 2rem;
border-bottom: 1px solid var(--secondary1);
.title {
height: 6.4rem;
font-size: var(--fs-ml);
color: var(--white);
padding: 2rem 0;
}
}
.tabBtm {
flex: 1;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
padding: 3rem 1.9rem;
}
}
.toogle {
position: absolute;
overflow: hidden;
z-index: 10;
top: 50%;
left: 100%;
width: 2.4rem;
height: 5rem;
border-radius: 0 1rem 1rem 0;
transform: translateY(-50%);
background-color: var(--secondary2);
border: solid 1px var(--secondary3);
&::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 2.4rem;
height: 2.4rem;
transform: translate(-50%, -50%);
background: url(../assets/images/ico_toggle.svg) no-repeat center / 2.4rem;
transition: transform .3s ease;
}
}
}
}
/* main */
#main {
width: 100%;
height: calc(100% - 4.4rem);
position: relative;
/* top-bar */
.topBar {
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
padding: 1.5rem 5.4rem;
}
/* top-location */
.locationInfo {
grid-column: 2;
width: 71rem;
max-width: 100%;
height: 3.8rem;
background-color: rgba(var(--secondary6-rgb), .9);
ul {
display: flex;
align-items: center;
width: 100%;
height: 100%;
li {
display: flex;
align-items: center;
height: 100%;
padding: 0 1rem;
color: var(--white);
font-weight: var(--fw-bold);
font-size: 1.3rem;
&:first-child,
&:last-child {
padding: 0;
}
span + span {
padding-right: .5rem;
}
&.divider {
position: relative;
&::after {
content: "";
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 1px;
height: 1.8rem;
background-color: rgba(255, 255, 255, .3);
}
}
.wgs {
display: flex;
align-items: center;
padding-right: .5rem;
&::before {
content: "";
display: block;
width: 2rem;
height: 2rem;
margin-right: .5rem;
background: url(../assets/images/ico_globe.svg) no-repeat center / 2rem;
}
}
.kst {
display: flex;
align-items: center;
justify-content: center;
width: 3.1rem;
height: 1.8rem;
margin-right: .5rem;
border-radius: .3rem;
background-color: var(--tertiary3);
color: var(--white);
font-size: var(--fs-s);
}
}
}
button {
width: 4rem;
height: 3.8rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
box-sizing: border-box;
background-color: var(--secondary3);
background-repeat: no-repeat;
background-position: center;
background-size: 3rem;
&:hover,
&.active {
background-color: rgba(var(--primary1-rgb), .8);
}
&.map {
background-image: url(../assets/images/ico_map.svg);
}
&.ship {
background-image: url(../assets/images/ico_ship.svg);
}
&.set {
width: 2rem;
height: 2rem;
background-color: transparent;
background-image: url(../assets/images/ico_set_s.svg);
background-size: 2rem;
&:hover,
&.active {
background-color: transparent;
}
}
}
}
/* top-search */
.schBox {
position: relative;
grid-column: 3;
justify-self: end;
width: 20rem;
height: 3.8rem;
.sch {
width: 100%;
height: 100%;
padding-right: 4.4rem;
border-radius: 2rem;
background-color: rgba(var(--secondary6-rgb), .9);
&::placeholder {
color: var(--white);
}
}
.mainSchBtn {
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
width: 4rem;
height: 3.8rem;
font-size: 0;
text-indent: -999999em;
border: none;
cursor: pointer;
background: url(../assets/images/ico_search_main.svg) no-repeat center left / 2.4rem;
}
}
}
/* tool-bar */
#tool {
.toolBar {
position: absolute;
top: 5.9rem;
right: .9rem;
width: 3rem;
height: 41rem;
}
.control {
position: absolute;
bottom: 1.8rem;
right: .9rem;
width: 3rem;
height: 20rem;
}
.toolItem {
width: 100%;
display: flex;
flex-direction: column;
&.space {
li {
margin-bottom: .5rem;
}
}
&.zoom {
li {
height: 3rem;
&.num {
background-color: var(--white);
color: var(--gray-scale2);
border-left: 1px solid rgba(var(--secondary6-rgb), .8);
border-right: 1px solid rgba(var(--secondary6-rgb), .8);
font-size: var(--fs-m);
}
button {
padding-bottom: 0;
}
}
}
li {
display: flex;
align-items: center;
justify-content: center;
width: 3rem;
height: 4.2rem;
background-color: rgba(var(--secondary6-rgb), .8);
&:hover,
&.active {
background-color: rgba(var(--primary1-rgb), .8);
}
button {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding-bottom: .2rem;
cursor: pointer;
text-align: center;
font-size: var(--fs-xxs);
color: var(--white);
background-repeat: no-repeat;
background-position: top left;
background-size: 3rem;
&::before {
content: "";
flex-shrink: 0;
width: 3rem;
height: 3rem;
}
&.tool01 { background-image: url(../assets/images/ico_tool01.svg); }
&.tool02 { background-image: url(../assets/images/ico_tool02.svg); }
&.tool03 { background-image: url(../assets/images/ico_tool03.svg); }
&.tool04 { background-image: url(../assets/images/ico_tool04.svg); }
&.tool05 { background-image: url(../assets/images/ico_tool05.svg); }
&.tool06 { background-image: url(../assets/images/ico_tool06.svg); }
&.tool07 { background-image: url(../assets/images/ico_tool07.svg); }
&.tool08 { background-image: url(../assets/images/ico_tool08.svg); }
&.zoomin { background-image: url(../assets/images/ico_plus.svg); }
&.zoomout { background-image: url(../assets/images/ico_minus.svg); }
&.legend { background-image: url(../assets/images/ico_legend.svg); }
&.minimap { background-image: url(../assets/images/ico_minimap.svg); }
}
}
}
}
}

파일 보기

@ -0,0 +1,188 @@
@charset "utf-8";
#wrap {
//* main */
#main {
width: 100%;
min-height: calc(100vh - 4.4rem);
position: relative;
overscroll-behavior: none;
overflow: hidden;
//* top-bar */
.topBar {
position: absolute;
top: 1.5rem;
left: 0;
right: 0;
height: 4.4rem;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 5.4rem;
box-sizing: border-box;
z-index: 95;
}
//* top-location */
.locationInfo {
flex: 0 0 auto;
min-width: 71rem;
height: 3.8rem;
margin: 0 auto;
display: flex;
align-items: center;
background-color: rgba(var(--secondary6-rgb), .9);
overflow: hidden;
ul {
display: flex;
align-items: center;
width: 100%;
height: 100%;
li {
display: flex;
align-items: center;
height: 100%;
padding: 0 1rem;
color: var(--white);
font-weight: var(--fw-bold);
font-size: 1.3rem;
&:first-child,
&:last-child {
padding: 0;
}
span + span {
padding-right: .5rem;
}
&.divider {
position: relative;
&::after {
content: "";
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 1px;
height: 1.8rem;
background-color: rgba(255, 255, 255, .3);
}
}
.wgs {
display: flex;
align-items: center;
padding-right: .5rem;
&::before {
content: "";
display: block;
width: 2rem;
height: 2rem;
margin-right: .5rem;
background: url(../assets/images/ico_globe.svg) no-repeat center / 2rem;
}
}
.kst {
display: flex;
align-items: center;
justify-content: center;
width: 3.1rem;
height: 1.8rem;
margin-right: .5rem;
border-radius: .3rem;
background-color: var(--tertiary3);
color: var(--white);
font-size: var(--fs-s);
}
}
}
button {
width: 4rem;
height: 3.8rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
box-sizing: border-box;
background-color: var(--secondary3);
background-repeat: no-repeat;
background-position: center;
background-size: 3rem;
&:hover,
&.active {
background-color: rgba(var(--primary1-rgb), .8);
}
&.map {
background-image: url(../assets/images/ico_map.svg);
}
&.ship {
background-image: url(../assets/images/ico_ship.svg);
}
&.set {
width: 2rem;
height: 2rem;
background-color: transparent;
background-image: url(../assets/images/ico_set_s.svg);
background-size: 2rem;
&:hover,
&.active {
background-color: transparent;
}
}
}
}
//* top-search */
.topSchBox {
flex: 0 0 auto;
width: 20rem;
height: 3.8rem;
display: flex;
justify-content: flex-end;
position: relative;
.tschInput {
width: 100%;
height: 100%;
padding-right: 4.4rem;
border-radius: 2rem;
background-color: rgba(var(--secondary6-rgb), .9);
border: 0;
&::placeholder {
color: var(--white);
}
}
.mainSchBtn {
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
width: 4rem;
height: 3.8rem;
font-size: 0;
text-indent: -999999em;
border: none;
cursor: pointer;
background: url(../assets/images/ico_search_main.svg) no-repeat center left / 2.4rem;
}
}
}
}

파일 보기

@ -0,0 +1,539 @@
@charset "utf-8";
#wrap {
#sidePanel {
/* gnb */
#nav {
position: fixed;
top: 4.4rem;
left: 0;
z-index: 100;
width: 4.3rem;
height: calc(100% - 4.4rem);
display: flex;
flex-direction: column;
justify-content: space-between;
background: var(--secondary1);
border-right: 1px solid var(--tertiary2);
.gnb {
width: 4.2rem;
display: flex;
flex-direction: column;
li {
width: 100%;
height: 4.4rem;
display: flex;
align-items: center;
justify-content: center;
button {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-repeat: no-repeat;
background-position: center;
background-size: 3rem;
/* 공통 상태 */
&:hover,
&:active,
&.active {
background-color: var(--primary1);
}
/* gnb icons */
&.gnb1 {
background-image: url(../assets/images/ico_gnb01.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb01_on.svg);
}
}
&.gnb2 {
background-image: url(../assets/images/ico_gnb02.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb02_on.svg);
}
}
&.gnb3 {
background-image: url(../assets/images/ico_gnb03.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb03_on.svg);
}
}
&.gnb4 {
background-image: url(../assets/images/ico_gnb04.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb04_on.svg);
}
}
&.gnb5 {
background-image: url(../assets/images/ico_gnb05.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb05_on.svg);
}
}
&.gnb6 {
background-image: url(../assets/images/ico_gnb06.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb06_on.svg);
}
}
&.gnb7 {
background-image: url(../assets/images/ico_gnb07.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb07_on.svg);
}
}
&.gnb8 {
background-image: url(../assets/images/ico_gnb08.svg);
&:hover,
&:active,
&.active {
background-image: url(../assets/images/ico_gnb08_on.svg);
}
}
}
}
}
.side {
width: 4.2rem;
display: flex;
flex-direction: column;
li {
width: 100%;
height: 4.4rem;
display: flex;
align-items: center;
justify-content: center;
button {
width: 3rem;
height: 3rem;
display: flex;
align-items: center;
justify-content: center;
background-repeat: no-repeat;
background-position: center;
background-size: 3rem;
background-color: var(--primary3);
&.filter {
background-image: url('../assets/images/ico_side01.svg');
}
&.layer {
background-image: url('../assets/images/ico_side02.svg');
}
}
}
}
}
/* side-pannel */
.slidePanel {
position: fixed;
top: 4.4rem;
left: 4.3rem;
width: 54rem;
height: calc(100% - 4.4rem);
min-height: 0;
display: flex;
flex-direction: column;
background-color: var(--secondary2);
border-right: .1rem solid var(--secondary3);
z-index: 99;
transform: translateX(0);
transition: transform .3s ease;
/* 닫힘 상태 */
&.is-closed {
transform: translateX(-100%);
.toogle::after {
transform: translate(-50%, -50%) rotate(180deg);
}
}
.tabBox {
flex-shrink: 0;
width: 100%;
padding: 1.5rem 2rem 0 2rem;
}
.tabWrap {
flex: 1;
min-height: 0;
display: none;
flex-direction: column;
&.is-active {
display: flex;
}
.tabWrapInner {
display: flex;
flex-direction: column;
flex: 1;
min-height: 100%;
.tabWrapCnt {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
padding-bottom: 3rem;
// 필터 스위치그룹
.switchGroup {
display: flex;
flex-direction: column;
width: 100%;
.sgHeader {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.2rem 1rem;
background-color: var(--secondary1);
border-bottom: .1rem solid var(--secondary3);
.colL {
display: flex;
align-items: center;
gap: 1rem;
font-weight: var(--fw-bold);
.favship {
width: 2rem;
height: 2rem;
background: url(../assets/images/ico_favship.svg) no-repeat center / contain;
}
}
.toggleBtn {
display: flex;
align-items: center;
width: 2.4rem;
height: 2.4rem;
background: url(../assets/images/ico_arrow_toggle_down.svg) no-repeat center / contain;
transition: transform 0.3s ease;
&.is-open {
transform: rotate(180deg);
}
}
}
.switchBox {
overflow: hidden;
max-height: 0;
transition: max-height 0.3s ease;
&.is-open {
max-height: 500rem;
}
.switchList {
display: flex;
flex-direction: column;
flex: 1 1 auto;
width: 100%;
background-color: var(--tertiary1);
padding: 1rem;
li {
display: flex;
justify-content: space-between;
padding: .1rem 0;
span {
flex: 1;
min-width: 0;
word-break: break-word;
overflow-wrap: break-word;
white-space: normal;
}
}
}
}
}
}
.btnBox {
flex: 0 0 auto;
margin-top: auto;
display: flex;
justify-content: flex-end;
padding: 2rem;
}
}
.tabTop {
flex-shrink: 0;
width: 100%;
padding: 0 2rem;
.title {
font-size: var(--fs-ml);
color: var(--white);
font-weight: var(--fw-bold);
padding: 1.7rem 0;
.prevBtn {
width: 2rem;
height: 2rem;
background: url(../assets/images/ico_tit_prev.svg) no-repeat center /contain;
margin-right: .5rem;
}
}
// 기상패널 범례
.legend {
display: flex;
align-items: flex-start;
flex-direction: column;
gap: .4rem;
margin-bottom: 2.5rem;
.legendTitle {
font-size: var(--fs-m);
}
.legendList {
width: 100%;
border-radius: .6rem;
padding: 1.5rem;
background-color: rgba(var(--secondary4-rgb), .2);
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
li {
display: flex;
align-items: center;
justify-content: flex-start;
font-size: var(--fs-ml);
img {
width: 2.6rem;
height: 2.6rem;
margin-right: .5rem;
}
}
}
}
}
.tabBtm {
flex: 1;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
border-top: 1px solid var(--secondary1);
padding: 3rem 2rem;
&.noLine {
overflow-y: visible;
border-top: 0;
padding: 0 2rem;
}
&.noSc {
overflow-y: visible;
padding-bottom: 0;
}
.tabBtmInner {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
.tabBtmCnt {
flex: 1 1 auto;
min-height: 0 ;
overflow-y: auto;
padding-bottom: 2rem;
}
.btnBox {
flex: 0 0 auto;
margin-top: auto;
display: flex;
justify-content: flex-end;
gap: 1rem;
padding: 2rem 0;
&.rowSB {
justify-content: space-between;
}
button {
flex: initial;
min-width: 12rem;
}
}
}
}
}
.toogle {
position: absolute;
overflow: hidden;
z-index: 10;
top: 50%;
left: 100%;
width: 2.4rem;
height: 5rem;
border-radius: 0 1rem 1rem 0;
transform: translateY(-50%);
background-color: var(--secondary2);
border: solid 1px var(--secondary3);
&::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 2.4rem;
height: 2.4rem;
transform: translate(-50%, -50%);
background: url(../assets/images/ico_toggle.svg) no-repeat center / 2.4rem;
transition: transform .3s ease;
}
}
// ai모드
.panelHeader {
display: flex;
align-items: center;
padding: 0 2rem;
.panelTitle {
padding: 1.7rem 0;
font-size: var(--fs-ml);
font-weight: var(--fw-bold);
}
}
.panelBody {
display: flex;
flex: 1;
min-height: 0;
padding: 0 2rem;
.ai {
display: grid;
grid-template-columns: repeat(2, 1fr);
align-items: start;
gap: 1rem;
row-gap: 1rem;
grid-auto-rows: min-content;
width: 100%;
li {
display: flex;
align-items: center;
justify-content: flex-start;
a {
position: relative;
display: flex;
flex-direction: column;
gap: .6rem;
width: 100%;
height: auto;
padding: 2rem 1.2rem;
background-color: var(--secondary1);
border: 2px solid transparent;
border-radius: .6rem;
&.on {
background-color: var(--secondary5);
border-color: var(--primary1);
}
&:not(.on) > * {
opacity: .5;
}
.title {
display: flex;
align-items: center;
font-weight: var(--fw-bold);
img {
width: 2.2rem;
height: 2.2rem;
margin-right: .5rem;
}
}
.keyword {
font-weight: var(--fw-bold);
}
.control {
position: absolute;
top: 2rem;
right: 1.5rem;
display: flex;
align-items: center;
i {
width: .8rem;
height: .8rem;
border-radius: 50%;
background-color: var(--white);
margin-right: .5rem;
}
}
}
}
}
}
.panelFooter {
border-top: 1px solid var(--secondary1);
padding: 1rem 2rem;
}
}
}
}

파일 보기

@ -0,0 +1,153 @@
@charset "utf-8";
#wrap {
//* tool-bar */
#tool {
.toolBar {
position: absolute;
top: 5.9rem;
right: .9rem;
width: 3rem;
height: 41rem;
z-index: 95;
}
.control {
position: absolute;
bottom: 1.8rem;
right: .9rem;
width: 3rem;
height: 20rem;
}
.toolItem {
width: 100%;
display: flex;
flex-direction: column;
&.space {
li {
margin-bottom: .5rem;
}
}
&.zoom {
li {
height: 3rem;
&.num {
background-color: var(--white);
color: var(--gray-scale1);
border-left: 1px solid rgba(var(--secondary6-rgb), .8);
border-right: 1px solid rgba(var(--secondary6-rgb), .8);
font-size: var(--fs-m);
}
button {
padding-bottom: 0;
}
}
}
li {
display: flex;
align-items: center;
justify-content: center;
width: 3rem;
height: 4.2rem;
background-color: rgba(var(--secondary6-rgb), .8);
button {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding-bottom: .2rem;
cursor: pointer;
text-align: center;
font-size: var(--fs-xxs);
color: var(--white);
background-repeat: no-repeat;
background-position: top left;
background-size: 3rem;
&:hover,
&.active {
background-color: rgba(var(--primary1-rgb), .8);
}
&::before {
content: "";
flex-shrink: 0;
width: 3rem;
height: 3rem;
}
&.tool01 { background-image: url(../assets/images/ico_tool01.svg); }
&.tool02 { background-image: url(../assets/images/ico_tool02.svg); }
&.tool03 { background-image: url(../assets/images/ico_tool03.svg); }
&.tool04 { background-image: url(../assets/images/ico_tool04.svg); }
&.tool05 { background-image: url(../assets/images/ico_tool05.svg); }
&.tool06 { background-image: url(../assets/images/ico_tool06.svg); }
&.tool07 { background-image: url(../assets/images/ico_tool07.svg); }
&.tool08 { background-image: url(../assets/images/ico_tool08.svg); }
&.zoomin { background-image: url(../assets/images/ico_plus.svg); }
&.zoomout { background-image: url(../assets/images/ico_minus.svg); }
&.legend { background-image: url(../assets/images/ico_legend.svg); }
&.minimap { background-image: url(../assets/images/ico_minimap.svg); }
}
}
}
// 범례
.legendWrap {
position: fixed;
right: 5rem;
bottom: 5rem;
.legendList {
display: flex;
flex-direction: column;
width: 14rem;
height: auto;
border-radius: .5rem;
background-color: var(--gray-scale3);
padding: .7rem;
gap: .4rem;
li {
display: flex;
justify-content: space-between;
align-items: center;
&.legendItem {
padding: .5rem 0;
border-bottom: 1px solid var(--gray-scale7);
}
.legendLabel {
display: flex;
align-items: center;
gap: .2rem;
font-size: var(--fs-s);
font-weight: var(--fw-bold);
img {
width: 1.6rem;
height: 1.6rem;
}
}
.legendValue {
font-size: var(--fs-s);
font-weight: var(--fw-heavy);
}
}
}
}
}
}

파일 보기

@ -0,0 +1,7 @@
@charset "utf-8";
#wrap {
width: 100%;
min-height: 100vh;
}