diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php
new file mode 100644
index 0000000..dfafa0c
--- /dev/null
+++ b/app/Http/Controllers/ProfileController.php
@@ -0,0 +1,55 @@
+validate([
+ 'name' => 'required|string|max:255',
+ 'email' => 'required|email|max:255|unique:users,email,' . $user->id,
+ ]);
+
+ $user->update($validated);
+
+ return redirect()->route('profile')->with('success', 'Perfil actualizado.');
+ }
+
+ /**
+ * Delete the user's account.
+ */
+ public function destroy(Request $request)
+ {
+ $request->validate([
+ 'password' => ['required', 'current_password'],
+ ]);
+
+ $user = Auth::user();
+ Auth::logout();
+ $user->delete();
+
+ $request->session()->invalidate();
+ $request->session()->regenerateToken();
+
+ return redirect('/');
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php
index 6b96a38..5429d5b 100644
--- a/app/Http/Controllers/ProjectController.php
+++ b/app/Http/Controllers/ProjectController.php
@@ -8,6 +8,7 @@ use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
+use App\Models\Project;
class ProjectController extends Controller
{
@@ -53,9 +54,9 @@ class ProjectController extends Controller
/**
* Display the specified resource.
*/
- public function show(string $id)
+ public function show(Project $project)
{
- // No usamos show, redirigimos al mapa o a edición
+ // No usamos show, redirigimos al mapa
return redirect()->route('projects.map', $project);
}
diff --git a/app/Livewire/ProjectMap.php b/app/Livewire/ProjectMap.php
index fee066c..8bfa047 100644
--- a/app/Livewire/ProjectMap.php
+++ b/app/Livewire/ProjectMap.php
@@ -101,6 +101,7 @@ class ProjectMap extends Component
*/
public function selectFeature($featureId)
{
+ $this->selectedFeature = null;
$feature = Feature::with('template')->find($featureId);
if (!$feature) return;
diff --git a/app/Models/Phase.php b/app/Models/Phase.php
index 9633399..e3cb9d3 100644
--- a/app/Models/Phase.php
+++ b/app/Models/Phase.php
@@ -30,4 +30,12 @@ class Phase extends Model
{
return $this->hasOne(Layer::class)->latestOfMany();
}
+
+ /**
+ * Get all features across all layers of this phase.
+ */
+ public function features()
+ {
+ return $this->hasManyThrough(Feature::class, Layer::class);
+ }
}
\ No newline at end of file
diff --git a/resources/views/livewire/projects/project-map.blade.php b/resources/views/livewire/projects/project-map.blade.php
index 27989f6..ff3bbff 100644
--- a/resources/views/livewire/projects/project-map.blade.php
+++ b/resources/views/livewire/projects/project-map.blade.php
@@ -140,31 +140,50 @@
attribution: '© OpenStreetMap'
}).addTo(map);*/
- // Add geojson layers for active phases
+ // Add geojson layers for active phases - features loaded from DB via features() relationship
@foreach($phases as $phase)
- const phase{{ $phase->id }}Data = @json($phase->currentLayer?->geojson_data);
- if (phase{{ $phase->id }}Data) {
+ @php
+ $phaseFeatures = $phase->features()->with('layer')->get();
+ $fc = [
+ 'type' => 'FeatureCollection',
+ 'features' => $phaseFeatures->map(function($f) {
+ return [
+ 'type' => 'Feature',
+ 'id' => $f->id,
+ 'geometry' => $f->geometry,
+ 'properties' => array_merge($f->properties ?? [], [
+ 'name' => $f->name,
+ 'progress' => $f->progress,
+ 'responsible' => $f->responsible,
+ 'template_id' => $f->template_id,
+ '_feature_id' => $f->id,
+ ])
+ ];
+ })->values()->toArray()
+ ];
+ @endphp
+ const phase{{ $phase->id }}Data = @json($fc);
+ if (phase{{ $phase->id }}Data && phase{{ $phase->id }}Data.features && phase{{ $phase->id }}Data.features.length > 0) {
layers[{{ $phase->id }}] = L.geoJSON(phase{{ $phase->id }}Data, {
style: { color: '{{ $phase->color }}', weight: 3, opacity: 0.8, fillOpacity: 0.3 },
onEachFeature: function (feature, layer) {
- const props = feature.properties;
+ const props = feature.properties || {};
+ const featId = props._feature_id || feature.id;
let content = `${props.name || 'Elemento'}
Progreso: ${props.progress || 'N/A'}%
Responsable: ${props.responsible || '-'}
-
-
- `;
+
+ `;
layer.bindPopup(content);
layer.on('click', function(e) {
- @this.selectFeature(feature.properties.id, feature.properties);
+ @this.selectFeature(featId);
});
}
});
if (@json(in_array($phase->id, $activeLayers))) {
layers[{{ $phase->id }}].addTo(map);
}
- }
- @endforeach
+
// 🔁 Forzar que el mapa recalcule su tamaño (por si el contenedor no está visible al 100%)
setTimeout(() => {
@@ -242,8 +261,8 @@
}
}
- window.updatePhaseProgress = function(phaseId, progress) {
- @this.updateProgress(phaseId, progress);
+ window.updateFeatureProgress = function(featureId, progress) {
+ @this.updateProgress(featureId, progress, 'Actualizado desde mapa');
}
@endpush
\ No newline at end of file