El importador de plantillas solo parseaba name/label/type/required/options/
min/max/step, dejando fuera group (sección), question (pregunta corta) y help
(ayuda) que el builder manual sí soporta.
- Nuevo orden de columnas: group,name,label,question,type,required,options,min,max,step,help
- parseRows() rellena las 11 claves (coincide con addField()).
- readFileRows() ya no descarta filas por la 1ª columna (ahora es `group`, que
puede ir vacía); filtra por "fila con algún contenido" y parseRows descarta
las que no tengan `name`.
- CSV de ejemplo actualizado (con BOM UTF-8) y con filas que muestran group/
question/help/select.
- Modal de importación: texto de columnas y tabla de preview muestran Grupo y
Pregunta.
Tests: nuevo test csv_import_parses_group_question_and_help. Suite 95 passing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bug: kmlToGeoJson usaba xpath //kml:Placemark exigiendo el namespace
http://www.opengis.net/kml/2.2. KMLs con xmlns de Google Earth
(http://earth.google.com/kml/2.1) o sin namespace daban 0 placemarks →
la capa se creaba pero SIN elementos.
Fix:
- Todos los xpath usan local-name() (namespace-agnostic).
- parseKmlGeometry navega Point/LineString/Polygon/MultiGeometry sin
depender del prefijo del namespace.
- Soporte KMZ: descomprime el zip y parsea el .kml interno (habitualmente doc.kml).
- Nuevo helper kmlChild() para leer <name>/<description> tolerante al ns.
Tests: SpatialFileConverterTest cubre los tres KMLs (opengis 2.2, google 2.1,
sin xmlns). Suite 94 passing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Nueva InspectionTemplatesTable (DataTableComponent): columnas Plantilla,
Descripción, Campos, Proyectos (uso) y Acciones; filtros en cabecera por nombre,
descripción y uso (en uso / sin uso). Gateada por manage templates.
- GlobalTemplateManager: el listado HTML se sustituye por la tabla embebida.
Acciones Editar/Borrar viajan por eventos (template-edit/template-delete);
el manager emite templates-changed al guardar/borrar/importar para que la tabla
se refresque (#[On('templates-changed')]).
Tests: GlobalTemplatesTest amplía con tabla (render + permiso, evento edit). Suite 91 passing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Migración: tabla pivot inspection_template_project; drop de phase_id (asociación
a fase descartada); project_id se mantiene en inspection_templates por compat.
Migra automáticamente las plantillas existentes (project_id → pivot).
- Modelos: Project::inspectionTemplates() ↔ InspectionTemplate::projects() (BTM);
retirada la relación phase() de InspectionTemplate.
- GlobalTemplateManager (nuevo, ruta /inspection-templates, permiso manage templates):
catálogo único global, CRUD + import CSV; sin asociación a fase ni a proyecto.
- ProjectTemplatesPicker (nuevo, ruta projects.templates, permiso edit projects):
lista global con checkbox para asignar/desasignar plantillas al proyecto.
- ProjectMap: el selector de plantillas del mapa lee SOLO las asignadas al proyecto
vía pivot. API móvil (bundle + /templates) adaptado al pivot.
- Eliminado el viejo TemplateManager por-proyecto, su vista wrapper y la tabla
ImportTemplatesTable (ya no aplica: todas son globales).
- Acceso "Plantillas" añadido al menú principal (gate manage templates).
Tests: GlobalTemplatesTest (4). Suite 89 passing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sustituye el modal (proyecto + checkboxes) por una tabla Rappasoft:
- ImportTemplatesTable lista todas las plantillas de proyectos accesibles (excluye el
destino), con filtros en cabecera (nombre, proyecto) y checkbox de selección (bulk).
- Acción masiva "Importar seleccionadas" copia al proyecto destino (dedupe por nombre)
y avisa a TemplateManager por evento (templates-imported) para refrescar/cerrar.
- TemplateManager: eliminada la lógica vieja (importProjectId/importableTemplates/
selectedImportTemplateIds/importFromProject); el modal embebe la tabla.
Tests: TemplateImportTest adaptado (selección+import, excluye destino). Suite 92 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Parte 2 del bloque de plantillas/inspecciones:
- Formulario de inspección (mapa): campos agrupados por sección, muestra la
"pregunta" del campo (con la etiqueta como subtexto) y la ayuda; al final,
apartado de Comentarios (notes) y subida de Fotos (media en la inspección).
- ProjectMap: WithFileUploads + inspectionPhotos; saveInspection adjunta las fotos
como media; Inspection gana relación media().
- Visor de inspección: datos agrupados por sección con pregunta/valor, comentarios
y galería de fotos.
Tests: InspectionFormTest (registra inspección con comentarios + foto). Suite 91 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder de plantillas (parte 1 del bloque):
- Cada campo admite "Grupo / sección" (título), "Pregunta / revisar" (texto corto,
tras la etiqueta) y "Comentarios / ayuda" (texto largo, al final).
- Plantillas híbridas: toggle "Global" → project_id null; el listado de un proyecto
muestra sus plantillas + las globales (badge "Global").
Tests: TemplateCreationTest (grupo/pregunta/ayuda persisten; global sin proyecto y
visible en otros proyectos). Suite 90 passing.
Pendiente (parte 2): adaptar el formulario de inspección (grupos, comentarios, fotos)
y el visor de inspección.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- bundle: mapFeature incluye feature_type_id e is_active; nuevo array feature_types
(catálogo) para que el móvil resuelva nombres/colores.
- /sync feature.update acepta is_active y feature_type_id (last-write-wins).
- openapi.yaml: bundle documenta feature_types.
Tests: MobileApiTest cubre feature.update con los nuevos campos + bundle. Suite 88 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Migración: tabla feature_types + columnas feature_type_id (FK) e is_active en features.
Modelo FeatureType; Feature gana featureType() + casts is_active.
- Catálogo de tipos (FeatureTypeManager, ruta /feature-types): CRUD con modal
(nombre, descripción, color), contador de usos; gateado por edit layers.
- Editor de elementos del proyecto (FeatureManager, ruta projects.features):
tabla Rappasoft ProjectFeaturesTable (filtros nombre/capa/tipo en cabecera),
toggle activo/inactivo y borrado individual; modal para editar nombre/tipo/activo.
- Acceso: botón "Elementos" en el dashboard del proyecto (edit layers) y enlace a
"Tipos de elemento" desde el editor.
Tests: FeatureManagementTest (6). Suite 87 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El TemplateManager ya implementaba ambos flujos pero la vista no los exponía.
Añadidos a la cabecera los botones "Importar de proyecto" e "Importar CSV/Excel" y
sus modales:
- CSV/Excel: nombre + fichero, vista previa de campos detectados, crear; con enlace
para descargar CSV de ejemplo.
- Otro proyecto: selección de proyecto accesible + checklist de plantillas a importar.
Tests: TemplateImportTest (importar de proyecto y desde CSV). Suite 81 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
resources/views/projects/templates.blade.php usaba `<livewire/template-manager>`
(sintaxis inválida; tras la reorganización el alias es `inspections.template-manager`)
y un botón suelto con `$emit` (Livewire 2, fuera de componente) que no hacía nada.
Ahora embebe correctamente <livewire:inspections.template-manager> (que ya trae su
botón "Nuevo" + formulario funcional) y se elimina el botón roto.
Tests: TemplateCreationTest (la página embebe el componente; crear plantilla funciona).
Suite 79 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
No había forma de llegar a projects.templates. Añadidos dos accesos, gateados con
@can('edit projects') (mismo permiso que protege la ruta):
- Dashboard del proyecto: botón "Plantillas" junto a "Mapa".
- Mapa: enlace "Gestionar plantillas" junto al selector de plantilla.
Test: TemplatesAccessTest (403 sin permiso, 200 con edit projects). Suite 77 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Usa la cabecera secundaria de Rappasoft (setSecondaryHeaderEnabled + secondaryHeaderFilter):
- Elementos: Elemento (texto), Capa (select), Fase (select).
- Inspecciones: Fecha (date), Elemento (texto), Resultado (select), Usuario (select);
Plantilla sin filtro.
Tests: MapTablesTest amplía con filtro de capa funcional. Suite 75 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- FeatureTable e InspectionTable (DataTableComponent) sustituyen las tablas HTML
de las pestañas "Elementos" e "Inspecciones" del mapa: búsqueda, orden, filtro
(progreso) y acciones.
- Selección de elemento e "ver inspección" se comunican al ProjectMap por eventos
(map-select-feature / map-view-inspection, vía #[On]); seleccionar abre el panel
de edición y centra el mapa igual que antes.
- Las relaciones requieren sus FKs en additionalSelects (layer_id / feature_id,
template_id, user_id) porque Rappasoft no selecciona '*'.
Tests: MapTablesTest (3). Suite 74 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Sustituye el panel saturado de estadísticas por una vista "Home" centrada en lo
del usuario: mis proyectos (compacto), mis tareas pendientes, incidencias asignadas
y notificaciones recientes. Sin inundar de detalle (mejor con muchos proyectos).
- Etiqueta "Dashboard" -> "Home" en el menú y la cabecera.
- Elimina el orderByRaw FIELD() (solo MySQL) del antiguo dashboard.
- ExampleTest: ahora valida la conducta real (la raíz redirige a login).
Suite: 71 passing (0 fallos; resueltas las 2 pre-existentes de SQLite).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- app/Livewire: 34 componentes agrupados en Issues/, Projects/, Phases/,
Companies/, Users/, Admin/, Inspections/, Layers/, Media/, Common/
(Client/, Reports/, Forms/, Actions/ ya estaban). Namespaces actualizados.
- resources/views/livewire: vistas sueltas movidas a subcarpetas espejo
(companies/, users/, phases/, roles/, inspections/, media/, common/);
render() actualizado.
- Referencias actualizadas sin romper nada: rutas (FQN, nombres de ruta intactos),
tags <livewire:...>/@livewire() a alias con punto, y use de los tests.
- No tocado: Volt de Breeze (auth/profile/navigation), y el portal cliente
(user-nav/client-projects) que ya tenía referencias inconsistentes.
Verificado: 69 rutas OK, vistas compilan, suite 69 passing (solo 2 pre-existentes
sqlite). autoload regenerado con --ignore-platform-reqs (PHP 8.2).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Permiso 'assign companies' propio (antes empresas reutilizaba 'assign users');
concedido a los roles que ya tenían 'assign users'.
- ProjectUsersTable y ProjectCompaniesTable (Rappasoft): búsqueda, filtro por rol,
cambio de rol en línea y quitar; gateadas por assign users / assign companies.
- ProjectUsers/ProjectCompanies quedan como contenedor (form de asignación) que
embebe la tabla y refresca el desplegable vía eventos.
- Unificadas confirmaciones (wire:confirm) y notificaciones (dispatch notify).
Tests: ProjectAssignmentsTest (4). Suite 69 passing (solo 2 pre-existentes sqlite).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- PhaseList pasa a contenedor: botón "Agregar fase" + modal crear/editar con todos
los parámetros (nombre, descripción, orden, color, progreso, fechas previstas y
reales) y validación. Antes "Agregar fase" creaba directamente 'Nueva fase'.
- PhaseTable (Rappasoft): orden, nombre+descripción, barra de progreso, fechas,
color y acciones (editar abre el modal vía evento, actualizar progreso, eliminar);
búsqueda y ordenación. Gateado por 'manage phases' + acceso al proyecto.
Tests: PhaseManagementTest (4). Suite 65 passing (solo 2 pre-existentes sqlite).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Issue::TYPES + typeLabels() (ES) + accessors type_label/type_color; columna type
(string, default 'other') + fillable.
- IssueForm: select "Tipo de incidencia" con validación/carga/guardado.
- IssueTable: columna Tipo (badge) + SelectFilter por tipo.
- IssueDetail: badge de tipo en la cabecera.
- Sync offline: issue.create/update aceptan type; bundle (mapIssue) lo incluye.
Tests: IssuesEnhancementsTest (create muestra el campo vía HTTP, edición persiste) +
MobileApiTest (create con type). Suite 61 passing (solo 2 pre-existentes sqlite).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Campo "Idioma por defecto" al crear/editar usuario (columna locale ya existente),
como desplegable Alpine con banderas SVG reales (no emoji, que en Windows se ven
como "ES"/"GB") servidas localmente: public/images/flags/{es,gb}.svg.
- User: locale añadido a fillable. UserForm: propiedad/validación/guardado de locale.
- LanguageSwitcher de la cabecera usa las mismas banderas SVG.
- Regla CSS [x-cloak] en el layout para evitar parpadeo de desplegables Alpine.
Tests: UserLocaleTest (2) — crear/editar persisten el idioma.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Notificaciones (DB): asignación de incidencia (IssueAssigned), asignación de tarea
(IssueTaskAssigned), comentario (IssueCommented) y cambio de estado
(IssueStatusChanged) a reporter+asignado excluyendo al actor.
- Plantillas de checklist: tabla issue_checklist_templates + modelo, gestor CRUD
(IssueChecklistManager, ruta projects.issues.checklists) y "Aplicar plantilla" en
el detalle (alta masiva de tareas).
- Alertas de vencimiento: columna overdue_notified_at + scope overdue, comando
issues:notify-overdue (programado a diario) que avisa al asignado una sola vez;
badge "vencidas" en la tabla y resaltado por tarea en el detalle.
- Reporte desde el mapa: botón "Incidencia" en el panel del feature seleccionado →
formulario con feature pre-vinculado (IssueForm lee ?feature=).
Tests: IssuesEnhancementsTest (7). Suite 57 passing (solo 2 pre-existentes sqlite).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Web:
- IssueTask + IssueComment (modelos, migraciones, soft-deletes, campos de sync).
Issue gana tasks()/comments() y accessor de % de avance derivado de tareas.
- IssueDetail (página): checklist con asignado/fecha límite/progreso, hilo de
comentarios con foto por comentario, galería de fotos de la incidencia y flujo
de verificación open→in_review→resolved/closed (+reabrir) con notas.
- Creación/edición en páginas propias (IssueForm), sin modal; al guardar redirige
al detalle. Rutas projects.issues.create/edit/show.
- Listado con tabla Rappasoft (IssueTable): filtros por estado/prioridad, búsqueda,
barra de progreso y acciones por fila gateadas por permisos; IssueManager queda
como contenedor (cabecera + stats) que embebe la tabla.
- Seguridad: pertenencia al proyecto + permisos por acción (view/create/edit/delete
issues, upload/delete media) en todos los componentes.
API móvil (offline):
- /sync: issue_task.create/update y issue_comment.create (idempotente, LWW).
- /media: parent_entity issue_task / issue_comment.
- bundle + tombstones incluyen issue_tasks / issue_comments.
- openapi.yaml + MOBILE_SYNC_PROTOCOL.md actualizados.
Tests: MobileApiTest 23 passing (+5); IssuesTablePageTest (3) smoke de la tabla.
Branding: logo RTE International — MAI Group (public/images/logo-rte.png) en login
y navegación; application-logo pasa de SVG por defecto a <img>.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Milestone 5 (media):
- POST /api/v1/media — multipart upload, attaches to feature/issue/project/
phase/layer, idempotent by uuid, authz member + 'upload media'. Added
uuid+client_updated_at to media.
- Bundle now includes a 'media' array (URLs) for the project's project/feature/
issue attachments (delta-aware).
Milestone 6 (hardening + docs):
- sync_logs table/model: every applied op is logged; /sync short-circuits on a
repeated op uuid -> 'duplicate' (true idempotency for updates too, not just
creates).
- Rate limiting on login (10/min), sync (60/min), media (120/min).
- docs/openapi.yaml: OpenAPI 3 contract for the mobile team.
Tests: 18 passing (added media upload idempotency + sync_logs idempotency).
The mobile API (Milestones 1-6) is now feature-complete on the webapp side.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SyncController now handles the full mutation vocabulary:
- inspection.create (idempotent by uuid; project/layer derived from feature;
authz member + 'create inspections'; status defaults to completed).
- issue.create (idempotent; authz member + 'create issues').
- issue.update (by server id; authz member + 'edit issues'; sets resolved_at
when resolved/closed; last-write-wins conflict).
- feature.update (by server id; authz member + 'update progress'; recomputes
phase progress; last-write-wins conflict).
- Conflict detection: client_updated_at vs server updated_at → returns
'conflict' with the current server value.
Added uuid + client_updated_at to features/inspections/issues (guarded migration)
and their fillables. Tests: 16 passing (added inspection/issue/feature + conflict).
Note: 2 PRE-EXISTING test failures remain (not from this work, sqlite-only):
ExampleTest expects '/'=200 (app redirects), and the dashboard route uses MySQL
FIELD() which sqlite lacks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ProjectApiController bundle now supports incremental sync:
- ?since=<ISO8601> returns only records changed after that time (phases, layers,
features, inspections, issues, templates), each filtered by its own updated_at.
- 'deleted' tombstones (soft-deleted ids since 'since') for phases/layers/
features/inspections/issues so the device can purge locally.
- Bundle now also includes inspections, issues and inspection templates
(with version + content hash for incremental template download).
- New GET /api/v1/templates (accessible projects, ?since= delta).
Tests: 12 passing (added delta, tombstones, templates cases). Note: the 'since'
query param must be URL-encoded by clients (ISO8601 '+' offset).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Milestone 1 (auth foundation):
- Installed laravel/sanctum; HasApiTokens on User; published config + migration.
- routes/api.php with /api/v1; Sanctum 'ability' middleware alias registered.
- AuthController: POST login (long-lived revocable device token w/ ability
mobile-sync + devices table), GET me, POST logout. New Device model/table.
Milestone 2 (vertical slice, offline-first):
- progress_updates: +uuid (client-generated) +client_updated_at.
- ProjectApiController: GET projects (accessibleBy), GET projects/{id}/bundle
(project/phases/layers/features, membership-authorized).
- SyncController: POST sync — batch ops, idempotent by uuid, per-op result
(applied/duplicate/error), server-set user_id, authz by permission+membership.
Currently handles progress_update.create.
Tests: tests/Feature/Api/MobileApiTest (9 passing) — auth, accessible projects,
bundle authz, sync apply+idempotency, permission enforcement.
Also fixed a latent schema bug: projects.reference (and external_reference_1)
existed in the live DB but had no migration — added a guarded migration so fresh
installs match production.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>