From ca04b5a760a890e1d962931656e2891d9072f0f8 Mon Sep 17 00:00:00 2001 From: javier Date: Tue, 7 Jul 2026 13:29:27 +0200 Subject: [PATCH] docs(mobile): actualizar API v1.1 (templates globales+pivot, feature_types, issues enriquecidas) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - openapi.yaml: bumped a 1.1.0, añadido changelog con los cambios acumulados respecto al brief inicial; /templates y bundle.templates documentan que ahora usan pivot (asignación por proyecto); enums de issue documentados. - MOBILE_APP_BRIEF.md: reescrito para reflejar v1.1 — bundle con feature_types / issue_tasks / issue_comments; feature con feature_type_id/is_active; issue.type; nuevas ops (issue.update, issue_task.*, issue_comment.create); parent_entity admite issue_task/issue_comment; deleted incluye tasks/comments; y bloque detallado sobre templates ahora globales con fields[].group/question/help. Co-Authored-By: Claude Opus 4.7 --- docs/MOBILE_APP_BRIEF.md | 70 ++++++++++++++++++++++++++-------------- docs/openapi.yaml | 66 +++++++++++++++++++++++++++++++++---- 2 files changed, 105 insertions(+), 31 deletions(-) diff --git a/docs/MOBILE_APP_BRIEF.md b/docs/MOBILE_APP_BRIEF.md index 5808738..5c35a78 100644 --- a/docs/MOBILE_APP_BRIEF.md +++ b/docs/MOBILE_APP_BRIEF.md @@ -1,5 +1,7 @@ # ConstruProgress — Brief para la App Móvil +**Contrato de la API móvil (v1.1)** — actualizado 2026-07-07. + Documento único de traspaso para construir la app móvil que consume la API de ConstruProgress. La **fuente de verdad** del contrato es [`openapi.yaml`](openapi.yaml); el modelo offline está en [`MOBILE_SYNC_PROTOCOL.md`](MOBILE_SYNC_PROTOCOL.md). Este @@ -13,9 +15,9 @@ brief los resume y añade ejemplos de payloads reales y el modelo de datos. ## 1. Objetivo App de **seguimiento de obra** que funciona **sin conexión** en campo: descarga los -datos de un proyecto (estructura + plantillas), permite trabajar offline (actualizar -progreso, registrar inspecciones, gestionar incidencias con tareas/comentarios/fotos) -y **sincroniza cuando hay red**. +datos de un proyecto (estructura + plantillas asignadas), permite trabajar offline +(actualizar progreso, registrar inspecciones con fotos, gestionar incidencias con +tareas/comentarios/fotos) y **sincroniza cuando hay red**. ## 2. Autenticación (Laravel Sanctum) @@ -25,8 +27,7 @@ y **sincroniza cuando hay red**. - `POST /logout` revoca el token del dispositivo actual. - Guarda el token en almacenamiento seguro (Expo SecureStore / flutter_secure_storage). -**Base URL:** `https:///api/v1` (confirmar host de despliegue; en local XAMPP -suele ser `http://localhost/construprogress/public/api/v1`). +**Base URL:** `https:///api/v1` (confirmar host de despliegue). ```bash curl -X POST https:///api/v1/login \ @@ -44,9 +45,9 @@ curl -X POST https:///api/v1/login \ | POST | `/logout` | Revocar token | — | | GET | `/projects` | Proyectos accesibles | — | | GET | `/projects/{id}/bundle?since=` | PULL: snapshot o delta + tombstones | — | -| GET | `/templates?since=` | Plantillas de inspección (version+hash) | — | +| GET | `/templates?since=` | Plantillas **asignadas** a proyectos accesibles | — | | POST | `/sync` | PUSH: lote de mutaciones offline | 60/min | -| POST | `/media` | Subir fichero (multipart) | 120/min | +| POST | `/media` | Subida de ficheros (multipart) | 120/min | ## 4. PULL — descarga de datos @@ -55,18 +56,19 @@ curl -X POST https:///api/v1/login \ ```jsonc { - "server_time": "2026-06-18T12:00:00+00:00", // úsalo como próximo `since` + "server_time": "2026-07-07T12:00:00+00:00", // úsalo como próximo `since` "project": { ... }, "phases": [ ... ], "layers": [ ... ], "features": [ ... ], + "feature_types": [ ... ], // catálogo global (id, name, color) "inspections": [ ... ], "issues": [ ... ], - "issue_tasks": [ ... ], - "issue_comments": [ ... ], - "templates": [ ... ], + "issue_tasks": [ ... ], // checklist por incidencia + "issue_comments": [ ... ], // comentarios por incidencia + "templates": [ ... ], // asignadas al proyecto (vía pivot) "media": [ ... ], - "deleted": {} // vacío en snapshot completo + "deleted": {} // vacío en snapshot completo } ``` @@ -84,9 +86,15 @@ y un objeto `deleted` con los **ids borrados** (tombstones) por entidad: > ⚠️ **URL-encodea el `since`** (el `+` del offset horario). Guarda `server_time` de > cada respuesta y úsalo como el siguiente `since`. -### 4.3 Plantillas -`GET /templates?since=` devuelve las plantillas de inspección de los proyectos -accesibles, cada una con `version` (timestamp) y `hash` (para detectar cambios). +### 4.3 Plantillas (globales, con asignación por proyecto) +`GET /templates?since=` devuelve las plantillas del **catálogo global** que están +**asignadas** a alguno de los proyectos accesibles del usuario (vía pivot +`inspection_template_project`). Cada plantilla trae `version` (timestamp) y `hash`. + +> **Cambio de v1.0 → v1.1:** las plantillas ya **no** viven por proyecto. Son un +> catálogo global, y los proyectos consumen las que se les han asignado. +> `template.phase_id` se **eliminó**. `template.project_id` sigue en el JSON por +> compatibilidad pero **no** determina visibilidad — trátalo como informativo. ## 5. PUSH — `POST /sync` @@ -100,7 +108,7 @@ idempotencia) y `client_updated_at`: "entity": "feature", "op": "update", "uuid": "0f8e2b6c-....", // único y estable por operación - "client_updated_at": "2026-06-18T11:30:00+00:00", + "client_updated_at": "2026-07-07T11:30:00+00:00", "data": { "id": 5, "status": "completed", "progress": 100 } } ] @@ -119,12 +127,12 @@ Respuesta — **un resultado por operación**: (last-write-wins por `client_updated_at`). Resuélvelo en el cliente y reintenta. - `error`: trae `"error": "..."` (validación o permisos). -### 5.1 Operaciones soportadas (entity.op → data → permiso requerido) +### 5.1 Operaciones soportadas | entity.op | `data` | Permiso | |---|---|---| | `progress_update.create` | `{ phase_id, progress(0-100), comment?, location? }` | `update progress` | -| `feature.update` | `{ id, status?, progress?(0-100), responsible? }` | `update progress` | +| `feature.update` | `{ id, status?, progress?(0-100), responsible?, is_active?, feature_type_id? }` | `update progress` | | `inspection.create` | `{ feature_id, template_id?, data?, status?, result?, notes? }` | `create inspections` | | `issue.create` | `{ project_id, feature_id?, title, description?, priority?, status?, type? }` | `create issues` | | `issue.update` | `{ id, title?, description?, priority?, status?, type?, assigned_to?, resolution_notes? }` | `edit issues` | @@ -164,7 +172,8 @@ project : id, reference, name, address, lat, lng, status, updated_at phase : id, name, order, color, progress_percent, updated_at layer : id, phase_id, name, color, updated_at feature : id, layer_id, name, geometry(GeoJSON), status, progress, - responsible, template_id, updated_at + responsible, template_id, feature_type_id, is_active, updated_at +feature_type : id, name, color # catálogo global inspection : id, feature_id, layer_id, template_id, user_id, data(obj), status, result, notes, created_at, updated_at issue : id, feature_id, title, description, status, priority, type, @@ -172,8 +181,13 @@ issue : id, feature_id, title, description, status, priority, type, issue_task : id, issue_id, title, is_done, done_at, done_by, assigned_to, due_date, order, updated_at issue_comment : id, issue_id, user_id, body, created_at, updated_at -template : id, project_id, phase_id, name, description, fields(array), - version, hash, updated_at +template : id, name, description, fields(array), version, hash, + project_id(informativo, puede ser null), updated_at + # fields[]: cada campo puede incluir: + # group (string, sección para agrupar) + # question (string, prompt corto en la inspección) + # help (string, ayuda/comentarios largos) + # name, label, type, required, options, min, max, step media : id, uuid, parent_entity, parent_id, url, name, file_type, category, updated_at ``` @@ -193,6 +207,13 @@ media : id, uuid, parent_entity, parent_id, url, name, file_type, la `url` devuelta. 5. **Token**: en almacenamiento seguro; si 401 → re-login. +### Renderizar plantillas de inspección +Cada plantilla trae `fields[]` con posibles metadatos: `group` (agrupar campos por +sección), `question` (mostrar como prompt), `help` (bajo el campo). El resto de +campos (`name`, `label`, `type`, `required`, `options`, `min`, `max`, `step`) sigue +igual. Al enviar la inspección (`inspection.create`), `data` es un objeto plano +`{ nombre_del_campo: valor }` — los grupos son solo de presentación. + ### Flujo típico de sesión ``` login → guardar token @@ -206,7 +227,8 @@ con red: POST /sync (lote) → POST /media (ficheros) → GET bundle?since=serve - [ ] Elegir stack (RN+Expo / Flutter) y crear el proyecto. - [ ] `claude --add-dir C:\xampp\htdocs\construprogress` para tener el contrato a mano. - [ ] Capa de API (login/me/logout, projects, bundle, templates, sync, media). -- [ ] BD local + repositorios por entidad. +- [ ] BD local + repositorios por entidad (incluyendo feature_types, issue_tasks, + issue_comments, y templates con `fields[].group/question/help`). - [ ] Motor de sync (PULL delta + outbox PUSH + media) con manejo de conflictos. -- [ ] UI: lista de proyectos, mapa/fases, inspecciones, incidencias (checklist, - comentarios, fotos), indicador de estado de sincronización. +- [ ] UI: lista de proyectos, mapa/fases, inspecciones (grupos + fotos), incidencias + (checklist, comentarios, fotos), indicador de estado de sincronización. diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 08ebfc7..ea28b08 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1,11 +1,37 @@ openapi: 3.0.3 info: title: ConstruProgress Mobile API - version: "1.0.0" + version: "1.1.0" description: > Offline-first sync API for the mobile app. Auth via Laravel Sanctum bearer tokens (ability `mobile-sync`). All protected endpoints require `Authorization: Bearer `. See docs/MOBILE_SYNC_PROTOCOL.md. + + + ## Changelog (from v1.0) + + + - Inspection templates are now **global** and assigned to projects via + pivot. `bundle.templates` and `/templates` return templates *assigned* to + the accessible projects (no longer filtered by `project_id`). + `template.phase_id` was **removed**. `project_id` remains in the schema + for compatibility but no longer determines visibility. + + - Template `fields[]` items may include `group` (section), `question` (short + prompt shown at inspection time) and `help` (long help text). + + - New entities in the bundle: `feature_types` (global catalogue), + `issue_tasks`, `issue_comments`. + + - `feature` gains `feature_type_id`, `is_active`; `issue` gains `type`. + + - New `/sync` ops: `issue.update`, `issue_task.create`, `issue_task.update`, + `issue_comment.create`. `feature.update` accepts `is_active` and + `feature_type_id`; `issue.create`/`update` accept `type`. + + - `POST /media` `parent_entity` also accepts `issue_task` and `issue_comment`. + + - `deleted` tombstones include `issue_tasks` and `issue_comments`. servers: - url: /api/v1 security: @@ -85,7 +111,12 @@ paths: "403": { description: Not a member of the project } /templates: get: - summary: Inspection templates for accessible projects (with version/hash) + summary: Global inspection templates ASSIGNED to any accessible project (via pivot) + description: > + Templates are a global catalogue. This endpoint returns the templates + that have been *assigned* (via `inspection_template_project` pivot) to + any of the user's accessible projects. Each item includes `version` + (updated_at timestamp) and `hash` for change detection. parameters: - name: since in: query @@ -183,12 +214,33 @@ components: phases: { type: array, items: { type: object } } layers: { type: array, items: { type: object } } features: { type: array, items: { type: object } } - feature_types: { type: array, items: { type: object } } + feature_types: + type: array + description: Global catalogue of feature types (id, name, color) + items: { type: object } inspections: { type: array, items: { type: object } } - issues: { type: array, items: { type: object } } - issue_tasks: { type: array, items: { type: object } } - issue_comments: { type: array, items: { type: object } } - templates: { type: array, items: { type: object } } + issues: + type: array + description: > + Includes `type` (defect|safety|quality|documentation|other), + `status` (open|in_review|resolved|closed), + `priority` (low|medium|high|critical). + items: { type: object } + issue_tasks: + type: array + description: Checklist tasks per issue. + items: { type: object } + issue_comments: + type: array + description: Comment thread per issue. + items: { type: object } + templates: + type: array + description: > + Templates assigned to this project via pivot (no longer filtered by + project_id). Each item exposes version/hash for change detection and + `fields[]` where each field may include `group`, `question`, `help`. + items: { type: object } media: { type: array, items: { type: object } } deleted: type: object