selectedFeature) { return; } $feature = Feature::with('layer.phase')->findOrFail($this->selectedFeature->id); if ($feature->layer->phase->project_id !== $this->project->id) { abort(403); } $feature->status = $status; if ($status === 'completed') { $feature->progress = 100; } if ($status === 'planned') { $feature->progress = 0; } $feature->save(); $this->selectedFeature = $feature; $this->editProgress = $feature->progress; $this->allFeatures = $this->allFeatures->map(fn ($f) => $f->id === $feature->id ? $feature : $f); $this->dispatch('featureStatusChanged', $feature->id, $feature->status, $feature->status_color); $this->dispatch('notify', 'Estado actualizado'); } public function updateProgress($featureId, $newProgress, $comment = null) { $feature = Feature::with('layer.phase')->findOrFail($featureId); $user = Auth::user(); if (! $user->can('update progress')) { $this->dispatch('notify', 'Sin permisos'); return; } if ($feature->layer->phase->project_id !== $this->project->id) { abort(403); } $feature->progress = min(100, max(0, $newProgress)); $feature->save(); $phase = $feature->layer->phase; $phase->progress_percent = $phase->features()->avg('progress') ?: 0; $phase->save(); $phase->progressUpdates()->create([ 'user_id' => $user->id, 'progress_percent' => $phase->progress_percent, 'comment' => $comment, ]); $this->dispatch('progressUpdated', $featureId, $feature->progress); $this->dispatch('notify', 'Progreso actualizado'); if ($this->selectedFeature && $this->selectedFeature->id == $featureId) { $this->selectedFeature->progress = $feature->progress; $this->editProgress = $feature->progress; } } #[On('map-select-feature')] public function selectFeature($featureId) { \Log::info('[ProjectMap] map-select-feature received', ['payload' => $featureId, 'type' => gettype($featureId)]); // Handle both formats: direct ID or { featureId: X } if (is_array($featureId) && isset($featureId['featureId'])) { $featureId = $featureId['featureId']; \Log::info('[ProjectMap] Extracted featureId from array', ['featureId' => $featureId]); } $this->selectedFeature = null; $feature = Feature::with(['template', 'layer.phase'])->find($featureId); if (! $feature) { \Log::warning('[ProjectMap] Feature not found', ['featureId' => $featureId]); return; } if ($feature->layer->phase->project_id !== $this->project->id) { \Log::warning('[ProjectMap] Feature not in project', ['featureId' => $featureId, 'projectId' => $this->project->id]); abort(403); } $this->selectedFeature = $feature; $this->selectedPhaseId = $feature->layer->phase_id; $this->editProgress = $feature->progress; $this->editResponsible = $feature->responsible ?? ''; $this->editPhotos = $feature->properties['photos'] ?? []; $this->selectedTemplateId = $feature->template_id; $this->activeTab = 'edit'; $this->loadInspectionHistory(); $this->resetInspectionForm(); $this->dispatch('featureSelected', $featureId, $feature->name); } public function saveFeatureProgress() { if (! $this->selectedFeature) { return; } $feature = Feature::with('layer.phase')->findOrFail($this->selectedFeature->id); if ($feature->layer->phase->project_id !== $this->project->id) { abort(403); } $feature->progress = min(100, max(0, (int) $this->editProgress)); $feature->responsible = $this->editResponsible; $feature->save(); $this->selectedFeature = $feature; $phase = $feature->layer->phase; $phase->progress_percent = $phase->features()->avg('progress') ?: 0; $phase->save(); $this->dispatch('progressUpdated', $phase->id, $phase->progress_percent); $this->dispatch('notify', 'Progreso guardado'); } // ─── Feature Images ──────────────────────────────────────────────────────── public function toggleFeatureImages() { $this->showFeatureImages = ! $this->showFeatureImages; $this->loadFeatureImageMarkers(); $this->dispatch('featureImagesToggled', $this->showFeatureImages, $this->featureImageMarkers); } public function loadFeatureImageMarkers() { if (! $this->showFeatureImages) { $this->featureImageMarkers = []; return; } $markers = []; foreach ($this->phases as $phase) { foreach ($phase->layers as $layer) { foreach ($layer->features as $feature) { $image = $feature->images->first(); if ($image) { $geo = $feature->geometry; $coords = null; if ($geo && isset($geo['coordinates'])) { if ($geo['type'] === 'Point') { $coords = ['lat' => $geo['coordinates'][1], 'lng' => $geo['coordinates'][0]]; } elseif (in_array($geo['type'], ['Polygon', 'MultiPolygon'])) { $firstRing = $geo['type'] === 'Polygon' ? $geo['coordinates'][0] : $geo['coordinates'][0][0]; $coords = ['lat' => $firstRing[0][1], 'lng' => $firstRing[0][0]]; } } if ($coords && $coords['lat'] && $coords['lng']) { $markers[] = [ 'feature_id' => $feature->id, 'name' => $feature->name, 'lat' => $coords['lat'], 'lng' => $coords['lng'], 'image_url' => $image->url, 'image_name' => $image->name, ]; } } } } } $this->featureImageMarkers = $markers; } public function toggleFullscreen() { $this->formFullscreen = ! $this->formFullscreen; if (! $this->formFullscreen) { $this->dispatch('mapResize'); } } public function setActiveTab($tab) { $this->activeTab = $tab; } }