From 5db21095f34361d783caf21fbc41a2763d224bb1 Mon Sep 17 00:00:00 2001 From: hskwon Date: Tue, 2 Dec 2025 21:41:15 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[=EB=A9=94=EB=89=B4]=20=EA=B8=80?= =?UTF-8?q?=EB=A1=9C=EB=B2=8C=20=EB=A9=94=EB=89=B4=20=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(=EC=8A=88=ED=8D=BC?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=EC=A0=84=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 글로벌 메뉴 CRUD API 및 라우트 추가 (GlobalMenuController) - 글로벌 메뉴 목록/생성/수정 뷰 추가 (보라색 테마) - MenuService에 글로벌 메뉴 관련 메서드 11개 추가 - 메뉴 관리 페이지에 '글로벌 메뉴 관리' 버튼 추가 - 가져오기 모드에서 이미 가져온 메뉴 비활성화 표시 - super.admin 미들웨어로 접근 제어 --- .../Api/Admin/GlobalMenuController.php | 296 ++++++++++++++++++ .../Controllers/Api/Admin/MenuController.php | 10 +- app/Http/Controllers/MenuController.php | 47 +++ app/Services/MenuService.php | 227 +++++++++++++- resources/views/menus/global-create.blade.php | 174 ++++++++++ resources/views/menus/global-edit.blade.php | 187 +++++++++++ resources/views/menus/global-index.blade.php | 252 +++++++++++++++ resources/views/menus/index.blade.php | 80 ++++- .../menus/partials/global-table.blade.php | 143 +++++++++ .../views/menus/partials/table.blade.php | 52 ++- routes/api.php | 24 ++ routes/web.php | 5 + 12 files changed, 1458 insertions(+), 39 deletions(-) create mode 100644 app/Http/Controllers/Api/Admin/GlobalMenuController.php create mode 100644 resources/views/menus/global-create.blade.php create mode 100644 resources/views/menus/global-edit.blade.php create mode 100644 resources/views/menus/global-index.blade.php create mode 100644 resources/views/menus/partials/global-table.blade.php diff --git a/app/Http/Controllers/Api/Admin/GlobalMenuController.php b/app/Http/Controllers/Api/Admin/GlobalMenuController.php new file mode 100644 index 00000000..f23da385 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/GlobalMenuController.php @@ -0,0 +1,296 @@ +menuService->getGlobalMenus( + $request->all(), + $request->integer('per_page', 100) // 글로벌 메뉴는 많지 않으므로 페이지당 100개 + ); + + // HTMX 요청인 경우 HTML 반환 + if ($request->header('HX-Request')) { + $html = view('menus.partials.global-table', compact('menus'))->render(); + + return response()->json(['html' => $html]); + } + + // 일반 API 요청인 경우 JSON 반환 + return response()->json([ + 'success' => true, + 'data' => $menus->items(), + 'meta' => [ + 'current_page' => $menus->currentPage(), + 'last_page' => $menus->lastPage(), + 'per_page' => $menus->perPage(), + 'total' => $menus->total(), + ], + ]); + } + + /** + * 글로벌 메뉴 상세 조회 + */ + public function show(int $id): JsonResponse + { + $menu = $this->menuService->getGlobalMenuById($id); + + if (! $menu) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴를 찾을 수 없습니다.', + ], 404); + } + + return response()->json([ + 'success' => true, + 'data' => $menu, + ]); + } + + /** + * 글로벌 메뉴 생성 + */ + public function store(StoreMenuRequest $request): JsonResponse + { + try { + $menu = $this->menuService->createGlobalMenu($request->validated()); + + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴가 생성되었습니다.', + 'data' => $menu, + 'redirect' => route('menus.global.index'), + ], 201); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴 생성에 실패했습니다: '.$e->getMessage(), + ], 500); + } + } + + /** + * 글로벌 메뉴 수정 + */ + public function update(UpdateMenuRequest $request, int $id): JsonResponse + { + try { + $result = $this->menuService->updateGlobalMenu($id, $request->validated()); + + if (! $result) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴를 찾을 수 없거나 수정할 수 없습니다.', + ], 404); + } + + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴가 수정되었습니다.', + 'redirect' => route('menus.global.index'), + ]); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴 수정에 실패했습니다: '.$e->getMessage(), + ], 500); + } + } + + /** + * 글로벌 메뉴 삭제 + */ + public function destroy(int $id): JsonResponse + { + try { + $result = $this->menuService->deleteGlobalMenu($id); + + if (! $result) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴를 찾을 수 없거나 자식 메뉴가 있어 삭제할 수 없습니다.', + ], 404); + } + + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴가 삭제되었습니다.', + ]); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴 삭제에 실패했습니다: '.$e->getMessage(), + ], 500); + } + } + + /** + * 글로벌 메뉴 복원 + */ + public function restore(Request $request, int $id): JsonResponse + { + $this->menuService->restoreGlobalMenu($id); + + if ($request->header('HX-Request')) { + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴가 복원되었습니다.', + 'action' => 'refresh', + ]); + } + + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴가 복원되었습니다.', + ]); + } + + /** + * 글로벌 메뉴 영구 삭제 + */ + public function forceDestroy(Request $request, int $id): JsonResponse + { + try { + $result = $this->menuService->forceDeleteGlobalMenu($id); + + if (! $result) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴를 찾을 수 없거나 자식 메뉴가 있어 영구 삭제할 수 없습니다.', + ], 404); + } + + if ($request->header('HX-Request')) { + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴가 영구 삭제되었습니다.', + 'action' => 'refresh', + ]); + } + + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴가 영구 삭제되었습니다.', + ]); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴 영구 삭제에 실패했습니다: '.$e->getMessage(), + ], 500); + } + } + + /** + * 글로벌 메뉴 활성 상태 토글 + */ + public function toggleActive(Request $request, int $id): JsonResponse + { + try { + $result = $this->menuService->toggleGlobalActive($id); + + if (! $result) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴를 찾을 수 없습니다.', + ], 404); + } + + if ($request->header('HX-Request')) { + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴 활성 상태가 변경되었습니다.', + 'action' => 'refresh', + ]); + } + + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴 활성 상태가 변경되었습니다.', + ]); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴 활성 상태 변경에 실패했습니다: '.$e->getMessage(), + ], 500); + } + } + + /** + * 글로벌 메뉴 숨김 상태 토글 + */ + public function toggleHidden(Request $request, int $id): JsonResponse + { + try { + $result = $this->menuService->toggleGlobalHidden($id); + + if (! $result) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴를 찾을 수 없습니다.', + ], 404); + } + + if ($request->header('HX-Request')) { + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴 숨김 상태가 변경되었습니다.', + 'action' => 'refresh', + ]); + } + + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴 숨김 상태가 변경되었습니다.', + ]); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴 숨김 상태 변경에 실패했습니다: '.$e->getMessage(), + ], 500); + } + } + + /** + * 글로벌 메뉴 순서 변경 + */ + public function reorder(Request $request): JsonResponse + { + $validated = $request->validate([ + 'items' => 'required|array', + 'items.*.id' => 'required|integer', + 'items.*.sort_order' => 'required|integer', + ]); + + try { + $this->menuService->reorderGlobalMenus($validated['items']); + + return response()->json([ + 'success' => true, + 'message' => '글로벌 메뉴 순서가 변경되었습니다.', + ]); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => '글로벌 메뉴 순서 변경에 실패했습니다: '.$e->getMessage(), + ], 500); + } + } +} diff --git a/app/Http/Controllers/Api/Admin/MenuController.php b/app/Http/Controllers/Api/Admin/MenuController.php index a1f5d474..38aa2f46 100644 --- a/app/Http/Controllers/Api/Admin/MenuController.php +++ b/app/Http/Controllers/Api/Admin/MenuController.php @@ -24,8 +24,8 @@ public function index(Request $request): JsonResponse $importMode = $request->get('mode') === 'import' && $tenantId; if ($importMode) { - // 가져오기 모드: 복사 가능한 글로벌 메뉴 목록 - $menus = $this->menuService->getAvailableGlobalMenus($tenantId); + // 가져오기 모드: 전체 글로벌 메뉴 (가져오기 상태 포함) + $menus = $this->menuService->getAllGlobalMenusWithStatus($tenantId); } else { // 일반 모드: 현재 범위의 메뉴 목록 $menus = $this->menuService->getMenus( @@ -369,8 +369,7 @@ public function move(Request $request): JsonResponse } /** - * 복사 가능한 글로벌 메뉴 목록 조회 - * (현재 테넌트에 존재하지 않는 글로벌 메뉴만) + * 글로벌 메뉴 목록 조회 (가져오기 상태 포함) */ public function availableGlobal(Request $request): JsonResponse { @@ -385,7 +384,7 @@ public function availableGlobal(Request $request): JsonResponse } try { - $menus = $this->menuService->getAvailableGlobalMenus($tenantId); + $menus = $this->menuService->getAllGlobalMenusWithStatus($tenantId); return response()->json([ 'success' => true, @@ -396,6 +395,7 @@ public function availableGlobal(Request $request): JsonResponse 'url' => $menu->url, 'icon' => $menu->icon, 'depth' => $menu->depth ?? 0, + 'is_imported' => $menu->is_imported ?? false, ]; })->values(), ]); diff --git a/app/Http/Controllers/MenuController.php b/app/Http/Controllers/MenuController.php index 52fbb77d..bff3a976 100644 --- a/app/Http/Controllers/MenuController.php +++ b/app/Http/Controllers/MenuController.php @@ -20,6 +20,53 @@ public function index(Request $request): View return view('menus.index'); } + /** + * 글로벌 메뉴 관리 페이지 (슈퍼관리자 전용) + */ + public function globalIndex(): View + { + // 슈퍼관리자 체크 + if (! auth()->user()?->is_super_admin) { + abort(403, '접근 권한이 없습니다.'); + } + + return view('menus.global-index'); + } + + /** + * 글로벌 메뉴 생성 페이지 (슈퍼관리자 전용) + */ + public function globalCreate(): View + { + if (! auth()->user()?->is_super_admin) { + abort(403, '접근 권한이 없습니다.'); + } + + $parentMenus = $this->menuService->getGlobalParentMenus(); + + return view('menus.global-create', compact('parentMenus')); + } + + /** + * 글로벌 메뉴 수정 페이지 (슈퍼관리자 전용) + */ + public function globalEdit(int $id): View + { + if (! auth()->user()?->is_super_admin) { + abort(403, '접근 권한이 없습니다.'); + } + + $menu = $this->menuService->getGlobalMenuById($id); + + if (! $menu) { + abort(404, '글로벌 메뉴를 찾을 수 없습니다.'); + } + + $parentMenus = $this->menuService->getGlobalParentMenus(); + + return view('menus.global-edit', compact('menu', 'parentMenus')); + } + /** * 메뉴 생성 페이지 */ diff --git a/app/Services/MenuService.php b/app/Services/MenuService.php index 6a2ff56c..7f8d2aa3 100644 --- a/app/Services/MenuService.php +++ b/app/Services/MenuService.php @@ -445,10 +445,223 @@ private function compactSiblings(?int $parentId): void } /** - * 복사 가능한 글로벌 메뉴 목록 조회 - * (현재 테넌트에 존재하지 않는 글로벌 메뉴만) + * 글로벌 부모 메뉴 목록 조회 (드롭다운용) */ - public function getAvailableGlobalMenus(int $tenantId): Collection + public function getGlobalParentMenus(): Collection + { + return GlobalMenu::query() + ->where('is_active', true) + ->orderBy('sort_order') + ->orderBy('name') + ->get(); + } + + /** + * 글로벌 메뉴 상세 조회 + */ + public function getGlobalMenuById(int $id): ?GlobalMenu + { + return GlobalMenu::with(['parent', 'children'])->find($id); + } + + /** + * 글로벌 메뉴 목록 조회 (페이지네이션) - 트리 구조로 정렬 + */ + public function getGlobalMenus(array $filters = [], int $perPage = 15): LengthAwarePaginator + { + $query = GlobalMenu::query()->withTrashed(); + + // Soft Delete 필터 + if (isset($filters['trashed'])) { + if ($filters['trashed'] === 'only') { + $query->onlyTrashed(); + } elseif ($filters['trashed'] === 'with') { + $query->withTrashed(); + } + } + + // 검색 필터 + if (! empty($filters['search'])) { + $search = $filters['search']; + $query->where(function ($q) use ($search) { + $q->where('name', 'like', "%{$search}%") + ->orWhere('url', 'like', "%{$search}%"); + }); + } + + // 활성 상태 필터 + if (isset($filters['is_active'])) { + $query->where('is_active', $filters['is_active']); + } + + // 모든 메뉴 가져오기 (트리 구조 정렬을 위해) + $allMenus = $query->with(['parent'])->orderBy('sort_order')->orderBy('id')->get(); + + // 트리 구조로 정렬 후 플랫한 배열로 변환 + $flattenedMenus = $this->flattenMenuTree($allMenus); + + // 수동 페이지네이션 + $currentPage = request()->input('page', 1); + $offset = ($currentPage - 1) * $perPage; + $items = $flattenedMenus->slice($offset, $perPage)->values(); + + return new \Illuminate\Pagination\LengthAwarePaginator( + $items, + $flattenedMenus->count(), + $perPage, + $currentPage, + ['path' => request()->url(), 'query' => request()->query()] + ); + } + + /** + * 글로벌 메뉴 생성 + */ + public function createGlobalMenu(array $data): GlobalMenu + { + // is_active 처리 + $data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1'; + + // hidden 처리 + $data['hidden'] = isset($data['hidden']) && $data['hidden'] == '1'; + + // is_external 처리 + $data['is_external'] = isset($data['is_external']) && $data['is_external'] == '1'; + + // parent_id null 처리 + if (empty($data['parent_id'])) { + $data['parent_id'] = null; + } + + return GlobalMenu::create($data); + } + + /** + * 글로벌 메뉴 수정 + */ + public function updateGlobalMenu(int $id, array $data): bool + { + $menu = $this->getGlobalMenuById($id); + if (! $menu) { + return false; + } + + // is_active 처리 + $data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1'; + + // hidden 처리 + $data['hidden'] = isset($data['hidden']) && $data['hidden'] == '1'; + + // is_external 처리 + $data['is_external'] = isset($data['is_external']) && $data['is_external'] == '1'; + + // parent_id null 처리 + if (empty($data['parent_id'])) { + $data['parent_id'] = null; + } + + // 자기 자신을 부모로 설정하는 것 방지 + if (isset($data['parent_id']) && $data['parent_id'] == $id) { + return false; + } + + return $menu->update($data); + } + + /** + * 글로벌 메뉴 삭제 (Soft Delete) + */ + public function deleteGlobalMenu(int $id): bool + { + $menu = $this->getGlobalMenuById($id); + if (! $menu) { + return false; + } + + // 자식 메뉴가 있는 경우 삭제 불가 + if ($menu->children()->count() > 0) { + return false; + } + + return $menu->delete(); + } + + /** + * 글로벌 메뉴 복원 + */ + public function restoreGlobalMenu(int $id): bool + { + $menu = GlobalMenu::onlyTrashed()->findOrFail($id); + + return $menu->restore(); + } + + /** + * 글로벌 메뉴 영구 삭제 + */ + public function forceDeleteGlobalMenu(int $id): bool + { + $menu = GlobalMenu::withTrashed()->findOrFail($id); + + // 자식 메뉴가 있는 경우 영구 삭제 불가 + if ($menu->children()->withTrashed()->count() > 0) { + return false; + } + + return $menu->forceDelete(); + } + + /** + * 글로벌 메뉴 활성 상태 토글 + */ + public function toggleGlobalActive(int $id): bool + { + $menu = $this->getGlobalMenuById($id); + if (! $menu) { + return false; + } + + $menu->is_active = ! $menu->is_active; + + return $menu->save(); + } + + /** + * 글로벌 메뉴 숨김 상태 토글 + */ + public function toggleGlobalHidden(int $id): bool + { + $menu = $this->getGlobalMenuById($id); + if (! $menu) { + return false; + } + + $menu->hidden = ! $menu->hidden; + + return $menu->save(); + } + + /** + * 글로벌 메뉴 순서 변경 + */ + public function reorderGlobalMenus(array $items): bool + { + return \DB::transaction(function () use ($items) { + foreach ($items as $item) { + GlobalMenu::where('id', $item['id']) + ->update(['sort_order' => $item['sort_order']]); + } + + return true; + }); + } + + /** + * 글로벌 메뉴 목록 조회 (가져오기 상태 포함) + * - 모든 글로벌 메뉴 반환 + * - 이미 가져온 메뉴는 is_imported=true로 표시 + */ + public function getAllGlobalMenusWithStatus(int $tenantId): Collection { // 글로벌 메뉴 전체 조회 (global_menus 테이블에서) $globalMenus = GlobalMenu::query() @@ -463,13 +676,13 @@ public function getAvailableGlobalMenus(int $tenantId): Collection ->pluck('global_menu_id') ->toArray(); - // 현재 테넌트에 없는 글로벌 메뉴만 필터링 - $availableMenus = $globalMenus->filter(function ($menu) use ($existingGlobalIds) { - return ! in_array($menu->id, $existingGlobalIds); + // 각 메뉴에 is_imported 속성 추가 + $globalMenus->each(function ($menu) use ($existingGlobalIds) { + $menu->is_imported = in_array($menu->id, $existingGlobalIds); }); // 트리 구조로 정렬 (depth 정보 포함) - return $this->flattenMenuTree($availableMenus); + return $this->flattenMenuTree($globalMenus); } /** diff --git a/resources/views/menus/global-create.blade.php b/resources/views/menus/global-create.blade.php new file mode 100644 index 00000000..64005f9e --- /dev/null +++ b/resources/views/menus/global-create.blade.php @@ -0,0 +1,174 @@ +@extends('layouts.app') + +@section('title', '글로벌 메뉴 생성') + +@section('content') +
+ +
+
+

글로벌 메뉴 생성

+

시스템 전체에서 사용되는 기본 메뉴를 생성합니다.

+
+ + ← 목록으로 + +
+ + +
+ +
+
+@endsection + +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/menus/global-edit.blade.php b/resources/views/menus/global-edit.blade.php new file mode 100644 index 00000000..340b0e81 --- /dev/null +++ b/resources/views/menus/global-edit.blade.php @@ -0,0 +1,187 @@ +@extends('layouts.app') + +@section('title', '글로벌 메뉴 수정') + +@section('content') +
+ +
+
+

글로벌 메뉴 수정

+

시스템 전체에서 사용되는 기본 메뉴를 수정합니다.

+
+ + ← 목록으로 + +
+ + +
+ +
+
+@endsection + +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/menus/global-index.blade.php b/resources/views/menus/global-index.blade.php new file mode 100644 index 00000000..47ce8119 --- /dev/null +++ b/resources/views/menus/global-index.blade.php @@ -0,0 +1,252 @@ +@extends('layouts.app') + +@section('title', '글로벌 메뉴 관리') + +@section('content') + +
+
+

글로벌 메뉴 관리

+

+ 시스템 전체에서 사용되는 기본 메뉴를 관리합니다. 테넌트는 이 메뉴를 복사하여 사용합니다. +

+
+ +
+ + +
+
+ +
+ +
+ + +
+ +
+ + + +
+
+ + + +@endsection + +@push('styles') + +@endpush + +@push('scripts') + + + + +@endpush \ No newline at end of file diff --git a/resources/views/menus/index.blade.php b/resources/views/menus/index.blade.php index 85cc9787..2953e0a8 100644 --- a/resources/views/menus/index.blade.php +++ b/resources/views/menus/index.blade.php @@ -40,6 +40,14 @@ class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transitio + 새 메뉴 + @if(auth()->user()?->is_super_admin) + + + + + 글로벌 메뉴 관리 + + @endif @@ -57,8 +65,8 @@ class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transitio class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"> - -
+ +
+ + + +
+ @if($menu->global_menu_id) + + 글로벌 + + @endif + + 수정 + + +
@endif diff --git a/routes/api.php b/routes/api.php index 6a062083..a0ff469f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -3,6 +3,7 @@ use App\Http\Controllers\Api\Admin\BoardController; use App\Http\Controllers\Api\Admin\DailyLogController; use App\Http\Controllers\Api\Admin\DepartmentController; +use App\Http\Controllers\Api\Admin\GlobalMenuController; use App\Http\Controllers\Api\Admin\MenuController; use App\Http\Controllers\Api\Admin\PermissionController; use App\Http\Controllers\Api\Admin\ProjectManagement\ImportController as PmImportController; @@ -135,6 +136,29 @@ Route::post('/{id}/toggle-hidden', [MenuController::class, 'toggleHidden'])->name('toggleHidden'); }); + // 글로벌 메뉴 관리 API (슈퍼관리자 전용) + Route::middleware('super.admin')->prefix('global-menus')->name('global-menus.')->group(function () { + // 고정 경로 + Route::post('/reorder', [GlobalMenuController::class, 'reorder'])->name('reorder'); + + // 기본 CRUD + Route::get('/', [GlobalMenuController::class, 'index'])->name('index'); + Route::post('/', [GlobalMenuController::class, 'store'])->name('store'); + Route::get('/{id}', [GlobalMenuController::class, 'show'])->name('show'); + Route::put('/{id}', [GlobalMenuController::class, 'update'])->name('update'); + Route::delete('/{id}', [GlobalMenuController::class, 'destroy'])->name('destroy'); + + // 복원 + Route::post('/{id}/restore', [GlobalMenuController::class, 'restore'])->name('restore'); + + // 영구삭제 + Route::delete('/{id}/force', [GlobalMenuController::class, 'forceDestroy'])->name('forceDestroy'); + + // 추가 액션 + Route::post('/{id}/toggle-active', [GlobalMenuController::class, 'toggleActive'])->name('toggleActive'); + Route::post('/{id}/toggle-hidden', [GlobalMenuController::class, 'toggleHidden'])->name('toggleHidden'); + }); + // 권한 관리 API Route::prefix('permissions')->name('permissions.')->group(function () { Route::get('/', [PermissionController::class, 'index'])->name('index'); diff --git a/routes/web.php b/routes/web.php index eeab4277..7b71bb14 100644 --- a/routes/web.php +++ b/routes/web.php @@ -82,6 +82,11 @@ Route::get('/', [MenuController::class, 'index'])->name('index'); Route::get('/create', [MenuController::class, 'create'])->name('create'); Route::get('/{id}/edit', [MenuController::class, 'edit'])->name('edit'); + + // 글로벌 메뉴 관리 (슈퍼관리자 전용) + Route::get('/global', [MenuController::class, 'globalIndex'])->name('global.index'); + Route::get('/global/create', [MenuController::class, 'globalCreate'])->name('global.create'); + Route::get('/global/{id}/edit', [MenuController::class, 'globalEdit'])->name('global.edit'); }); // 권한 관리 (Blade 화면만)