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:
@@ -18,29 +18,35 @@ class GlobalTemplateManager extends Component
|
||||
public $templates;
|
||||
|
||||
public $editingTemplate = null;
|
||||
|
||||
public $showForm = false;
|
||||
|
||||
public $form = [
|
||||
'name' => '',
|
||||
'name' => '',
|
||||
'description' => '',
|
||||
'fields' => [],
|
||||
'fields' => [],
|
||||
];
|
||||
|
||||
// ── Importar desde CSV/Excel ───────────────────────────────────────────
|
||||
public $showImportFileModal = false;
|
||||
public $importFile = null;
|
||||
public $importPreviewFields = [];
|
||||
public $importTemplateName = '';
|
||||
public $importError = '';
|
||||
public $showImportFileModal = false;
|
||||
|
||||
public $importFile = null;
|
||||
|
||||
public $importPreviewFields = [];
|
||||
|
||||
public $importTemplateName = '';
|
||||
|
||||
public $importError = '';
|
||||
|
||||
public $fieldTypes = [
|
||||
'text' => 'Texto corto',
|
||||
'textarea' => 'Texto largo',
|
||||
'integer' => 'Número entero',
|
||||
'decimal' => 'Número decimal',
|
||||
'text' => 'Texto corto',
|
||||
'textarea' => 'Texto largo',
|
||||
'integer' => 'Número entero',
|
||||
'decimal' => 'Número decimal',
|
||||
'percentage' => 'Porcentaje (0-100)',
|
||||
'boolean' => 'Sí/No (checkbox)',
|
||||
'date' => 'Fecha',
|
||||
'select' => 'Lista desplegable',
|
||||
'boolean' => 'Sí/No (checkbox)',
|
||||
'date' => 'Fecha',
|
||||
'select' => 'Lista desplegable',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
@@ -66,9 +72,9 @@ class GlobalTemplateManager extends Component
|
||||
{
|
||||
$template = InspectionTemplate::findOrFail($id);
|
||||
$this->form = [
|
||||
'name' => $template->name,
|
||||
'name' => $template->name,
|
||||
'description' => $template->description ?? '',
|
||||
'fields' => $template->fields ?? [],
|
||||
'fields' => $template->fields ?? [],
|
||||
];
|
||||
$this->editingTemplate = $id;
|
||||
$this->showForm = true;
|
||||
@@ -83,9 +89,9 @@ class GlobalTemplateManager extends Component
|
||||
public function resetForm()
|
||||
{
|
||||
$this->form = [
|
||||
'name' => '',
|
||||
'name' => '',
|
||||
'description' => '',
|
||||
'fields' => [],
|
||||
'fields' => [],
|
||||
];
|
||||
$this->editingTemplate = null;
|
||||
}
|
||||
@@ -93,17 +99,17 @@ class GlobalTemplateManager extends Component
|
||||
public function addField()
|
||||
{
|
||||
$this->form['fields'][] = [
|
||||
'group' => '',
|
||||
'name' => '',
|
||||
'label' => '',
|
||||
'group' => '',
|
||||
'name' => '',
|
||||
'label' => '',
|
||||
'question' => '',
|
||||
'type' => 'text',
|
||||
'options' => '',
|
||||
'type' => 'text',
|
||||
'options' => '',
|
||||
'required' => false,
|
||||
'min' => null,
|
||||
'max' => null,
|
||||
'step' => null,
|
||||
'help' => '',
|
||||
'min' => null,
|
||||
'max' => null,
|
||||
'step' => null,
|
||||
'help' => '',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -116,15 +122,15 @@ class GlobalTemplateManager extends Component
|
||||
public function saveTemplate()
|
||||
{
|
||||
$this->validate([
|
||||
'form.name' => 'required|string|max:255',
|
||||
'form.name' => 'required|string|max:255',
|
||||
'form.fields' => 'array',
|
||||
]);
|
||||
|
||||
$data = [
|
||||
'name' => $this->form['name'],
|
||||
'name' => $this->form['name'],
|
||||
'description' => $this->form['description'],
|
||||
'project_id' => null,
|
||||
'fields' => array_values($this->form['fields']),
|
||||
'project_id' => null,
|
||||
'fields' => array_values($this->form['fields']),
|
||||
];
|
||||
|
||||
if ($this->editingTemplate) {
|
||||
@@ -153,10 +159,10 @@ class GlobalTemplateManager extends Component
|
||||
|
||||
public function openImportFileModal()
|
||||
{
|
||||
$this->importFile = null;
|
||||
$this->importFile = null;
|
||||
$this->importPreviewFields = [];
|
||||
$this->importTemplateName = '';
|
||||
$this->importError = '';
|
||||
$this->importTemplateName = '';
|
||||
$this->importError = '';
|
||||
$this->showImportFileModal = true;
|
||||
}
|
||||
|
||||
@@ -164,31 +170,34 @@ class GlobalTemplateManager extends Component
|
||||
{
|
||||
$headers = ['Content-Type' => 'text/csv'];
|
||||
$csv = "\xEF\xBB\xBF" // BOM UTF-8 (para que Excel respete los acentos)
|
||||
. "group,name,label,question,type,required,options,min,max,step,help\n"
|
||||
. "Dimensiones,altura,Altura (m),¿Cumple la altura de proyecto?,decimal,1,,0,100,0.1,Medir con flexómetro\n"
|
||||
. "Dimensiones,material,Material,,select,1,Hormigón|Acero|Madera,,,,\n"
|
||||
. "Acabados,ok,¿Acabado correcto?,,boolean,1,,,,,\n";
|
||||
return response()->streamDownload(fn () => print($csv), 'plantilla_ejemplo.csv', $headers);
|
||||
."group,name,label,question,type,required,options,min,max,step,help\n"
|
||||
."Dimensiones,altura,Altura (m),¿Cumple la altura de proyecto?,decimal,1,,0,100,0.1,Medir con flexómetro\n"
|
||||
."Dimensiones,material,Material,,select,1,Hormigón|Acero|Madera,,,,\n"
|
||||
."Acabados,ok,¿Acabado correcto?,,boolean,1,,,,,\n";
|
||||
|
||||
return response()->streamDownload(fn () => print ($csv), 'plantilla_ejemplo.csv', $headers);
|
||||
}
|
||||
|
||||
public function parseImportFile()
|
||||
{
|
||||
$this->importError = '';
|
||||
$this->validate([
|
||||
'importFile' => 'required|file|mimes:csv,txt,xlsx,xls|max:5120',
|
||||
'importFile' => 'required|file|mimes:csv,txt,xlsx,xls|max:5120',
|
||||
'importTemplateName' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
try {
|
||||
$rows = $this->readFileRows();
|
||||
} catch (\Throwable $e) {
|
||||
$this->importError = 'No se pudo leer el archivo: ' . $e->getMessage();
|
||||
$this->importError = 'No se pudo leer el archivo: '.$e->getMessage();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->parseRows($rows);
|
||||
if (empty($fields)) {
|
||||
$this->importError = 'No se encontraron filas válidas.';
|
||||
|
||||
return;
|
||||
}
|
||||
$this->importPreviewFields = $fields;
|
||||
@@ -196,19 +205,21 @@ class GlobalTemplateManager extends Component
|
||||
|
||||
public function confirmImportFile()
|
||||
{
|
||||
if (empty($this->importPreviewFields) || empty($this->importTemplateName)) return;
|
||||
if (empty($this->importPreviewFields) || empty($this->importTemplateName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
InspectionTemplate::create([
|
||||
'name' => $this->importTemplateName,
|
||||
'name' => $this->importTemplateName,
|
||||
'description' => 'Importado desde archivo',
|
||||
'project_id' => null,
|
||||
'fields' => array_values($this->importPreviewFields),
|
||||
'project_id' => null,
|
||||
'fields' => array_values($this->importPreviewFields),
|
||||
]);
|
||||
|
||||
$this->showImportFileModal = false;
|
||||
$this->importPreviewFields = [];
|
||||
$this->importTemplateName = '';
|
||||
$this->importFile = null;
|
||||
$this->importTemplateName = '';
|
||||
$this->importFile = null;
|
||||
$this->loadTemplates();
|
||||
$this->dispatch('templates-changed');
|
||||
$this->dispatch('notify', 'Plantilla importada');
|
||||
@@ -216,7 +227,7 @@ class GlobalTemplateManager extends Component
|
||||
|
||||
private function readFileRows(): array
|
||||
{
|
||||
$ext = strtolower($this->importFile->getClientOriginalExtension());
|
||||
$ext = strtolower($this->importFile->getClientOriginalExtension());
|
||||
$path = $this->importFile->getRealPath();
|
||||
|
||||
// Fila no vacía = tiene al menos una celda con contenido (no filtramos por
|
||||
@@ -226,21 +237,27 @@ class GlobalTemplateManager extends Component
|
||||
|
||||
if ($ext === 'xlsx' || $ext === 'xls') {
|
||||
$spreadsheet = IOFactory::load($path);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$rows = $sheet->toArray(null, true, true, false);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$rows = $sheet->toArray(null, true, true, false);
|
||||
array_shift($rows);
|
||||
|
||||
return array_values(array_filter($rows, $notEmpty));
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
$rows = [];
|
||||
$handle = fopen($path, 'r');
|
||||
$bom = fread($handle, 3);
|
||||
if ($bom !== "\xEF\xBB\xBF") rewind($handle);
|
||||
if ($bom !== "\xEF\xBB\xBF") {
|
||||
rewind($handle);
|
||||
}
|
||||
fgetcsv($handle);
|
||||
while (($row = fgetcsv($handle)) !== false) {
|
||||
if ($notEmpty($row)) $rows[] = $row;
|
||||
if ($notEmpty($row)) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
fclose($handle);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
@@ -250,24 +267,27 @@ class GlobalTemplateManager extends Component
|
||||
// group, name, label, question, type, required, options, min, max, step, help
|
||||
$fields = [];
|
||||
foreach ($rows as $row) {
|
||||
$row = array_values((array) $row);
|
||||
$row = array_values((array) $row);
|
||||
$rawName = trim($row[1] ?? '');
|
||||
if ($rawName === '') continue;
|
||||
if ($rawName === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fields[] = [
|
||||
'group' => trim($row[0] ?? ''),
|
||||
'name' => $this->slugify($rawName),
|
||||
'label' => trim($row[2] ?? '') ?: $rawName,
|
||||
'group' => trim($row[0] ?? ''),
|
||||
'name' => $this->slugify($rawName),
|
||||
'label' => trim($row[2] ?? '') ?: $rawName,
|
||||
'question' => trim($row[3] ?? ''),
|
||||
'type' => $this->normalizeType($row[4] ?? 'text'),
|
||||
'type' => $this->normalizeType($row[4] ?? 'text'),
|
||||
'required' => in_array(strtolower(trim($row[5] ?? '0')), ['1', 'si', 'sí', 'yes', 'true']),
|
||||
'options' => trim($row[6] ?? ''),
|
||||
'min' => ($row[7] ?? '') !== '' ? $row[7] : null,
|
||||
'max' => ($row[8] ?? '') !== '' ? $row[8] : null,
|
||||
'step' => ($row[9] ?? '') !== '' ? $row[9] : null,
|
||||
'help' => trim($row[10] ?? ''),
|
||||
'options' => trim($row[6] ?? ''),
|
||||
'min' => ($row[7] ?? '') !== '' ? $row[7] : null,
|
||||
'max' => ($row[8] ?? '') !== '' ? $row[8] : null,
|
||||
'step' => ($row[9] ?? '') !== '' ? $row[9] : null,
|
||||
'help' => trim($row[10] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
@@ -276,6 +296,7 @@ class GlobalTemplateManager extends Component
|
||||
$str = mb_strtolower(trim($str));
|
||||
$str = preg_replace('/\s+/', '_', $str);
|
||||
$str = preg_replace('/[^a-z0-9_]/i', '', $str);
|
||||
|
||||
return trim($str, '_') ?: 'campo';
|
||||
}
|
||||
|
||||
@@ -291,6 +312,7 @@ class GlobalTemplateManager extends Component
|
||||
'date' => 'date', 'fecha' => 'date',
|
||||
'select' => 'select', 'lista' => 'select', 'dropdown' => 'select', 'opciones' => 'select',
|
||||
];
|
||||
|
||||
return $map[strtolower(trim($type))] ?? 'text';
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ class InspectionTemplatesTable extends DataTableComponent
|
||||
public function configure(): void
|
||||
{
|
||||
$this->setPrimaryKey('id')
|
||||
->setDefaultSort('inspection_templates.name', 'asc')
|
||||
->setSortingPillsEnabled(false)
|
||||
->setSecondaryHeaderEnabled()
|
||||
->setAdditionalSelects([
|
||||
'inspection_templates.id as id',
|
||||
'inspection_templates.fields as fields',
|
||||
]);
|
||||
->setDefaultSort('inspection_templates.name', 'asc')
|
||||
->setSortingPillsEnabled(false)
|
||||
->setSecondaryHeaderEnabled()
|
||||
->setAdditionalSelects([
|
||||
'inspection_templates.id as id',
|
||||
'inspection_templates.fields as fields',
|
||||
]);
|
||||
}
|
||||
|
||||
/** Refrescar cuando el manager crea/edita/borra. */
|
||||
@@ -46,47 +46,46 @@ class InspectionTemplatesTable extends DataTableComponent
|
||||
{
|
||||
return [
|
||||
Column::make('Plantilla', 'name')
|
||||
->sortable()->searchable()
|
||||
->secondaryHeaderFilter('name')
|
||||
->format(fn ($value) => '<span class="font-medium">' . e($value) . '</span>')
|
||||
->html(),
|
||||
->sortable()->searchable()
|
||||
->secondaryHeaderFilter('name')
|
||||
->format(fn ($value) => '<span class="font-medium">'.e($value).'</span>')
|
||||
->html(),
|
||||
|
||||
Column::make('Descripción', 'description')
|
||||
->searchable()
|
||||
->secondaryHeaderFilter('description')
|
||||
->format(fn ($value) => $value
|
||||
? '<span class="text-sm text-base-content/70">' . e($value) . '</span>'
|
||||
: '<span class="text-base-content/30 text-xs">—</span>')
|
||||
->html(),
|
||||
->searchable()
|
||||
->secondaryHeaderFilter('description')
|
||||
->format(fn ($value) => $value
|
||||
? '<span class="text-sm text-base-content/70">'.e($value).'</span>'
|
||||
: '<span class="text-base-content/30 text-xs">—</span>')
|
||||
->html(),
|
||||
|
||||
Column::make('Campos')
|
||||
->label(fn ($row) =>
|
||||
'<span class="badge badge-ghost badge-sm">' . count($row->fields ?? []) . '</span>')
|
||||
->html(),
|
||||
->label(fn ($row) => '<span class="badge badge-ghost badge-sm">'.count($row->fields ?? []).'</span>')
|
||||
->html(),
|
||||
|
||||
Column::make('Proyectos')
|
||||
->secondaryHeaderFilter('usage')
|
||||
->label(function ($row) {
|
||||
$n = (int) ($row->projects_count ?? 0);
|
||||
$cls = $n > 0 ? 'badge-info' : 'badge-ghost';
|
||||
return '<span class="badge ' . $cls . ' badge-sm">' . $n . '</span>';
|
||||
})
|
||||
->html(),
|
||||
->secondaryHeaderFilter('usage')
|
||||
->label(function ($row) {
|
||||
$n = (int) ($row->projects_count ?? 0);
|
||||
$cls = $n > 0 ? 'badge-info' : 'badge-ghost';
|
||||
|
||||
return '<span class="badge '.$cls.' badge-sm">'.$n.'</span>';
|
||||
})
|
||||
->html(),
|
||||
|
||||
Column::make('Acciones')
|
||||
->label(fn ($row) =>
|
||||
'<div class="flex justify-end gap-1">
|
||||
<button wire:click="$dispatch(\'template-edit\', { id: ' . $row->id . ' })"
|
||||
->label(fn ($row) => '<div class="flex justify-end gap-1">
|
||||
<button wire:click="$dispatch(\'template-edit\', { id: '.$row->id.' })"
|
||||
class="btn btn-xs btn-ghost" title="Editar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
|
||||
</button>
|
||||
<button wire:click="$dispatch(\'template-delete\', { id: ' . $row->id . ' })"
|
||||
wire:confirm="¿Eliminar la plantilla \'' . e($row->name) . '\'? Esta acción no se puede deshacer."
|
||||
<button wire:click="$dispatch(\'template-delete\', { id: '.$row->id.' })"
|
||||
wire:confirm="¿Eliminar la plantilla \''.e($row->name).'\'? Esta acción no se puede deshacer."
|
||||
class="btn btn-xs btn-error btn-outline" title="Eliminar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
|
||||
</button>
|
||||
</div>')
|
||||
->html(),
|
||||
->html(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -95,17 +94,21 @@ class InspectionTemplatesTable extends DataTableComponent
|
||||
return [
|
||||
TextFilter::make('Plantilla', 'name')
|
||||
->config(['placeholder' => 'Buscar nombre…'])
|
||||
->filter(fn (Builder $q, string $v) => $q->where('inspection_templates.name', 'like', '%' . $v . '%')),
|
||||
->filter(fn (Builder $q, string $v) => $q->where('inspection_templates.name', 'like', '%'.$v.'%')),
|
||||
|
||||
TextFilter::make('Descripción', 'description')
|
||||
->config(['placeholder' => 'Buscar descripción…'])
|
||||
->filter(fn (Builder $q, string $v) => $q->where('inspection_templates.description', 'like', '%' . $v . '%')),
|
||||
->filter(fn (Builder $q, string $v) => $q->where('inspection_templates.description', 'like', '%'.$v.'%')),
|
||||
|
||||
SelectFilter::make('Uso', 'usage')
|
||||
->options(['' => 'Todos', 'used' => 'En uso (≥1 proyecto)', 'unused' => 'Sin uso'])
|
||||
->filter(function (Builder $q, string $v) {
|
||||
if ($v === 'used') $q->has('projects');
|
||||
if ($v === 'unused') $q->doesntHave('projects');
|
||||
if ($v === 'used') {
|
||||
$q->has('projects');
|
||||
}
|
||||
if ($v === 'unused') {
|
||||
$q->doesntHave('projects');
|
||||
}
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user