route('dashboard'); })->middleware(['auth']); // Grupo de rutas protegidas por autenticación Route::middleware(['auth'])->group(function () { // Home: vista ligera (proyectos, tareas e incidencias del usuario + notificaciones) Route::get('/dashboard', function () { $user = Auth::user(); $projects = Project::accessibleBy($user) ->with(['phases' => fn ($q) => $q->select('id', 'project_id', 'progress_percent')]) ->orderBy('name')->take(8)->get(); $projectsCount = Project::accessibleBy($user)->count(); $myTasks = IssueTask::where('assigned_to', $user->id) ->where('is_done', false) ->with('issue.project') ->orderByRaw('due_date IS NULL, due_date ASC') ->take(8)->get(); $myTasksFromTasks = Task::where('assigned_to', $user->id) ->where('status', '!=', 'completed') ->with('project') ->orderByRaw('due_date IS NULL, due_date ASC') ->take(8)->get(); $myTasksCount = IssueTask::where('assigned_to', $user->id)->where('is_done', false)->count() + Task::where('assigned_to', $user->id)->where('status', '!=', 'completed')->count(); $myIssues = Issue::where('assigned_to', $user->id) ->whereIn('status', ['open', 'in_review']) ->with('project') ->latest()->take(6)->get(); $myIssuesCount = Issue::where('assigned_to', $user->id)->whereIn('status', ['open', 'in_review'])->count(); $notifications = $user->notifications()->latest()->take(6)->get(); $unreadCount = $user->unreadNotifications()->count(); return view('dashboard', compact( 'user', 'projects', 'projectsCount', 'myTasks', 'myTasksFromTasks', 'myTasksCount', 'myIssues', 'myIssuesCount', 'notifications', 'unreadCount' )); })->name('dashboard'); Route::get('/reports/dashboard', ReportsDashboard::class)->name('reports.dashboard'); Route::prefix('reports')->name('reports.')->group(function () { Route::get('export/projects', [ExportController::class, 'exportProjects'])->name('export.projects'); Route::get('export/phases', [ExportController::class, 'exportPhases'])->name('export.phases'); Route::get('export/inspections', [ExportController::class, 'exportInspections'])->name('export.inspections'); }); // New Report routes Route::prefix('projects/{project}/reports')->name('reports.project.')->middleware('can:view reports')->group(function () { Route::get('/', [ReportController::class, 'show'])->name('builder'); Route::post('/generate', [ReportController::class, 'generate'])->name('generate'); Route::get('/preview', [ReportController::class, 'preview'])->name('preview'); Route::get('/excel', [ReportController::class, 'generate'])->name('excel'); }); // ------------------------------------------------------------ // Gestión de proyectos // ------------------------------------------------------------ // Create/Edit handled by unified Livewire component Route::get('/projects/create', ProjectForm::class)->name('projects.create'); Route::get('/projects/{project}/edit', ProjectForm::class)->name('projects.edit'); Route::resource('projects', ProjectController::class)->except(['create', 'edit']); // Ruta personalizada para ver el mapa de un proyecto específico Route::get('/projects/{project}/map', [ProjectController::class, 'map'])->name('projects.map'); // Ruta para que el componente Livewire muestre/gestione el progreso de una fase Route::get('/phases/{phase}/progress', PhaseProgress::class)->name('phases.progress'); Route::get('/projects-list', ProjectList::class)->name('projects.list'); // Ruta para templates // Plantillas globales (catálogo único) Route::get('/inspection-templates', GlobalTemplateManager::class) ->middleware('can:manage templates')->name('inspection-templates'); // Plantillas asignadas a un proyecto (picker sobre el catálogo global) Route::get('/projects/{project}/templates', ProjectTemplatesPicker::class) ->middleware('can:edit projects')->name('projects.templates'); // Rutas para el LayerManager: Route::get('/projects/{project}/phases/{phase}/layers/manage', LayerManager::class)->name('layers.manage'); // Elementos del proyecto y catálogo de tipos de elemento Route::get('/projects/{project}/features', FeatureManager::class)->middleware('can:edit layers')->name('projects.features'); Route::get('/feature-types', FeatureTypeManager::class)->middleware('can:edit layers')->name('feature-types'); // Cronograma Gantt y reporte del proyecto Route::get('/projects/{project}/gantt', PhaseGantt::class)->name('projects.gantt'); Route::get('/projects/{project}/report', [ProjectReportController::class, 'show'])->name('projects.report'); // Issues del proyecto Route::get('/projects/{project}/issues', IssueManager::class)->name('projects.issues'); Route::get('/projects/{project}/issues/create', IssueForm::class)->name('projects.issues.create'); Route::get('/projects/{project}/issues/checklists', IssueChecklistManager::class)->name('projects.issues.checklists'); Route::get('/projects/{project}/issues/{issue}', IssueDetail::class)->name('projects.issues.show'); Route::get('/projects/{project}/issues/{issue}/edit', IssueForm::class)->name('projects.issues.edit'); // Tasks del proyecto Route::get('/projects/{project}/tasks', TaskManager::class)->name('projects.tasks'); Route::get('/projects/{project}/tasks/create', TaskForm::class)->name('projects.tasks.create'); Route::get('/projects/{project}/tasks/{task}', TaskDetail::class)->name('projects.tasks.show'); Route::get('/projects/{project}/tasks/{task}/edit', TaskForm::class)->name('projects.tasks.edit'); Route::get('/projects/{project}/tasks/kanban', TaskKanban::class)->name('projects.tasks.kanban'); Route::get('/projects/{project}/tasks/calendar', TaskCalendar::class)->name('projects.tasks.calendar'); // Dashboard por proyecto Route::get('/projects/{project}/dashboard', ProjectDashboard::class)->name('projects.dashboard'); // Cliente: portal cliente Route::middleware(['auth', 'role:client'])->prefix('client')->name('client.')->group(function () { Route::get('/', function () { return view('client.dashboard'); })->name('dashboard'); }); // Admin: gestión de usuarios y roles (cada ruta protegida por su permiso) Route::prefix('admin')->name('admin.')->group(function () { Route::get('/users', function () { return view('admin.users'); })->middleware('can:view users')->name('users'); Route::get('/users/create', UserForm::class)->middleware('can:create users')->name('users.create'); Route::get('/users/{user}', UserView::class)->middleware('can:view users')->name('users.show'); Route::get('/users/{user}/edit', UserForm::class)->middleware('can:edit users')->name('users.edit'); Route::get('/roles', function () { return view('admin.roles'); })->middleware('can:manage roles')->name('roles'); Route::get('/roles/create', RoleForm::class)->middleware('can:manage roles')->name('roles.create'); Route::get('/roles/{role}/edit', RoleForm::class)->middleware('can:manage roles')->name('roles.edit'); Route::get('/roles/{role}', RoleView::class)->middleware('can:manage roles')->name('roles.show'); Route::get('/permissions', RolePermissionManager::class)->middleware('can:manage roles')->name('permissions'); }); // Gestor de medios Route::get('/projects/{project}/media', function (Project $project) { return view('projects.media', compact('project')); })->name('projects.media'); Route::get('/companies', CompanyManagement::class)->name('companies.manage'); Route::get('/companies/create', CompanyForm::class)->name('companies.create'); Route::get('/companies/{company}', CompanyView::class)->name('companies.show'); Route::get('/companies/{company}/edit', CompanyForm::class)->name('companies.edit'); // ------------------------------------------------------------ // Sincronización offline (para trabajadores en campo) // ------------------------------------------------------------ Route::post('/offline/pending', [OfflineSyncController::class, 'storePending'])->name('offline.store'); Route::post('/offline/sync', [OfflineSyncController::class, 'sync'])->name('offline.sync'); // ------------------------------------------------------------ // Perfil de usuario (proporcionado por Laravel Breeze) // ------------------------------------------------------------ Route::get('/profile', [ProfileController::class, 'edit'])->name('profile'); // Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); // Logout se gestiona vía la acción Volt en el componente de navegación // (App\Livewire\Actions\Logout), por lo que no hace falta una ruta /logout. }); // Incluir rutas de autenticación (login, registro, recuperación de contraseña, logout) require __DIR__.'/auth.php';