diff --git a/app/Livewire/Projects/InspectionTable.php b/app/Livewire/Projects/InspectionTable.php
index 774d175..1e4a644 100644
--- a/app/Livewire/Projects/InspectionTable.php
+++ b/app/Livewire/Projects/InspectionTable.php
@@ -45,7 +45,7 @@ class InspectionTable extends DataTableComponent
return Inspection::query()
->where('inspections.project_id', $this->projectId)
- ->with(['feature', 'template', 'user']);
+ ->with(['feature', 'template', 'user', 'media']);
}
public function columns(): array
@@ -78,18 +78,48 @@ class InspectionTable extends DataTableComponent
->secondaryHeaderFilter('usuario')
->label(fn ($row) => e($row->user?->name ?? '—')),
+ Column::make('Fotos')
+ ->label(fn ($row) => $this->renderPhotosColumn($row))
+ ->html(),
+
Column::make('Acciones')
->label(fn ($row) =>
- '
+ '
+ @can("edit inspections")
+
+ @endcan
')
->html(),
];
}
+ private function renderPhotosColumn($row): string
+ {
+ $images = $row->media->where('category', 'image')->values();
+ $count = $images->count();
+
+ if ($count === 0) {
+ return '
—';
+ }
+
+ $thumbnails = $images->take(3)->map(fn ($m) =>
+ '
+
+ '
+ )->implode('');
+
+ $more = $count > 3 ? '
+' . ($count - 3) . '' : '';
+
+ return '
' . $thumbnails . $more . '
';
+ }
+
public function filters(): array
{
$results = Inspection::where('project_id', $this->projectId)
diff --git a/app/Livewire/Projects/ProjectMap.php b/app/Livewire/Projects/ProjectMap.php
index 207b6e4..7d608ab 100644
--- a/app/Livewire/Projects/ProjectMap.php
+++ b/app/Livewire/Projects/ProjectMap.php
@@ -65,6 +65,14 @@ class ProjectMap extends Component
// Inspection viewer
public $viewingInspection = null;
+ // Inspection editor (para editar inspecciones existentes)
+ public $editingInspection = null;
+ public $editInspectionFormData = [];
+ public $editInspectionResult = '';
+ public $editInspectionNotes = '';
+ public $editInspectionPhotos = [];
+ public $editInspectionPhotosToDelete = [];
+
public function mount(Project $project)
{
$this->project = $project;
@@ -271,12 +279,19 @@ class ProjectMap extends Component
$this->dispatch('notify', 'Selecciona un elemento y un template.');
return;
}
+
+ // Verificar permiso
+ if (!auth()->user()->can('create inspections')) {
+ $this->dispatch('notify', 'Sin permisos para crear inspecciones.');
+ return;
+ }
+
$feature = Feature::with('layer.phase')->find($this->selectedFeature->id);
if (!$feature || $feature->layer->phase->project_id !== $this->project->id) abort(403);
$this->validate([
'selectedTemplateId' => 'required|exists:inspection_templates,id',
- 'inspectionPhotos.*' => 'nullable|image|max:20480',
+ 'inspectionPhotos.*' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:10240',
]);
$template = InspectionTemplate::find($this->selectedTemplateId);
@@ -406,7 +421,7 @@ class ProjectMap extends Component
'notes' => $ins->notes,
'data' => $ins->data ?? [],
'fields' => $ins->template?->fields ?? [],
- 'photos' => $ins->media->map(fn ($m) => ['url' => $m->url, 'name' => $m->name])->values()->all(),
+ 'photos' => $ins->media->map(fn ($m) => ['url' => $m->url, 'name' => $m->name, 'id' => $m->id])->values()->all(),
];
}
@@ -415,6 +430,123 @@ class ProjectMap extends Component
$this->viewingInspection = null;
}
+ // ─── Inspection Editor (editar inspección existente) ─────────────────────────
+
+ #[On('edit-inspection')]
+ public function editInspection($id)
+ {
+ $ins = Inspection::where('project_id', $this->project->id)
+ ->with(['feature.layer.phase', 'template', 'user', 'media'])
+ ->find($id);
+ if (!$ins) return;
+
+ $this->editingInspection = $ins;
+ $this->editInspectionFormData = $ins->data ?? [];
+ $this->editInspectionResult = $ins->result ?? '';
+ $this->editInspectionNotes = $ins->notes ?? '';
+ $this->editInspectionPhotos = [];
+ $this->editInspectionPhotosToDelete = [];
+ $this->selectedTemplateId = $ins->template_id;
+
+ $this->dispatch('openEditInspectionModal');
+ }
+
+ public function closeEditInspection()
+ {
+ $this->editingInspection = null;
+ $this->editInspectionFormData = [];
+ $this->editInspectionResult = '';
+ $this->editInspectionNotes = '';
+ $this->editInspectionPhotos = [];
+ $this->editInspectionPhotosToDelete = [];
+ }
+
+ public function deleteEditPhoto($mediaIndex)
+ {
+ if (!$this->editingInspection) return;
+ $media = $this->editingInspection->media;
+ if (isset($media[$mediaIndex])) {
+ $m = $media[$mediaIndex];
+ if (!in_array($m->id, $this->editInspectionPhotosToDelete)) {
+ $this->editInspectionPhotosToDelete[] = $m->id;
+ }
+ }
+ }
+
+ public function saveEditInspection()
+ {
+ if (!$this->editingInspection) return;
+
+ // Verificar permiso
+ if (!auth()->user()->can('edit inspections')) {
+ $this->dispatch('notify', 'Sin permisos para editar inspecciones.');
+ return;
+ }
+
+ $this->validate([
+ 'selectedTemplateId' => 'required|exists:inspection_templates,id',
+ 'editInspectionPhotos.*' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:10240',
+ 'editInspectionPhotosToDelete' => 'array',
+ 'editInspectionPhotosToDelete.*' => 'exists:media,id',
+ ]);
+
+ $ins = $this->editingInspection;
+ if ($ins->project_id !== $this->project->id) abort(403);
+
+ $template = InspectionTemplate::find($this->selectedTemplateId);
+ foreach ($template->fields as $field) {
+ if (($field['required'] ?? false) && empty($this->editInspectionFormData[$field['name']])) {
+ $this->dispatch('notify', "El campo {$field['label']} es obligatorio.");
+ return;
+ }
+ }
+
+ // Eliminar fotos marcadas
+ if (!empty($this->editInspectionPhotosToDelete)) {
+ $mediaToDelete = Media::whereIn('id', $this->editInspectionPhotosToDelete)
+ ->where('mediable_type', Inspection::class)
+ ->where('mediable_id', $ins->id)
+ ->get();
+ foreach ($mediaToDelete as $m) {
+ $m->delete();
+ }
+ }
+
+ // Actualizar datos
+ $ins->update([
+ 'template_id' => $this->selectedTemplateId,
+ 'result' => $this->editInspectionResult ?: null,
+ 'notes' => $this->editInspectionNotes ?: null,
+ 'data' => $this->editInspectionFormData,
+ ]);
+
+ // Añadir nuevas fotos
+ foreach ($this->editInspectionPhotos as $photo) {
+ $mime = $photo->getMimeType();
+ $path = $photo->store("uploads/inspections/{$ins->id}", 'public');
+ $ins->media()->create([
+ 'name' => $photo->getClientOriginalName(),
+ 'file_path' => $path,
+ 'file_type' => $mime,
+ 'file_extension' => $photo->getClientOriginalExtension(),
+ 'file_size' => $photo->getSize(),
+ 'category' => str_starts_with($mime, 'image/') ? 'image' : 'document',
+ 'uploaded_by' => auth()->id(),
+ 'uuid' => (string) \Illuminate\Support\Str::uuid(),
+ ]);
+ }
+
+ // Refresh
+ $this->allInspections = Inspection::where('project_id', $this->project->id)
+ ->with(['feature.layer.phase', 'template', 'user'])
+ ->orderBy('created_at', 'desc')
+ ->get();
+ $this->loadInspectionHistory();
+
+ $this->closeEditInspection();
+ $this->dispatch('notify', 'Inspección actualizada correctamente');
+ }
+
// ─── Feature images ──────────────────────────────────────────────────────────
public function toggleFeatureImages()
diff --git a/database/seeders/PermissionCatalogSeeder.php b/database/seeders/PermissionCatalogSeeder.php
index f70fc09..1896bf8 100644
--- a/database/seeders/PermissionCatalogSeeder.php
+++ b/database/seeders/PermissionCatalogSeeder.php
@@ -39,6 +39,7 @@ class PermissionCatalogSeeder extends Seeder
'Inspecciones' => [
'view inspections' => 'Ver inspecciones e historial',
'create inspections' => 'Registrar inspecciones',
+ 'edit inspections' => 'Editar inspecciones existentes',
'delete inspections' => 'Eliminar inspecciones',
'manage templates' => 'Gestionar plantillas de inspección',
],
diff --git a/database/seeders/RolesAndPermissionsSeeder.php b/database/seeders/RolesAndPermissionsSeeder.php
index b68298c..688037e 100644
--- a/database/seeders/RolesAndPermissionsSeeder.php
+++ b/database/seeders/RolesAndPermissionsSeeder.php
@@ -17,7 +17,8 @@ class RolesAndPermissionsSeeder extends Seeder
// Create permissions
$permissions = [
'view projects', 'create projects', 'edit projects', 'delete projects',
- 'assign users', 'upload layers', 'update progress', 'view reports', 'manage all'
+ 'assign users', 'upload layers', 'update progress', 'view reports', 'manage all',
+ 'view inspections', 'create inspections', 'edit inspections', 'delete inspections', 'manage templates',
];
foreach ($permissions as $perm) {
Permission::firstOrCreate(['name' => $perm]);
@@ -28,7 +29,10 @@ class RolesAndPermissionsSeeder extends Seeder
$admin->givePermissionTo(Permission::all());
$supervisor = Role::firstOrCreate(['name' => 'Supervisor']);
- $supervisor->givePermissionTo(['view projects', 'upload layers', 'update progress']);
+ $supervisor->givePermissionTo([
+ 'view projects', 'upload layers', 'update progress',
+ 'view inspections', 'create inspections', 'edit inspections',
+ ]);
$consultor = Role::firstOrCreate(['name' => 'Consultor']);
$consultor->givePermissionTo(['view projects', 'view reports']);
diff --git a/resources/views/livewire/projects/project-map-inspections-tab.blade.php b/resources/views/livewire/projects/project-map-inspections-tab.blade.php
index ad4d702..ec93394 100644
--- a/resources/views/livewire/projects/project-map-inspections-tab.blade.php
+++ b/resources/views/livewire/projects/project-map-inspections-tab.blade.php
@@ -78,8 +78,18 @@
{{ __("by") }} {{ $inspection->user->name }}
@endif
-
+
{{ __("View") }}
+ @can('edit inspections')
+
+ @endcan
diff --git a/resources/views/livewire/projects/project-map.blade.php b/resources/views/livewire/projects/project-map.blade.php
index 19b4700..2d013cb 100644
--- a/resources/views/livewire/projects/project-map.blade.php
+++ b/resources/views/livewire/projects/project-map.blade.php
@@ -374,22 +374,197 @@
@if(!empty($viewingInspection['photos']))