chore: cleanup dead code + format with Pint
- Remove unused FeaturesController (empty stubs, no routes) - Remove ConvertSpatialFile CLI command (unused; service used in LayerManager) - Remove MigrateGeojsonToFeatures CLI command (one-shot migration, not referenced) - Remove .claude/worktrees/ (11 old agent worktrees from June) - Apply Laravel Pint formatting across 219 files (style only, no functional changes) Tests: 101 passing (319 assertions) API routes: unchanged (8 routes intact)
This commit is contained in:
@@ -2,19 +2,19 @@
|
||||
|
||||
namespace App\Livewire\Layers;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\On;
|
||||
use App\Models\Project;
|
||||
use App\Models\Phase;
|
||||
use App\Models\Layer;
|
||||
use App\Models\Feature;
|
||||
use App\Models\InspectionTemplate;
|
||||
use App\Models\Layer;
|
||||
use App\Models\Phase;
|
||||
use App\Models\Project;
|
||||
use App\Services\SpatialFileConverter;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
|
||||
#[Layout('layouts.app')]
|
||||
class LayerManager extends Component
|
||||
@@ -22,29 +22,39 @@ class LayerManager extends Component
|
||||
use WithFileUploads;
|
||||
|
||||
public Project $project;
|
||||
public Phase $phase;
|
||||
|
||||
public Phase $phase;
|
||||
|
||||
public $layers;
|
||||
|
||||
public $selectedLayer = null;
|
||||
|
||||
public $visibleLayers = [];
|
||||
|
||||
public $uploadFile = null;
|
||||
public $layerName = '';
|
||||
public $layerColor = '#3b82f6';
|
||||
public $uploadFile = null;
|
||||
|
||||
public $layerName = '';
|
||||
|
||||
public $layerColor = '#3b82f6';
|
||||
|
||||
// Batch assign
|
||||
public $templates = [];
|
||||
public $templates = [];
|
||||
|
||||
public $batchTemplateId = null;
|
||||
public $batchStatus = '';
|
||||
|
||||
public $batchStatus = '';
|
||||
|
||||
public function mount(Project $project, Phase $phase)
|
||||
{
|
||||
$this->project = $project;
|
||||
$this->phase = $phase;
|
||||
$this->phase = $phase;
|
||||
|
||||
if ($this->phase->project_id !== $this->project->id) abort(404);
|
||||
if ($this->phase->project_id !== $this->project->id) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
if (!$user->can('manage all') && !$project->users()->where('user_id', $user->id)->exists()) {
|
||||
if (! $user->can('manage all') && ! $project->users()->where('user_id', $user->id)->exists()) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
@@ -71,28 +81,28 @@ class LayerManager extends Component
|
||||
|
||||
private function buildLayerPayload(Layer $layer): array
|
||||
{
|
||||
$color = $layer->color ?: '#3b82f6';
|
||||
$color = $layer->color ?: '#3b82f6';
|
||||
$features = ($layer->relationLoaded('features') ? $layer->features : $layer->features()->get())
|
||||
->map(fn($f) => [
|
||||
'type' => 'Feature',
|
||||
'id' => $f->id,
|
||||
'geometry' => $f->geometry,
|
||||
->map(fn ($f) => [
|
||||
'type' => 'Feature',
|
||||
'id' => $f->id,
|
||||
'geometry' => $f->geometry,
|
||||
'properties' => [
|
||||
'name' => $f->name ?? 'Elemento',
|
||||
'progress' => $f->progress,
|
||||
'status' => $f->status ?? 'planned',
|
||||
'name' => $f->name ?? 'Elemento',
|
||||
'progress' => $f->progress,
|
||||
'status' => $f->status ?? 'planned',
|
||||
'responsible' => $f->responsible,
|
||||
'template_id' => $f->template_id,
|
||||
],
|
||||
])->values()->toArray();
|
||||
|
||||
return [
|
||||
'id' => $layer->id,
|
||||
'color' => $color,
|
||||
'id' => $layer->id,
|
||||
'color' => $color,
|
||||
'geojson' => [
|
||||
'type' => 'FeatureCollection',
|
||||
'type' => 'FeatureCollection',
|
||||
'features' => $features,
|
||||
'style' => ['color' => $color],
|
||||
'style' => ['color' => $color],
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -101,8 +111,8 @@ class LayerManager extends Component
|
||||
{
|
||||
$this->layers->loadMissing('features');
|
||||
$this->dispatch('initialLayersData', [
|
||||
'layers' => $this->layers->map(fn($l) => $this->buildLayerPayload($l)),
|
||||
'visibleLayers' => $this->visibleLayers,
|
||||
'layers' => $this->layers->map(fn ($l) => $this->buildLayerPayload($l)),
|
||||
'visibleLayers' => $this->visibleLayers,
|
||||
'selectedLayerId' => $this->selectedLayer?->id,
|
||||
]);
|
||||
}
|
||||
@@ -113,6 +123,7 @@ class LayerManager extends Component
|
||||
{
|
||||
if ($this->selectedLayer && $this->selectedLayer->id == $layerId) {
|
||||
$this->dispatch('notify', 'No puedes ocultar la capa que estás editando');
|
||||
|
||||
return;
|
||||
}
|
||||
if (in_array($layerId, $this->visibleLayers)) {
|
||||
@@ -128,9 +139,11 @@ class LayerManager extends Component
|
||||
public function selectLayer($layerId)
|
||||
{
|
||||
$this->selectedLayer = Layer::with('features')->find($layerId);
|
||||
if (!$this->selectedLayer) return;
|
||||
if (! $this->selectedLayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array($layerId, $this->visibleLayers)) {
|
||||
if (! in_array($layerId, $this->visibleLayers)) {
|
||||
$this->visibleLayers[] = $layerId;
|
||||
$this->dispatch('visibilityChanged', $this->visibleLayers);
|
||||
}
|
||||
@@ -139,9 +152,9 @@ class LayerManager extends Component
|
||||
$this->dispatch('layerSelectedForEdit', [
|
||||
'layerId' => $layerId,
|
||||
'geojson' => $payload['geojson'],
|
||||
'color' => $payload['color'],
|
||||
'color' => $payload['color'],
|
||||
]);
|
||||
$this->dispatch('notify', 'Editando: ' . $this->selectedLayer->name);
|
||||
$this->dispatch('notify', 'Editando: '.$this->selectedLayer->name);
|
||||
}
|
||||
|
||||
// ── Import file ───────────────────────────────────────────────────────────
|
||||
@@ -149,32 +162,35 @@ class LayerManager extends Component
|
||||
public function importFile()
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (!$user->can('upload layers')) {
|
||||
if (! $user->can('upload layers')) {
|
||||
$this->dispatch('notify', 'Sin permisos para subir capas');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validate([
|
||||
'uploadFile' => 'required|file|max:51200',
|
||||
'layerName' => 'required|string|max:255',
|
||||
'layerName' => 'required|string|max:255',
|
||||
'layerColor' => 'nullable|string|size:7',
|
||||
]);
|
||||
|
||||
$ext = strtolower($this->uploadFile->getClientOriginalExtension());
|
||||
$ext = strtolower($this->uploadFile->getClientOriginalExtension());
|
||||
$allowed = ['geojson', 'json', 'kmz', 'kml', 'shp', 'dwg', 'zip'];
|
||||
if (!in_array($ext, $allowed)) {
|
||||
$this->dispatch('notify', 'Extensión no permitida. Válidas: ' . implode(', ', $allowed));
|
||||
if (! in_array($ext, $allowed)) {
|
||||
$this->dispatch('notify', 'Extensión no permitida. Válidas: '.implode(', ', $allowed));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$geojson = SpatialFileConverter::convertToGeoJson($this->uploadFile);
|
||||
if (!$geojson) {
|
||||
if (! $geojson) {
|
||||
$this->dispatch('notify', 'No se pudo convertir el archivo. Comprueba que sea GeoJSON, KML o Shapefile válido.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$layerColor = $this->layerColor ?: '#3b82f6';
|
||||
$layerName = $this->layerName;
|
||||
$layerName = $this->layerName;
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($geojson, $layerColor, $layerName, $user) {
|
||||
@@ -183,28 +199,30 @@ class LayerManager extends Component
|
||||
);
|
||||
|
||||
$layer = Layer::create([
|
||||
'project_id' => $this->project->id,
|
||||
'phase_id' => $this->phase->id,
|
||||
'name' => $layerName,
|
||||
'color' => $layerColor,
|
||||
'project_id' => $this->project->id,
|
||||
'phase_id' => $this->phase->id,
|
||||
'name' => $layerName,
|
||||
'color' => $layerColor,
|
||||
'original_file' => $path,
|
||||
'uploaded_by' => $user->id,
|
||||
'uploaded_by' => $user->id,
|
||||
]);
|
||||
|
||||
$idx = 0;
|
||||
foreach ($geojson['features'] ?? [] as $fd) {
|
||||
$idx++;
|
||||
$name = trim($fd['properties']['name'] ?? '');
|
||||
if ($name === '') $name = $layerName . ' — Elemento ' . $idx;
|
||||
if ($name === '') {
|
||||
$name = $layerName.' — Elemento '.$idx;
|
||||
}
|
||||
|
||||
Feature::create([
|
||||
'layer_id' => $layer->id,
|
||||
'name' => $name,
|
||||
'geometry' => $fd['geometry'],
|
||||
'properties' => $fd['properties'] ?? [],
|
||||
'layer_id' => $layer->id,
|
||||
'name' => $name,
|
||||
'geometry' => $fd['geometry'],
|
||||
'properties' => $fd['properties'] ?? [],
|
||||
'template_id' => $fd['properties']['template_id'] ?? null,
|
||||
'progress' => $fd['properties']['progress'] ?? 0,
|
||||
'status' => in_array($fd['properties']['status'] ?? '', Feature::STATUSES)
|
||||
'progress' => $fd['properties']['progress'] ?? 0,
|
||||
'status' => in_array($fd['properties']['status'] ?? '', Feature::STATUSES)
|
||||
? $fd['properties']['status']
|
||||
: 'planned',
|
||||
'responsible' => $fd['properties']['responsible'] ?? null,
|
||||
@@ -214,7 +232,8 @@ class LayerManager extends Component
|
||||
$this->visibleLayers[] = $layer->id;
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
$this->dispatch('notify', 'Error al importar: ' . $e->getMessage());
|
||||
$this->dispatch('notify', 'Error al importar: '.$e->getMessage());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -229,18 +248,19 @@ class LayerManager extends Component
|
||||
public function createEmptyLayer()
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (!$user->can('upload layers')) {
|
||||
if (! $user->can('upload layers')) {
|
||||
$this->dispatch('notify', 'Sin permisos para crear capas');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$layer = Layer::create([
|
||||
'project_id' => $this->project->id,
|
||||
'phase_id' => $this->phase->id,
|
||||
'name' => $this->layerName ?: 'Nueva capa',
|
||||
'color' => $this->layerColor ?: '#3b82f6',
|
||||
'project_id' => $this->project->id,
|
||||
'phase_id' => $this->phase->id,
|
||||
'name' => $this->layerName ?: 'Nueva capa',
|
||||
'color' => $this->layerColor ?: '#3b82f6',
|
||||
'original_file' => null,
|
||||
'uploaded_by' => $user->id,
|
||||
'uploaded_by' => $user->id,
|
||||
]);
|
||||
|
||||
$this->loadLayers();
|
||||
@@ -255,18 +275,20 @@ class LayerManager extends Component
|
||||
#[On('save-manual-geojson')]
|
||||
public function saveManualGeojson($geojsonString)
|
||||
{
|
||||
if (!$this->selectedLayer) {
|
||||
if (! $this->selectedLayer) {
|
||||
$this->dispatch('notify', 'No hay capa seleccionada');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$geojson = json_decode($geojsonString, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE || !isset($geojson['features'])) {
|
||||
if (json_last_error() !== JSON_ERROR_NONE || ! isset($geojson['features'])) {
|
||||
$this->dispatch('notify', 'GeoJSON inválido');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$layerId = $this->selectedLayer->id;
|
||||
$layerId = $this->selectedLayer->id;
|
||||
$layerName = $this->selectedLayer->name;
|
||||
|
||||
try {
|
||||
@@ -278,16 +300,18 @@ class LayerManager extends Component
|
||||
foreach ($geojson['features'] as $fd) {
|
||||
$idx++;
|
||||
$name = trim($fd['properties']['name'] ?? '');
|
||||
if ($name === '') $name = $layerName . ' — Elemento ' . $idx;
|
||||
if ($name === '') {
|
||||
$name = $layerName.' — Elemento '.$idx;
|
||||
}
|
||||
|
||||
Feature::create([
|
||||
'layer_id' => $layerId,
|
||||
'name' => $name,
|
||||
'geometry' => $fd['geometry'],
|
||||
'properties' => $fd['properties'] ?? [],
|
||||
'layer_id' => $layerId,
|
||||
'name' => $name,
|
||||
'geometry' => $fd['geometry'],
|
||||
'properties' => $fd['properties'] ?? [],
|
||||
'template_id' => $fd['properties']['template_id'] ?? null,
|
||||
'progress' => $fd['properties']['progress'] ?? 0,
|
||||
'status' => in_array($fd['properties']['status'] ?? '', Feature::STATUSES)
|
||||
'progress' => $fd['properties']['progress'] ?? 0,
|
||||
'status' => in_array($fd['properties']['status'] ?? '', Feature::STATUSES)
|
||||
? $fd['properties']['status']
|
||||
: 'planned',
|
||||
'responsible' => $fd['properties']['responsible'] ?? null,
|
||||
@@ -295,14 +319,15 @@ class LayerManager extends Component
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
$this->dispatch('notify', 'Error al guardar: ' . $e->getMessage());
|
||||
$this->dispatch('notify', 'Error al guardar: '.$e->getMessage());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->loadLayers();
|
||||
$this->selectLayer($this->selectedLayer->id);
|
||||
$this->emitInitialLayersData();
|
||||
$this->dispatch('notify', count($geojson['features']) . ' elementos guardados');
|
||||
$this->dispatch('notify', count($geojson['features']).' elementos guardados');
|
||||
}
|
||||
|
||||
// ── Delete layer ──────────────────────────────────────────────────────────
|
||||
@@ -310,13 +335,19 @@ class LayerManager extends Component
|
||||
public function deleteLayer($layerId)
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (!$user->can('delete layers')) abort(403);
|
||||
if (! $user->can('delete layers')) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
// Verify it belongs to this phase (prevents cross-project deletion)
|
||||
$layer = Layer::where('id', $layerId)->where('phase_id', $this->phase->id)->first();
|
||||
if (!$layer) return;
|
||||
if (! $layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($layer->original_file) Storage::disk('public')->delete($layer->original_file);
|
||||
if ($layer->original_file) {
|
||||
Storage::disk('public')->delete($layer->original_file);
|
||||
}
|
||||
$layer->features()->delete();
|
||||
$layer->delete();
|
||||
|
||||
@@ -337,25 +368,27 @@ class LayerManager extends Component
|
||||
->where('id', $layerId)
|
||||
->where('phase_id', $this->phase->id)
|
||||
->first();
|
||||
if (!$layer) return;
|
||||
if (! $layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fc = [
|
||||
'type' => 'FeatureCollection',
|
||||
'name' => $layer->name,
|
||||
'features' => $layer->features->map(fn($f) => [
|
||||
'type' => 'Feature',
|
||||
'geometry' => $f->geometry,
|
||||
'type' => 'FeatureCollection',
|
||||
'name' => $layer->name,
|
||||
'features' => $layer->features->map(fn ($f) => [
|
||||
'type' => 'Feature',
|
||||
'geometry' => $f->geometry,
|
||||
'properties' => array_merge($f->properties ?? [], [
|
||||
'name' => $f->name,
|
||||
'progress' => $f->progress,
|
||||
'status' => $f->status,
|
||||
'name' => $f->name,
|
||||
'progress' => $f->progress,
|
||||
'status' => $f->status,
|
||||
'responsible' => $f->responsible,
|
||||
'template_id' => $f->template_id,
|
||||
]),
|
||||
])->values()->toArray(),
|
||||
];
|
||||
|
||||
$filename = preg_replace('/[^a-z0-9_\-]/i', '_', $layer->name) . '.geojson';
|
||||
$filename = preg_replace('/[^a-z0-9_\-]/i', '_', $layer->name).'.geojson';
|
||||
|
||||
return response()->streamDownload(function () use ($fc) {
|
||||
echo json_encode($fc, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
@@ -367,7 +400,9 @@ class LayerManager extends Component
|
||||
public function batchAssign($layerId)
|
||||
{
|
||||
$layer = Layer::where('id', $layerId)->where('phase_id', $this->phase->id)->first();
|
||||
if (!$layer) return;
|
||||
if (! $layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [];
|
||||
if ($this->batchStatus && in_array($this->batchStatus, Feature::STATUSES)) {
|
||||
@@ -378,6 +413,7 @@ class LayerManager extends Component
|
||||
}
|
||||
if (empty($data)) {
|
||||
$this->dispatch('notify', 'Selecciona un estado o template para asignar');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user