mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
feat(r2.4): portail public QR — signalement sans compte, suivi par jeton
- 3 routes publiques /portal (les seules @Public métier), throttlées
(@nestjs/throttler : 10 signalements/min, 60 lectures/min) : résolution
du QR, signalement (retourne un jeton de suivi opaque), suivi par
référence + jeton — pas de jeton, pas de lecture, jamais de liste
- migration r2_portail : Request.publicToken (unique)
- page /q/{réf} fidèle à l'écran 6 validé R0 (mobile d'abord) : équipement
prérempli « détecté par le QR », interrupteur personne bloquée,
suivi sans jargon Reçu → Intervention → Résolu (dérivé de l'OT lié),
rejet affiché « Sans suite : {motif} », photo annoncée (upload R3) ;
signalements gardés sur le téléphone (localStorage, 10 max)
- e2e : LA boucle produit — gardien sans compte → traitement → ✓ Résolu ;
11 tests Playwright verts, 50 tests API, 52 opérations au contrat
- générateur OpenAPI : paramètres non-« id » plus typés uuid
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -956,6 +956,119 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/portal/assets/{reference}": {
|
||||
"get": {
|
||||
"operationId": "getPortalAsset",
|
||||
"summary": "Résoudre le QR scanné (référence → appareil, préremplissage)",
|
||||
"tags": [
|
||||
"portal"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "reference",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Appareil",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PortalAsset"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Référence inconnue"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/portal/requests": {
|
||||
"post": {
|
||||
"operationId": "createPortalRequest",
|
||||
"summary": "Signaler sans compte — renvoie le jeton de suivi (à garder sur le téléphone)",
|
||||
"tags": [
|
||||
"portal"
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PortalRequestCreate"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Signalement enregistré",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PortalRequestCreated"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Référence inconnue"
|
||||
},
|
||||
"429": {
|
||||
"description": "Trop de signalements — réessayez dans une minute"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/portal/requests/{reference}/{token}": {
|
||||
"get": {
|
||||
"operationId": "getPortalRequestStatus",
|
||||
"summary": "Suivi sans jargon (Reçu → Intervention → Résolu) — jeton requis",
|
||||
"tags": [
|
||||
"portal"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "reference",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "token",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Avancement",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PortalRequestStatus"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Signalement inconnu ou jeton invalide"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/preventive/templates": {
|
||||
"get": {
|
||||
"operationId": "listTaskTemplates",
|
||||
@@ -3634,6 +3747,171 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PortalAsset": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reference": {
|
||||
"type": "string"
|
||||
},
|
||||
"siteName": {
|
||||
"type": "string"
|
||||
},
|
||||
"locationName": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"reference",
|
||||
"siteName",
|
||||
"locationName"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PortalRequestCreated": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reference": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"isPersonTrapped": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"RECEIVED",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"step": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"RECEIVED",
|
||||
"IN_PROGRESS",
|
||||
"RESOLVED"
|
||||
]
|
||||
},
|
||||
"rejectionReason": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
|
||||
},
|
||||
"publicToken": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"reference",
|
||||
"description",
|
||||
"isPersonTrapped",
|
||||
"status",
|
||||
"step",
|
||||
"rejectionReason",
|
||||
"createdAt",
|
||||
"publicToken"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PortalRequestCreate": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"assetReference": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 30
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 1000
|
||||
},
|
||||
"isPersonTrapped": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"requesterName": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"assetReference",
|
||||
"description"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PortalRequestStatus": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reference": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"isPersonTrapped": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"RECEIVED",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"step": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"RECEIVED",
|
||||
"IN_PROGRESS",
|
||||
"RESOLVED"
|
||||
]
|
||||
},
|
||||
"rejectionReason": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"reference",
|
||||
"description",
|
||||
"isPersonTrapped",
|
||||
"status",
|
||||
"step",
|
||||
"rejectionReason",
|
||||
"createdAt"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TaskTemplatesResponse": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
|
||||
Reference in New Issue
Block a user