prefix('menus')->group(function () { Route::get('/', [MenuController::class, 'index'])->name('v1.menus.index'); Route::post('/', [MenuController::class, 'store'])->name('v1.menus.store'); Route::post('/reorder', [MenuController::class, 'reorder'])->name('v1.menus.reorder'); // 동기화 관련 라우트 (/{id} 전에 위치해야 함) Route::get('/trashed', [MenuController::class, 'trashed'])->name('v1.menus.trashed'); Route::get('/available-global', [MenuController::class, 'availableGlobal'])->name('v1.menus.available-global'); Route::get('/sync-status', [MenuController::class, 'syncStatus'])->name('v1.menus.sync-status'); Route::post('/sync', [MenuController::class, 'sync'])->name('v1.menus.sync'); Route::post('/sync-new', [MenuController::class, 'syncNew'])->name('v1.menus.sync-new'); Route::post('/sync-updates', [MenuController::class, 'syncUpdates'])->name('v1.menus.sync-updates'); // 단일 메뉴 관련 라우트 Route::get('/{id}', [MenuController::class, 'show'])->name('v1.menus.show'); Route::patch('/{id}', [MenuController::class, 'update'])->name('v1.menus.update'); Route::delete('/{id}', [MenuController::class, 'destroy'])->name('v1.menus.destroy'); Route::post('/{id}/toggle', [MenuController::class, 'toggle'])->name('v1.menus.toggle'); Route::post('/{id}/restore', [MenuController::class, 'restore'])->name('v1.menus.restore'); }); // Role API Route::prefix('roles')->group(function () { Route::get('/', [RoleController::class, 'index'])->name('v1.roles.index'); Route::post('/', [RoleController::class, 'store'])->name('v1.roles.store'); Route::get('/stats', [RoleController::class, 'stats'])->name('v1.roles.stats'); Route::get('/active', [RoleController::class, 'active'])->name('v1.roles.active'); Route::get('/{id}', [RoleController::class, 'show'])->name('v1.roles.show'); Route::patch('/{id}', [RoleController::class, 'update'])->name('v1.roles.update'); Route::delete('/{id}', [RoleController::class, 'destroy'])->name('v1.roles.destroy'); }); // Role Permission API - 공통 Route::get('/role-permissions/menus', [RolePermissionController::class, 'menus'])->name('v1.roles.perms.menus'); // Role Permission API - 역할별 Route::prefix('roles/{id}/permissions')->group(function () { Route::get('/', [RolePermissionController::class, 'index'])->name('v1.roles.perms.index'); Route::post('/', [RolePermissionController::class, 'grant'])->name('v1.roles.perms.grant'); Route::delete('/', [RolePermissionController::class, 'revoke'])->name('v1.roles.perms.revoke'); Route::put('/sync', [RolePermissionController::class, 'sync'])->name('v1.roles.perms.sync'); // 권한 매트릭스 API Route::get('/matrix', [RolePermissionController::class, 'matrix'])->name('v1.roles.perms.matrix'); Route::post('/toggle', [RolePermissionController::class, 'toggle'])->name('v1.roles.perms.toggle'); Route::post('/allow-all', [RolePermissionController::class, 'allowAll'])->name('v1.roles.perms.allowAll'); Route::post('/deny-all', [RolePermissionController::class, 'denyAll'])->name('v1.roles.perms.denyAll'); Route::post('/reset', [RolePermissionController::class, 'reset'])->name('v1.roles.perms.reset'); }); // Permission API Route::prefix('permissions')->group(function () { Route::get('departments/{dept_id}/menu-matrix', [PermissionController::class, 'deptMenuMatrix'])->name('v1.permissions.deptMenuMatrix'); Route::get('roles/{role_id}/menu-matrix', [PermissionController::class, 'roleMenuMatrix'])->name('v1.permissions.roleMenuMatrix'); Route::get('users/{user_id}/menu-matrix', [PermissionController::class, 'userMenuMatrix'])->name('v1.permissions.userMenuMatrix'); }); // Settings & Configuration (설정 및 환경설정 통합 관리) Route::prefix('settings')->group(function () { // 근무 설정 Route::get('/work', [WorkSettingController::class, 'showWorkSetting'])->name('v1.settings.work.show'); Route::put('/work', [WorkSettingController::class, 'updateWorkSetting'])->name('v1.settings.work.update'); // 출퇴근 설정 Route::get('/attendance', [WorkSettingController::class, 'showAttendanceSetting'])->name('v1.settings.attendance.show'); Route::put('/attendance', [WorkSettingController::class, 'updateAttendanceSetting'])->name('v1.settings.attendance.update'); // 급여 설정 Route::get('/payroll', [PayrollController::class, 'getSettings'])->name('v1.settings.payroll.show'); Route::put('/payroll', [PayrollController::class, 'updateSettings'])->name('v1.settings.payroll.update'); // 테넌트 필드 설정 Route::get('/fields', [TenantFieldSettingController::class, 'index'])->name('v1.settings.fields.index'); Route::put('/fields/bulk', [TenantFieldSettingController::class, 'bulkUpsert'])->name('v1.settings.fields.bulk'); Route::patch('/fields/{key}', [TenantFieldSettingController::class, 'updateOne'])->name('v1.settings.fields.update'); // 옵션 그룹/값 Route::get('/options', [TenantOptionGroupController::class, 'index'])->name('v1.settings.options.index'); Route::post('/options', [TenantOptionGroupController::class, 'store'])->name('v1.settings.options.store'); Route::get('/options/{id}', [TenantOptionGroupController::class, 'show'])->name('v1.settings.options.show'); Route::patch('/options/{id}', [TenantOptionGroupController::class, 'update'])->name('v1.settings.options.update'); Route::delete('/options/{id}', [TenantOptionGroupController::class, 'destroy'])->name('v1.settings.options.destroy'); Route::get('/options/{gid}/values', [TenantOptionValueController::class, 'index'])->name('v1.settings.options.values.index'); Route::post('/options/{gid}/values', [TenantOptionValueController::class, 'store'])->name('v1.settings.options.values.store'); Route::get('/options/{gid}/values/{id}', [TenantOptionValueController::class, 'show'])->name('v1.settings.options.values.show'); Route::patch('/options/{gid}/values/{id}', [TenantOptionValueController::class, 'update'])->name('v1.settings.options.values.update'); Route::delete('/options/{gid}/values/{id}', [TenantOptionValueController::class, 'destroy'])->name('v1.settings.options.values.destroy'); Route::patch('/options/{gid}/values/reorder', [TenantOptionValueController::class, 'reorder'])->name('v1.settings.options.values.reorder'); // 공통 코드 관리 Route::get('/common/code', [CommonController::class, 'getComeCode'])->name('v1.settings.common.code'); Route::get('/common', [CommonController::class, 'list'])->name('v1.settings.common.list'); Route::get('/common/{group}', [CommonController::class, 'index'])->name('v1.settings.common.index'); Route::post('/common', [CommonController::class, 'store'])->name('v1.settings.common.store'); Route::patch('/common/{id}', [CommonController::class, 'update'])->name('v1.settings.common.update'); Route::delete('/common/{id}', [CommonController::class, 'destroy'])->name('v1.settings.common.destroy'); // 알림 설정 (그룹 기반) - auth:sanctum 필수 Route::middleware('auth:sanctum')->group(function () { Route::get('/notifications', [NotificationSettingController::class, 'indexGrouped'])->name('v1.settings.notifications.index'); Route::put('/notifications', [NotificationSettingController::class, 'updateGrouped'])->name('v1.settings.notifications.update'); }); }); // Push Notification API (FCM) - auth:sanctum 필수 Route::prefix('push')->middleware('auth:sanctum')->group(function () { Route::post('/register-token', [PushNotificationController::class, 'registerToken'])->name('v1.push.register-token'); Route::post('/unregister-token', [PushNotificationController::class, 'unregisterToken'])->name('v1.push.unregister-token'); Route::get('/tokens', [PushNotificationController::class, 'getTokens'])->name('v1.push.tokens'); Route::get('/settings', [PushNotificationController::class, 'getSettings'])->name('v1.push.settings'); Route::put('/settings', [PushNotificationController::class, 'updateSettings'])->name('v1.push.settings.update'); Route::get('/notification-types', [PushNotificationController::class, 'getNotificationTypes'])->name('v1.push.notification-types'); }); // Category API (통합) Route::prefix('categories')->group(function () { // 확장 기능 (와일드카드 라우트보다 먼저 정의) Route::get('/tree', [CategoryController::class, 'tree'])->name('v1.categories.tree'); Route::post('/reorder', [CategoryController::class, 'reorder'])->name('v1.categories.reorder'); // 기본 Category CRUD Route::get('', [CategoryController::class, 'index'])->name('v1.categories.index'); Route::post('', [CategoryController::class, 'store'])->name('v1.categories.store'); Route::get('/{id}', [CategoryController::class, 'show'])->name('v1.categories.show'); Route::patch('/{id}', [CategoryController::class, 'update'])->name('v1.categories.update'); Route::delete('/{id}', [CategoryController::class, 'destroy'])->name('v1.categories.destroy'); Route::post('/{id}/toggle', [CategoryController::class, 'toggle'])->name('v1.categories.toggle'); Route::patch('/{id}/move', [CategoryController::class, 'move'])->name('v1.categories.move'); // Category Fields Route::get('/{id}/fields', [CategoryFieldController::class, 'index'])->name('v1.categories.fields.index'); Route::post('/{id}/fields', [CategoryFieldController::class, 'store'])->name('v1.categories.fields.store'); Route::get('/fields/{field}', [CategoryFieldController::class, 'show'])->name('v1.categories.fields.show'); Route::patch('/fields/{field}', [CategoryFieldController::class, 'update'])->name('v1.categories.fields.update'); Route::delete('/fields/{field}', [CategoryFieldController::class, 'destroy'])->name('v1.categories.fields.destroy'); Route::post('/{id}/fields/reorder', [CategoryFieldController::class, 'reorder'])->name('v1.categories.fields.reorder'); Route::put('/{id}/fields/bulk-upsert', [CategoryFieldController::class, 'bulkUpsert'])->name('v1.categories.fields.bulk'); // Category Templates Route::get('/{id}/templates', [CategoryTemplateController::class, 'index'])->name('v1.categories.templates.index'); Route::post('/{id}/templates', [CategoryTemplateController::class, 'store'])->name('v1.categories.templates.store'); Route::get('/templates/{tpl}', [CategoryTemplateController::class, 'show'])->name('v1.categories.templates.show'); Route::patch('/templates/{tpl}', [CategoryTemplateController::class, 'update'])->name('v1.categories.templates.update'); Route::delete('/templates/{tpl}', [CategoryTemplateController::class, 'destroy'])->name('v1.categories.templates.destroy'); Route::post('/{id}/templates/{tpl}/apply', [CategoryTemplateController::class, 'apply'])->name('v1.categories.templates.apply'); Route::get('/{id}/templates/{tpl}/preview', [CategoryTemplateController::class, 'preview'])->name('v1.categories.templates.preview'); Route::get('/{id}/templates/diff', [CategoryTemplateController::class, 'diff'])->name('v1.categories.templates.diff'); // Category Logs Route::get('/{id}/logs', [CategoryLogController::class, 'index'])->name('v1.categories.logs.index'); Route::get('/logs/{log}', [CategoryLogController::class, 'show'])->name('v1.categories.logs.show'); }); // Classifications API Route::prefix('classifications')->group(function () { Route::get('', [ClassificationController::class, 'index'])->name('v1.classifications.index'); Route::post('', [ClassificationController::class, 'store'])->name('v1.classifications.store'); Route::get('/{id}', [ClassificationController::class, 'show'])->whereNumber('id')->name('v1.classifications.show'); Route::patch('/{id}', [ClassificationController::class, 'update'])->whereNumber('id')->name('v1.classifications.update'); Route::delete('/{id}', [ClassificationController::class, 'destroy'])->whereNumber('id')->name('v1.classifications.destroy'); }); // Popup API (팝업관리) Route::prefix('popups')->group(function () { Route::get('', [PopupController::class, 'index'])->name('v1.popups.index'); Route::post('', [PopupController::class, 'store'])->name('v1.popups.store'); Route::get('/active', [PopupController::class, 'active'])->name('v1.popups.active'); Route::get('/{id}', [PopupController::class, 'show'])->whereNumber('id')->name('v1.popups.show'); Route::put('/{id}', [PopupController::class, 'update'])->whereNumber('id')->name('v1.popups.update'); Route::delete('/{id}', [PopupController::class, 'destroy'])->whereNumber('id')->name('v1.popups.destroy'); }); // Report API (보고서) Route::prefix('reports')->group(function () { Route::get('/daily', [ReportController::class, 'daily'])->name('v1.reports.daily'); Route::get('/daily/export', [ReportController::class, 'dailyExport'])->name('v1.reports.daily.export'); Route::get('/expense-estimate', [ReportController::class, 'expenseEstimate'])->name('v1.reports.expense-estimate'); Route::get('/expense-estimate/export', [ReportController::class, 'expenseEstimateExport'])->name('v1.reports.expense-estimate.export'); // AI Report API Route::get('/ai', [AiReportController::class, 'index'])->name('v1.reports.ai.index'); Route::post('/ai/generate', [AiReportController::class, 'generate'])->name('v1.reports.ai.generate'); Route::get('/ai/{id}', [AiReportController::class, 'show'])->whereNumber('id')->name('v1.reports.ai.show'); Route::delete('/ai/{id}', [AiReportController::class, 'destroy'])->whereNumber('id')->name('v1.reports.ai.destroy'); }); // Dashboard API (대시보드) Route::prefix('dashboard')->group(function () { Route::get('/summary', [DashboardController::class, 'summary'])->name('v1.dashboard.summary'); Route::get('/charts', [DashboardController::class, 'charts'])->name('v1.dashboard.charts'); Route::get('/approvals', [DashboardController::class, 'approvals'])->name('v1.dashboard.approvals'); });