name('login'); // Control Panel Login Route::get('/controlpannel/login', [LoginController::class, 'showAdminLoginForm'])->name('admin.login'); Route::post('/controlpannel/login', [LoginController::class, 'adminLogin'])->name('admin.login.submit'); Route::get('/admin/login', fn() => redirect()->route('admin.login')); Route::post('/logout', [LoginController::class, 'logout'])->name('logout'); Route::get('/logout', [LoginController::class, 'logout']); // For ease of development/testing via GET // Microsoft OAuth endpoints Route::get('/auth/microsoft/redirect', [LoginController::class, 'redirectToMicrosoft'])->name('auth.microsoft.redirect'); Route::get('/auth/microsoft/callback', [LoginController::class, 'handleMicrosoftCallback'])->name('auth.microsoft.callback'); Route::get('/auth/microsoft/logout', [LoginController::class, 'frontChannelLogout'])->name('auth.microsoft.logout'); // Candidate Assessment Portal Routes (Public & Temporary Credentials Auth) Route::get('/candidate/login', [InterviewController::class, 'showCandidateLogin'])->name('interview.candidate.login'); Route::post('/candidate/login', [InterviewController::class, 'loginCandidate'])->name('interview.candidate.login.submit'); Route::get('/candidate/test/{uniqueId}', [InterviewController::class, 'showCandidateRoom'])->name('interview.room'); Route::post('/candidate/execute-code', [InterviewController::class, 'executeCode'])->name('interview.execute'); Route::post('/candidate/submit-code/{id}', [InterviewController::class, 'submitCode'])->name('interview.submit'); Route::post('/candidate/log-violation/{id}', [InterviewController::class, 'logViolation'])->name('interview.violation'); Route::post('/candidate/upload-recording/{id}', [InterviewController::class, 'uploadRecording'])->name('interview.upload-recording'); Route::post('/candidate/upload-tab-screenshot/{id}', [InterviewController::class, 'uploadTabScreenshot'])->name('interview.upload-tab-screenshot'); Route::post('/candidate/sync-code/{id}', [InterviewController::class, 'syncCandidateCode'])->name('interview.candidate.sync-code'); Route::post('/candidate/notes/{id}', [InterviewController::class, 'saveCandidateNotes'])->name('interview.candidate.notes'); Route::post('/candidate/drawing/{id}', [InterviewController::class, 'saveCandidateDrawing'])->name('interview.candidate.drawing'); Route::post('/candidate/send-message/{id}', [InterviewController::class, 'candidateSendMessage'])->name('interview.candidate.send-message'); Route::post('/candidate/peer-heartbeat/{id}', [InterviewController::class, 'registerPeerHeartbeat'])->name('interview.candidate.peer-heartbeat'); Route::get('/candidate/poll/{id}', [InterviewController::class, 'getPollData'])->name('interview.candidate.poll'); // Public Storage & Media Stream Delivery & ICE Servers Route::get('/media-stream/{path}', [InterviewController::class, 'serveStorageFile'])->where('path', '.*')->name('media.stream'); Route::get('/storage/{path}', [InterviewController::class, 'serveStorageFile'])->where('path', '.*')->name('storage.file'); Route::get('/ice-servers', IceServerController::class)->name('ice-servers'); // User Dashboard Portal (requires authenticated user) Route::middleware(['auth', 'role:user', 'verify-ms-session'])->group(function () { Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); }); Route::middleware(['auth'])->group(function () { Route::get('/sso/launch/{id}', [DashboardController::class, 'launchSSO'])->name('sso.launch'); Route::get('/sso/personal-launch/{id}', [DashboardController::class, 'launchPersonalSSO'])->name('sso.personal-launch'); Route::get('/sso/callback', [DashboardController::class, 'handleSSOCallback'])->name('sso.callback'); Route::get('/sso/launch-app', [DashboardController::class, 'launchApp'])->name('sso.launch-app'); // Personal custom apps management Route::post('/dashboard/personal-apps', [DashboardController::class, 'storePersonalApp'])->name('dashboard.personal-apps.store'); Route::post('/dashboard/personal-apps/{id}/update', [DashboardController::class, 'updatePersonalApp'])->name('dashboard.personal-apps.update'); Route::delete('/dashboard/personal-apps/{id}', [DashboardController::class, 'destroyPersonalApp'])->name('dashboard.personal-apps.destroy'); // Onboarding & Offboarding Management Routes Route::get('/onboarding', [OnboardingController::class, 'index'])->name('onboarding.index'); Route::post('/onboarding', [OnboardingController::class, 'store'])->name('onboarding.store'); Route::post('/onboarding/{id}/activate', [OnboardingController::class, 'activate'])->name('onboarding.activate'); Route::post('/onboarding/{id}/offboard', [OnboardingController::class, 'offboard'])->name('onboarding.offboard'); Route::post('/onboarding/{id}/checklist', [OnboardingController::class, 'updateChecklist'])->name('onboarding.checklist'); Route::delete('/onboarding/{id}', [OnboardingController::class, 'destroy'])->name('onboarding.destroy'); // Candidate Assessment Management for HR & Panelists Route::get('/interviews', [InterviewController::class, 'index'])->name('interview.index'); Route::get('/interviews/active-calls', [InterviewController::class, 'getActiveCalls'])->name('interview.active-calls'); Route::get('/interviews/{id}', [InterviewController::class, 'show'])->name('interview.show'); Route::post('/interviews', [InterviewController::class, 'store'])->name('interview.store'); Route::post('/interviews/{id}/warning', [InterviewController::class, 'sendWarning'])->name('interview.warning'); Route::get('/interviews/{id}/poll', [InterviewController::class, 'getPollData'])->name('interview.poll'); Route::post('/interviews/{id}/peer-heartbeat', [InterviewController::class, 'registerPeerHeartbeat'])->name('interview.peer-heartbeat'); Route::post('/interviews/{id}/regenerate-password', [InterviewController::class, 'regeneratePassword'])->name('interview.regenerate-password'); Route::post('/interviews/{id}/block-candidate', [InterviewController::class, 'blockCandidate'])->name('interview.block-candidate'); Route::get('/interviews/{id}/report', [InterviewController::class, 'generateReport'])->name('interview.report'); Route::get('/interviews/{id}/download-recording', [InterviewController::class, 'downloadRecording'])->name('interview.download-recording'); Route::post('/interviews/{id}/call-status', [InterviewController::class, 'updateCallStatus'])->name('interview.call-status'); Route::post('/interviews/{id}/toggle-tab-screenshot', [InterviewController::class, 'toggleTabScreenshot'])->name('interview.toggle-tab-screenshot'); // Client Call & Project Management Hub (PM, TL, Director, Admin) Route::get('/client-calls', [ClientCallController::class, 'index'])->name('client-calls.index'); Route::get('/client-calls/logs', [ClientCallController::class, 'callLogs'])->name('client-calls.logs'); Route::post('/client-calls', [ClientCallController::class, 'store'])->name('client-calls.store'); Route::get('/client-calls/{id}', [ClientCallController::class, 'show'])->name('client-calls.show'); Route::post('/client-calls/{id}/schedule-meeting', [ClientCallController::class, 'scheduleMeeting'])->name('client-calls.schedule-meeting'); Route::post('/client-calls/{id}/instant-meeting', [ClientCallController::class, 'createInstantMeeting'])->name('client-calls.instant-meeting'); Route::get('/client-calls/notes/{id}/download-mom', [ClientCallController::class, 'downloadMoM'])->name('client-calls.download-mom'); Route::get('/client-calls/notes/{id}/download-todos', [ClientCallController::class, 'downloadTodos'])->name('client-calls.download-todos'); Route::get('/client-calls/notes/{id}/emotion-details', [ClientCallController::class, 'getEmotionDetails'])->name('client-calls.emotion-details'); Route::post('/client-calls/notes/{id}/toggle-todo/{todoId}', [ClientCallController::class, 'toggleTodo'])->name('client-calls.toggle-todo'); Route::post('/client-calls/notes/{id}/add-todo', [ClientCallController::class, 'addTodo'])->name('client-calls.add-todo'); Route::delete('/client-calls/notes/{id}/delete-todo/{todoId}', [ClientCallController::class, 'deleteTodo'])->name('client-calls.delete-todo'); Route::get('/client-calls/notes/{id}/angry-analysis', [ClientCallController::class, 'getAngryAnalysis'])->name('client-calls.angry-analysis'); Route::post('/client-calls/notes/{id}/resend-email', [ClientCallController::class, 'resendMoMEmail'])->name('client-calls.resend-email'); Route::get('/client-calls/meetings/{id}/download-recording', [ClientCallController::class, 'downloadMeetingRecording'])->name('client-calls.download-meeting-recording'); Route::get('/client-calls/notes/{id}/download-recording', [ClientCallController::class, 'downloadNoteRecording'])->name('client-calls.download-note-recording'); }); // Client Video Call Room (Accessible to authenticated internal members & external guest clients via link) Route::get('/client-calls/room/{code}', [ClientCallController::class, 'showRoom'])->name('client-calls.room'); Route::post('/client-calls/room/{code}/join-guest', [ClientCallController::class, 'joinGuest'])->name('client-calls.join-guest'); Route::post('/client-calls/room/{code}/detect-emotion', [ClientCallController::class, 'detectEmotion'])->name('client-calls.detect-emotion'); Route::post('/client-calls/room/{code}/upload-recording', [ClientCallController::class, 'uploadRecording'])->name('client-calls.upload-recording'); Route::post('/client-calls/room/{code}/end-call', [ClientCallController::class, 'endCallAndGenerateMoM'])->name('client-calls.end-call'); Route::post('/client-calls/room/{code}/heartbeat', [ClientCallController::class, 'signalingHeartbeat'])->name('client-calls.heartbeat'); // Admin Control Panel (requires admin role) Route::middleware(['auth', 'role:admin'])->prefix('controlpannel')->group(function () { Route::get('/', [AdminController::class, 'index'])->middleware('verify-ms-session'); Route::get('/dashboard', [AdminController::class, 'index'])->name('admin.dashboard')->middleware('verify-ms-session'); Route::post('/apps', [AdminController::class, 'storeApp'])->name('admin.apps.store'); Route::post('/apps/{id}/update', [AdminController::class, 'updateApp'])->name('admin.apps.update'); Route::delete('/apps/{id}', [AdminController::class, 'destroyApp'])->name('admin.apps.destroy'); Route::post('/users/{id}/toggle-block', [AdminController::class, 'toggleBlockUser'])->name('admin.users.toggle-block'); Route::post('/users/{id}/toggle-admin', [AdminController::class, 'toggleAdminPrivilege'])->name('admin.users.toggle-admin'); // Roles CRUD Route::post('/roles', [AdminController::class, 'storeRole'])->name('admin.roles.store'); Route::post('/roles/{id}/update', [AdminController::class, 'updateRole'])->name('admin.roles.update'); Route::delete('/roles/{id}', [AdminController::class, 'destroyRole'])->name('admin.roles.destroy'); Route::post('/settings/default-role', [AdminController::class, 'updateDefaultRole'])->name('admin.settings.default-role'); Route::post('/settings/onboarding-toggle', [AdminController::class, 'updateOnboardingToggle'])->name('admin.settings.onboarding-toggle'); Route::post('/settings/screenshot-toggle', [AdminController::class, 'updateScreenshotToggle'])->name('admin.settings.screenshot-toggle'); // User Roles & Overrides Route::post('/users/{id}/roles', [AdminController::class, 'updateUserRoles'])->name('admin.users.roles.update'); Route::post('/overrides', [AdminController::class, 'storeUserOverride'])->name('admin.overrides.store'); Route::delete('/overrides/{id}', [AdminController::class, 'destroyUserOverride'])->name('admin.overrides.destroy'); }); // Legacy /admin redirect to /controlpannel/dashboard Route::get('/admin/{any?}', fn() => redirect()->route('admin.dashboard'))->where('any', '.*');