);
}
diff --git a/frontend/src/features/enforcement/EventList.tsx b/frontend/src/features/enforcement/EventList.tsx
index 26e47d5..e3c2b88 100644
--- a/frontend/src/features/enforcement/EventList.tsx
+++ b/frontend/src/features/enforcement/EventList.tsx
@@ -1,6 +1,9 @@
import { useState, useEffect, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Badge } from '@shared/components/ui/badge';
+import { Button } from '@shared/components/ui/button';
+import { Select } from '@shared/components/ui/select';
+import { PageContainer, PageHeader } from '@shared/components/layout';
import { DataTable, type DataColumn } from '@shared/components/common/DataTable';
import { FileUpload } from '@shared/components/common/FileUpload';
import {
@@ -135,45 +138,41 @@ export function EventList() {
const kpiTotal = (stats['TOTAL'] as number | undefined) ?? EVENTS.length;
return (
-
- {/* 헤더 */}
-
-
-
-
- {t('eventList.title')}
-
-
- {t('eventList.desc')}
-
-
-
- {/* 등급 필터 */}
-
-
-
-
-
-
-
+ 파일 업로드
+
+ >
+ }
+ />
{/* KPI 요약 */}
@@ -248,6 +247,6 @@ export function EventList() {
exportFilename="이벤트목록"
/>
)}
-
+
);
}
diff --git a/frontend/src/features/field-ops/AIAlert.tsx b/frontend/src/features/field-ops/AIAlert.tsx
index 66b5d42..ef78be7 100644
--- a/frontend/src/features/field-ops/AIAlert.tsx
+++ b/frontend/src/features/field-ops/AIAlert.tsx
@@ -1,6 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Badge } from '@shared/components/ui/badge';
+import { PageContainer, PageHeader } from '@shared/components/layout';
import { DataTable, type DataColumn } from '@shared/components/common/DataTable';
import { Send, Loader2, AlertTriangle } from 'lucide-react';
import { getAlerts, type PredictionAlert } from '@/services/event';
@@ -140,34 +141,37 @@ export function AIAlert() {
if (loading) {
return (
-
-
- 알림 데이터 로딩 중...
-
+
+
+
+ 알림 데이터 로딩 중...
+
+
);
}
if (error) {
return (
-
-
-
알림 조회 실패: {error}
-
-
+
+
+
+
알림 조회 실패: {error}
+
+
+
);
}
return (
-
-
-
-
- {t('aiAlert.title')}
-
-
{t('aiAlert.desc')}
-
+
+
{[
{ l: '총 발송', v: totalElements, c: 'text-heading' },
@@ -191,6 +195,6 @@ export function AIAlert() {
searchKeys={['channel', 'recipient']}
exportFilename="AI알림이력"
/>
-
+
);
}
diff --git a/frontend/src/features/field-ops/MobileService.tsx b/frontend/src/features/field-ops/MobileService.tsx
index 637edc2..135de7f 100644
--- a/frontend/src/features/field-ops/MobileService.tsx
+++ b/frontend/src/features/field-ops/MobileService.tsx
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useRef, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Card, CardContent } from '@shared/components/ui/card';
import { Badge } from '@shared/components/ui/badge';
+import { PageContainer, PageHeader } from '@shared/components/layout';
import { Smartphone, MapPin, Bell, Wifi, WifiOff, Shield, AlertTriangle, Navigation } from 'lucide-react';
import { BaseMap, createMarkerLayer, createPolylineLayer, useMapLayers, type MapHandle, type MarkerData } from '@lib/map';
import { useEventStore } from '@stores/eventStore';
@@ -52,11 +53,13 @@ export function MobileService() {
);
return (
-
-
-
{t('mobileService.title')}
-
{t('mobileService.desc')}
-
+
+
{/* 모바일 프리뷰 */}
@@ -143,6 +146,6 @@ export function MobileService() {
-
+
);
}
diff --git a/frontend/src/features/field-ops/ShipAgent.tsx b/frontend/src/features/field-ops/ShipAgent.tsx
index 2ea1a49..7f0f118 100644
--- a/frontend/src/features/field-ops/ShipAgent.tsx
+++ b/frontend/src/features/field-ops/ShipAgent.tsx
@@ -1,9 +1,9 @@
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
-import { Card, CardContent } from '@shared/components/ui/card';
import { Badge } from '@shared/components/ui/badge';
+import { PageContainer, PageHeader } from '@shared/components/layout';
import { DataTable, type DataColumn } from '@shared/components/common/DataTable';
-import { Monitor, Ship, Wifi, WifiOff, RefreshCw, MapPin, Clock, CheckCircle } from 'lucide-react';
+import { Monitor } from 'lucide-react';
import { getDeviceStatusIntent, getDeviceStatusLabel } from '@shared/constants/deviceStatuses';
import { useSettingsStore } from '@stores/settingsStore';
@@ -44,16 +44,14 @@ export function ShipAgent() {
], [tc, lang]);
return (
-
-
-
- {t('shipAgent.title')}
-
- ⚠데모 데이터 (백엔드 API 미구현)
-
-
-
{t('shipAgent.desc')}
-
+
+
{[{ l: '전체 Agent', v: DATA.length, c: 'text-heading' }, { l: '온라인', v: DATA.filter(d => d.status === '온라인').length, c: 'text-green-400' }, { l: '오프라인', v: DATA.filter(d => d.status === '오프라인').length, c: 'text-red-400' }, { l: '미배포', v: DATA.filter(d => d.status === '미배포').length, c: 'text-muted-foreground' }].map(k => (
@@ -62,6 +60,6 @@ export function ShipAgent() {
))}
-
+
);
}
diff --git a/frontend/src/features/patrol/FleetOptimization.tsx b/frontend/src/features/patrol/FleetOptimization.tsx
index 62810a1..b638423 100644
--- a/frontend/src/features/patrol/FleetOptimization.tsx
+++ b/frontend/src/features/patrol/FleetOptimization.tsx
@@ -3,6 +3,8 @@ import { useTranslation } from 'react-i18next';
import { BaseMap, STATIC_LAYERS, createMarkerLayer, createPolylineLayer, createZoneLayer, useMapLayers, type MapHandle } from '@lib/map';
import { Card, CardContent } from '@shared/components/ui/card';
import { Badge } from '@shared/components/ui/badge';
+import { Button } from '@shared/components/ui/button';
+import { PageContainer, PageHeader } from '@shared/components/layout';
import { Users, Ship, Target, BarChart3, Play, CheckCircle, AlertTriangle, Layers, RefreshCw } from 'lucide-react';
import { usePatrolStore } from '@stores/patrolStore';
@@ -96,24 +98,31 @@ export function FleetOptimization() {
useMapLayers(mapRef, buildLayers, [ships, COVERAGE, FLEET_ROUTES]);
return (
-
-
-
-
- {t('fleetOptimization.title')}
-
- ⚠
- 데모 데이터 (백엔드 API 미구현)
-
-
-
{t('fleetOptimization.desc')}
-
-
-
-
-
-
+
+
+
+
+ >
+ }
+ />
{/* KPI */}
@@ -218,6 +227,6 @@ export function FleetOptimization() {
-
+
);
}
diff --git a/frontend/src/features/patrol/PatrolRoute.tsx b/frontend/src/features/patrol/PatrolRoute.tsx
index f5f27cc..b77a5bd 100644
--- a/frontend/src/features/patrol/PatrolRoute.tsx
+++ b/frontend/src/features/patrol/PatrolRoute.tsx
@@ -4,6 +4,8 @@ import type maplibregl from 'maplibre-gl';
import { BaseMap, STATIC_LAYERS, createMarkerLayer, createPolylineLayer, useMapLayers, type MapHandle } from '@lib/map';
import { Card, CardContent } from '@shared/components/ui/card';
import { Badge } from '@shared/components/ui/badge';
+import { Button } from '@shared/components/ui/button';
+import { PageContainer, PageHeader } from '@shared/components/layout';
import { Navigation, Ship, MapPin, Clock, Wind, Anchor, Play, BarChart3, Target, Settings, CheckCircle, Share2 } from 'lucide-react';
import { usePatrolStore } from '@stores/patrolStore';
@@ -93,23 +95,24 @@ export function PatrolRoute() {
if (!currentShip || !route) return null;
return (
-
-
-
-
- {t('patrolRoute.title')}
-
- ⚠
- 데모 데이터 (백엔드 API 미구현)
-
-
-
{t('patrolRoute.desc')}
-
-
-
+
+
+ }>
+ 경로 생성
+
+ }>
+ 공유
+
+ >
+ }
+ />
{/* 함정 선택 */}
@@ -215,6 +218,6 @@ export function PatrolRoute() {
-
+
);
}
diff --git a/frontend/src/features/risk-assessment/EnforcementPlan.tsx b/frontend/src/features/risk-assessment/EnforcementPlan.tsx
index 5c37d28..8b39e5f 100644
--- a/frontend/src/features/risk-assessment/EnforcementPlan.tsx
+++ b/frontend/src/features/risk-assessment/EnforcementPlan.tsx
@@ -2,6 +2,8 @@ import { useState, useEffect, useRef, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Card, CardContent } from '@shared/components/ui/card';
import { Badge } from '@shared/components/ui/badge';
+import { Button } from '@shared/components/ui/button';
+import { PageContainer, PageHeader } from '@shared/components/layout';
import { DataTable, type DataColumn } from '@shared/components/common/DataTable';
import { Shield, AlertTriangle, Ship, Plus, Calendar, Users } from 'lucide-react';
import { BaseMap, STATIC_LAYERS, createMarkerLayer, createRadiusLayer, useMapLayers, type MapHandle } from '@lib/map';
@@ -118,14 +120,18 @@ export function EnforcementPlan() {
const totalCrew = PLANS.reduce((sum, p) => sum + p.crew, 0);
return (
-
-
-
-
{t('enforcementPlan.title')}
-
{t('enforcementPlan.desc')}
-
-
-
+
+ }>
+ 단속 계획 수립
+
+ }
+ />
{/* 로딩/에러 상태 */}
{loading && (
@@ -185,6 +191,6 @@ export function EnforcementPlan() {
-
+
);
}