From c3d3b82b602e8dcd138afc92cf6811728663390d Mon Sep 17 00:00:00 2001 From: Nan Kyung Lee Date: Thu, 5 Mar 2026 13:27:53 +0900 Subject: [PATCH] =?UTF-8?q?feat(scat):=20=ED=95=B4=EC=95=88=ED=8F=89?= =?UTF-8?q?=EA=B0=80=20=EC=84=9C=EB=B8=8C=EB=A9=94=EB=89=B4=203=EA=B0=9C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(=ED=95=B4=EC=95=88=EC=98=A4=EC=97=BC?= =?UTF-8?q?=20=EC=A1=B0=EC=82=AC=20=ED=8F=89=EA=B0=80,=20=ED=95=B4?= =?UTF-8?q?=EC=96=91=EC=98=A4=EC=97=BC=EB=B6=84=ED=8F=AC=EB=8F=84,=20Pre-S?= =?UTF-8?q?CAT)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useSubMenu에 scat 서브메뉴 설정 추가 (survey, distribution, pre-scat) - ScatView wrapper 컴포넌트로 서브탭 분기 처리 - SurveyView, DistributionView placeholder 컴포넌트 생성 - hasPermission에 부모 리소스 fallback 로직 추가 (scat:survey → scat) - App.tsx에서 PreScatView → ScatView 교체 Co-Authored-By: Claude Opus 4.6 --- frontend/src/App.tsx | 4 +-- frontend/src/common/hooks/useSubMenu.ts | 8 ++++-- frontend/src/common/store/authStore.ts | 13 +++++++-- .../tabs/scat/components/DistributionView.tsx | 13 +++++++++ .../src/tabs/scat/components/ScatView.tsx | 27 +++++++++++++++++++ .../src/tabs/scat/components/SurveyView.tsx | 13 +++++++++ frontend/src/tabs/scat/index.ts | 1 + 7 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 frontend/src/tabs/scat/components/DistributionView.tsx create mode 100644 frontend/src/tabs/scat/components/ScatView.tsx create mode 100644 frontend/src/tabs/scat/components/SurveyView.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 66dce36..1f148c4 100755 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -16,7 +16,7 @@ import { BoardView } from '@tabs/board' import { WeatherView } from '@tabs/weather' import { IncidentsView } from '@tabs/incidents' import { AdminView } from '@tabs/admin' -import { PreScatView } from '@tabs/scat' +import { ScatView } from '@tabs/scat' import { RescueView } from '@tabs/rescue' const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID || '' @@ -92,7 +92,7 @@ function App() { case 'incidents': return case 'scat': - return + return case 'admin': return case 'rescue': diff --git a/frontend/src/common/hooks/useSubMenu.ts b/frontend/src/common/hooks/useSubMenu.ts index 48177f5..f053d85 100755 --- a/frontend/src/common/hooks/useSubMenu.ts +++ b/frontend/src/common/hooks/useSubMenu.ts @@ -46,7 +46,11 @@ const subMenuConfigs: Record = { { id: 'theory', label: '항공탐색 이론', icon: '📐' } ], assets: null, - scat: null, + scat: [ + { id: 'survey', label: '해안오염 조사 평가', icon: '📋' }, + { id: 'distribution', label: '해양오염분포도', icon: '🗺' }, + { id: 'pre-scat', label: 'Pre-SCAT', icon: '🔍' } + ], incidents: null, board: [ { id: 'all', label: '전체', icon: '📋' }, @@ -72,7 +76,7 @@ const subMenuState: Record = { reports: 'report-list', aerial: 'media', assets: '', - scat: '', + scat: 'survey', incidents: '', board: 'all', weather: '', diff --git a/frontend/src/common/store/authStore.ts b/frontend/src/common/store/authStore.ts index f47a474..5d091f3 100644 --- a/frontend/src/common/store/authStore.ts +++ b/frontend/src/common/store/authStore.ts @@ -73,9 +73,18 @@ export const useAuthStore = create((set, get) => ({ hasPermission: (resource: string, operation?: string) => { const { user } = get() if (!user) return false + const op = operation ?? 'READ' + // 정확한 리소스 권한 확인 const ops = user.permissions[resource] - if (!ops) return false - return ops.includes(operation ?? 'READ') + if (ops) return ops.includes(op) + // 'scat:survey' → 부모 'scat' 권한으로 fallback + const colonIdx = resource.indexOf(':') + if (colonIdx > 0) { + const parent = resource.substring(0, colonIdx) + const parentOps = user.permissions[parent] + if (parentOps) return parentOps.includes(op) + } + return false }, clearError: () => set({ error: null, pendingMessage: null }), diff --git a/frontend/src/tabs/scat/components/DistributionView.tsx b/frontend/src/tabs/scat/components/DistributionView.tsx new file mode 100644 index 0000000..1c5cee8 --- /dev/null +++ b/frontend/src/tabs/scat/components/DistributionView.tsx @@ -0,0 +1,13 @@ +function DistributionView() { + return ( +
+
+
🗺
+
해양오염분포도
+
해양오염 분포도 기능이 준비 중입니다.
+
+
+ ); +} + +export default DistributionView; diff --git a/frontend/src/tabs/scat/components/ScatView.tsx b/frontend/src/tabs/scat/components/ScatView.tsx new file mode 100644 index 0000000..ba88351 --- /dev/null +++ b/frontend/src/tabs/scat/components/ScatView.tsx @@ -0,0 +1,27 @@ +import { useSubMenu } from '@common/hooks/useSubMenu'; +import { PreScatView } from './PreScatView'; +import SurveyView from './SurveyView'; +import DistributionView from './DistributionView'; + +export function ScatView() { + const { activeSubTab } = useSubMenu('scat'); + + const renderContent = () => { + switch (activeSubTab) { + case 'survey': + return ; + case 'distribution': + return ; + case 'pre-scat': + return ; + default: + return ; + } + }; + + return ( +
+
{renderContent()}
+
+ ); +} diff --git a/frontend/src/tabs/scat/components/SurveyView.tsx b/frontend/src/tabs/scat/components/SurveyView.tsx new file mode 100644 index 0000000..a749e10 --- /dev/null +++ b/frontend/src/tabs/scat/components/SurveyView.tsx @@ -0,0 +1,13 @@ +function SurveyView() { + return ( +
+
+
📋
+
해안오염 조사 평가
+
해안오염 조사 및 평가 기능이 준비 중입니다.
+
+
+ ); +} + +export default SurveyView; diff --git a/frontend/src/tabs/scat/index.ts b/frontend/src/tabs/scat/index.ts index b238189..6e461f7 100644 --- a/frontend/src/tabs/scat/index.ts +++ b/frontend/src/tabs/scat/index.ts @@ -1 +1,2 @@ export { PreScatView } from './components/PreScatView' +export { ScatView } from './components/ScatView'