# CSTM Enquêtes

## Vue d’ensemble

CSTM Enquêtes est une application Laravel + Vue permettant à des agents terrain de réaliser des visites sur le terrain, de répondre à des questionnaires dynamiques et de collecter des données géolocalisées. L’espace d’administration permet de visualiser les réponses, filtrer les points visités, suivre les parcours agents et consulter le détail de chaque visite.

Le projet est actuellement orienté autour de trois usages principaux :

- la collecte terrain par les agents
- la gestion des questionnaires dynamiques côté admin
- l’exploitation des données collectées dans un back-office d’analyse

## Stack technique

- Backend : Laravel 13
- Frontend : Vue 3 + Vite
- Authentification : Laravel Sanctum
- Base de données : MySQL (selon la configuration du fichier .env)
- Langage PHP : 8.3
- JavaScript : Node.js / npm
- UI : composants Shadcn/Vue + Tailwind
- Cartographie : Leaflet
- Graphiques : ApexCharts

## Structure du projet

- app/
  - Http/Controllers/Api/DashboardController.php : logique d’administration et de données de synthèse
  - Models/ : modèles métier (User, Reponse, Questionnaire, GroupeQuestion, Question, OptionQuestion)
  - Enums/ : RoleEnum, TypePointEnum, TypeReponseEnum
- database/
  - migrations/
  - seeders/
- resources/js/
  - pages/ : écrans d’administration et d’agent
  - layouts/ : structures de navigation
  - router/index.js : routes Vue
  - config/navigation.js : menu latéral
  - composables/ : utilitaires front, notamment la création d’icônes de pin map
- routes/
  - api.php : routes de l’API
  - web.php
- tests/
  - Feature/

## Rôles et accès

Le système repose sur un modèle très simple :

- admin : accès back-office, dashboards, gestion des questionnaires, suivi terrain
- agent : accès à sa collecte, ses enquêtes, ses visites

Les routes sont protégées par Sanctum et les garde-fous du router Vue :

- requireAuth
- guestOnly
- roles

La vérification des droits est centralisée dans le router principal ainsi que dans le menu de navigation.

## Modèle de données

### Questionnaire

Un questionnaire est un ensemble de groupes de questions. Un administrateur peut le créer, le modifier ou l’activer.

### GroupeQuestion

Un groupe contient plusieurs questions ordonnées. Il structure le questionnaire pour mieux organiser les blocs de collecte.

### Question

Une question possède :

- un libellé
- un type de réponse
- éventuellement des options
- un ordre

### Reponse

Une réponse correspond à une visite terrain complète d’un agent sur un point (boutique ou prospect). Elle contient :

- questionnaire_id
- agent_id
- nom_point
- type_point (client / prospect)
- quartier / commune
- latitude / longitude
- precision_metres
- date_soumission

Chaque réponse peut avoir plusieurs détails de réponse, eux-mêmes liés à des questions et à leurs options sélectionnées.

## Écran d’authentification

La page de connexion est dans :

- resources/js/pages/Login.vue

Elle est volontairement épurée et centrée sur l’identification. Le but est de rester claire et rapide pour un agent ou un admin qui se connecte au quotidien.

## Espace admin

### Tableau de bord

Le dashboard principal est dans :

- resources/js/pages/Dashboard.vue

Il propose :

- KPI globaux
- progression vers un objectif
- tendance sur 7 jours
- répartition clients / prospects
- graphiques par questionnaire
- top réponses texte
- carte des points visités

### Visites terrain

La page dédiée au parcours est dans :

- resources/js/pages/admin/VisiteTerrain.vue

C’est une vue de synthèse sur les visites des agents. Elle permet de :

- sélectionner une date ou laisser toutes les dates
- filtrer par agent
- visualiser les points sur une carte
- reconstruire un parcours chronologique
- afficher les statistiques de distance et de durée par agent

Important : le parcours affiché est une reconstruction logique à partir de l’ordre des visites, avec une ligne droite entre deux points. Il ne s’agit pas d’un vrai trajet GPS routier continu.

### Points de visite

- resources/js/pages/admin/PointsListe.vue : liste des points récoltés
- resources/js/pages/admin/PointDetail.vue : détail d’un point unique avec carte et réponses

### Agents

- resources/js/pages/admin/AgentsListe.vue : gestion des agents

## API principale

Les routes API sont déclarées dans :

- routes/api.php

### Administration / dashboard

- GET /api/dashboard/stats-generales
- GET /api/dashboard/stats-questions
- GET /api/dashboard/top-reponses-texte
- GET /api/dashboard/carte
- GET /api/dashboard/parcours-agents
- GET /api/dashboard/filtres

### Détail point

- GET /api/points/{reponse}

### Authentification

- POST /api/auth/login
- GET /api/auth/me
- POST /api/auth/logout

## Démo et données d’exemple

Le projet inclut un seed de démonstration pour simuler les données de production :

- database/seeders/DemoEnqueteSeeder.php
- database/seeders/DatabaseSeeder.php

Ce seed permet de créer :

- un administrateur
- plusieurs agents
- un questionnaire actif
- groupes et questions
- réponses réalistes avec géolocalisation
- variantes clients / prospects

## Cartographie

Les cartes utilisent Leaflet. Les pin markers de type client/prospect sont stylisés via un composable partagé :

- resources/js/composables/useMapPin.js

Les couleurs sont :

- client : bleu
- prospect : orange

## Commandes de développement

### Installer les dépendances

```bash
composer install
npm install
```

### Base de données

```bash
php artisan migrate
php artisan db:seed
```

### Lancer le projet

```bash
npm run dev
```

### Vérifier / corriger le formatage PHP

```bash
php ./vendor/bin/pint --test
php ./vendor/bin/pint
```

### Tester

```bash
php artisan test
```

### Test ciblé sur les réponses / parcours

```bash
php artisan test tests/Feature/ReponsePersistenceTest.php --compact
```

## Points de vigilance

- Le projet utilise des UUID sur les modèles clés.
- La géolocalisation est obligatoire pour les réponses terrain.
- Les parcours sont reconstitués à partir de l’ordre des visites, pas de traces GPS continues.
- Les filtres de date doivent être utilisés avec prudence pour ne pas masquer les autres enquêtes actives.

## Objectif actuel

Le projet vise à offrir un outil de suivi terrain opérationnel pour la CSTM :

- centraliser les réponses des agents,
- obtenir une vue claire sur les points visités,
- tracer les parcours de manière exploitable,
- permettre à l’administration d’analyser les données sans intervention technique.

Ce document est un point de référence pour la suite du développement et doit être mis à jour à chaque évolution importante du produit.
`bootstrap/app.php`). Un compte `agent` recoit un `403`, une requete sans token
un `401`.

Endpoints :

- `GET /api/questionnaires` : liste triee par `created_at` descendant.
- `POST /api/questionnaires` : creation complete en une requete.
- `GET /api/questionnaires/{questionnaire}` : arbre complet, pour reedition.
- `PUT /api/questionnaires/{questionnaire}` : remplacement de l'arbre.
- `DELETE /api/questionnaires/{questionnaire}` : suppression.
- `POST /api/questionnaires/{questionnaire}/activate` : activation exclusive.
- `POST /api/questionnaires/{questionnaire}/duplicate` : copie profonde inactive.

### Payload Imbrique

Groupes, questions et options sont enregistres en une seule requete, en arbre.
Il n'y a volontairement pas de controller dedie a ces niveaux.

```json
{
  "titre": "Enquete boutiques Dakar",
  "description": "Releve mensuel",
  "actif": false,
  "groupes": [
    {
      "id": "uuid ou absent",
      "titre": "Identification",
      "description": null,
      "ordre": 0,
      "questions": [
        {
          "id": "uuid ou absent",
          "libelle": "Quels produits sont en rayon ?",
          "type_reponse": "choix_multiple",
          "obligatoire": true,
          "ordre": 0,
          "options": [
            { "id": "uuid ou absent", "libelle": "Peintures", "ordre": 0 },
            { "id": "uuid ou absent", "libelle": "Colles", "ordre": 1 }
          ]
        }
      ]
    }
  ]
}
```

Synchronisation appliquee par `update`, dans une transaction :

- element avec `id` connu : mis a jour ;
- element sans `id` : cree ;
- element absent du payload : supprime, avec cascade sur ses enfants.

`ordre` est derive de la position dans le tableau par le frontend. En son
absence, le service retombe sur l'index.

### Validation

`App\Http\Requests\QuestionnaireRequest` est partagee par `store` et `update` :

- `titre` : requis, string, max 255 ;
- `description` : facultatif, max 2000 ;
- `groupes` : requis, tableau, au moins un element ;
- `groupes.*.titre` : requis ;
- `groupes.*.questions` : requis, tableau, au moins un element ;
- `groupes.*.questions.*.libelle` : requis ;
- `groupes.*.questions.*.type_reponse` : requis, valeur de `TypeReponseEnum` ;
- `groupes.*.questions.*.options` : requis et au moins deux elements si le type
  est `choix_unique` ou `choix_multiple` ;
- `groupes.*.questions.*.options.*.libelle` : requis.

`prepareForValidation` retire les options des questions dont le type n'en
attend pas, au lieu de les rejeter : le constructeur frontend peut conserver
des options residuelles apres un changement de type.

### Regles Metier

Un seul questionnaire actif dans toute l'application. `activate` desactive tous
les autres dans une transaction, et `update` avec `actif: true` applique la
meme exclusivite. Un questionnaire cree par `store` est toujours `actif = false`,
de meme qu'une copie produite par `duplicate`.

Structure verrouillee apres collecte. Des qu'une reponse existe pour un
questionnaire, le service compare une empreinte de la structure enregistree
(libelles, types, `obligatoire`, `ordre`, options) a celle du payload :

- empreintes differentes : `422` sur la cle `groupes`, avec le message de
  `QuestionnaireService::MESSAGE_VERROU` :
  `Impossible de modifier les questions d’un questionnaire ayant déjà des réponses — dupliquez-le plutôt.` ;
- empreintes identiques : `titre`, `description` et `actif` sont enregistres et
  la synchronisation de l'arbre est entierement sautee, pour ne pas recreer des
  questions referencees par `reponse_details`.

Les titres et descriptions de groupes restent modifiables apres collecte : ce ne
sont pas des questions et ils n'invalident aucune reponse.

Suppression refusee tant que des reponses existent : `422` sur la cle
`questionnaire`, avec le message de `QuestionnaireService::MESSAGE_SUPPRESSION` :
`Impossible de supprimer un questionnaire ayant déjà des réponses.`
Pour faire evoluer un questionnaire deja utilise, il faut le dupliquer.

### Reponses De L'API

`show`, `store` et `update` renvoient l'arbre complet, mis en forme par
`QuestionnaireResource` : `id`, `titre`, `description`, `actif`, `cree_par`,
`reponses_count`, `verrouille`, `groupes[]`, horodatages. Chaque groupe porte
`questions[]`, chaque question porte `type_reponse`, `type_reponse_libelle`,
`obligatoire`, `ordre` et `options[]`.

`index`, `activate` et `duplicate` renvoient la forme liste,
`QuestionnaireListeResource` : `id`, `titre`, `description`, `actif`,
`groupes_count`, `questions_count`, `reponses_count`, `verrouille`,
`created_at`.

Le champ `verrouille` vaut vrai des que `reponses_count` est superieur a zero.
Le frontend s'en sert pour desactiver l'edition de la structure.

### Service

`App\Services\QuestionnaireService` porte toute l'ecriture et garde le
controller mince :

- `creer(array $donnees, User $auteur)`
- `mettreAJour(Questionnaire $questionnaire, array $donnees)`
- `activer(Questionnaire $questionnaire)`
- `dupliquer(Questionnaire $questionnaire, User $auteur)`
- `supprimer(Questionnaire $questionnaire)`
- `arbre(Questionnaire $questionnaire)` : chargement ordonne des trois niveaux

## Gestion Des Agents

La gestion des comptes agents est reservee aux administrateurs et se trouve
derriere `auth:sanctum` et le middleware `admin`.

Endpoints disponibles :

- `GET /api/agents` : liste des agents, triee du plus recent au plus ancien,
  avec le nombre de reponses soumises ;
- `POST /api/agents` : creation d'un compte actif avec mot de passe temporaire
  genere par `Str::password(12)` ;
- `PATCH /api/agents/{agent}/desactiver` : desactivation du compte ;
- `PATCH /api/agents/{agent}/reactiver` : reactivation du compte ;
- `POST /api/agents/{agent}/reinitialiser-mot-de-passe` : generation et envoi
  d'un nouveau mot de passe temporaire ;
- `DELETE /api/agents/{agent}` : suppression si aucune reponse n'est liee.

La creation et la reinitialisation envoient immediatement les identifiants via
`App\Mail\AgentCredentialsMail`. Le mot de passe temporaire est affiche une
seule fois dans l'email et n'est jamais renvoye dans la reponse JSON. Si
l'envoi echoue, le compte est conserve et la reponse contient
`email_envoye: false`.
Le mailable est volontairement synchrone et n'implemente pas `ShouldQueue`.
L'email contient le nom de l'agent, son email, le mot de passe temporaire, le
lien de connexion construit depuis `config('app.url')` et la recommandation de
modifier le mot de passe apres la premiere connexion.

La desactivation empeche la connexion : `AuthController@login` refuse tout
compte dont `actif` vaut `false`. La suppression d'un agent ayant soumis une
enquete retourne `422` avec la recommandation de le desactiver.

### Interface Frontend

`resources/js/pages/admin/AgentsListe.vue` est accessible via `/admin/agents`
dans le layout admin. Elle propose :

- la liste des agents avec nom, email, telephone, statut et nombre d'enquetes
  soumises ;
- la creation d'un agent depuis une modale avec nom, email et telephone
  optionnel ;
- la desactivation et la reactivation d'un compte ;
- la reinitialisation du mot de passe avec confirmation ;
- la suppression avec confirmation, desactivee visuellement lorsqu'une
  reponse est liee.

Apres creation, l'interface confirme l'envoi des identifiants ou affiche un
avertissement si `email_envoye` vaut `false`. La reinitialisation affiche le
meme type de retour utilisateur.

## Constructeur De Questionnaires

Interface d'administration, dans l'espace authentifie.

Pages :

- `resources/js/pages/questionnaires/Index.vue` : liste en tableau, carte du
  questionnaire actif, menu d'actions par ligne (modifier, activer, dupliquer,
  supprimer), confirmation de suppression, etat vide et squelettes de
  chargement.
- `resources/js/pages/questionnaires/Builder.vue` : constructeur, utilise pour
  la creation et la reedition selon la presence du parametre de route `id`.

Composants :

- `resources/js/components/questionnaires/GroupeEditor.vue` : une section, son
  titre, sa description et ses questions.
- `resources/js/components/questionnaires/QuestionEditor.vue` : une question,
  son type, son caractere obligatoire et ses options.

Le reordonnancement des sections et des questions se fait par boutons flechees,
sans dependance de glisser-deposer.

`resources/js/stores/questionnaires.js` expose l'etat et les appels API. Les
actions renvoient la donnee produite ou `null`, et publient le detail de
l'erreur dans `message` et `erreurs`, comme le store d'authentification.
`erreurPour('groupes.0.questions.1.libelle')` permet d'afficher une erreur de
validation Laravel sous le champ concerne.

`resources/js/lib/questionnaire.js` centralise :

- `TYPES_REPONSE` : les cinq types, avec libelle, aide et icone ;
- `aBesoinDOptions()` : pendant frontend du helper de l'enum PHP ;
- `nouveauGroupe()`, `nouvelleQuestion()`, `nouvelleOption()` : fabriques avec
  cle locale pour les `v-for`, les elements non enregistres ayant `id = null` ;
- `versFormulaire()` : arbre API vers modele de formulaire ;
- `versPayload()` : modele de formulaire vers payload API, `ordre` derive de la
  position et options omises pour les types qui n'en attendent pas.

Les retours utilisateur passent par `vue-sonner`, monte une fois dans `App.vue`.

## Espace Agent

L'espace agent est accessible aux utilisateurs authentifies ayant le role
`agent` et utilise `AgentLayout.vue`, distinct du layout administrateur. Il est
mobile-first pour les telephones et tablettes : en-tete compact, gros controles
tactiles, menu mobile et navigation fixe en bas de l'ecran.

Il contient uniquement trois menus : `Nouvelle enquête`, `Mes enquêtes` et
`Paramètres`. Ce dernier permet a l'agent de modifier son mot de passe.

### API Des Reponses

Les routes suivantes sont accessibles a tout utilisateur authentifie via
`auth:sanctum` :

- `GET /api/questionnaire-actif` : questionnaire actif complet, avec groupes,
  questions et options ordonnes ; retourne `404` si aucun questionnaire n'est
  actif ;
- `POST /api/reponses` : enregistre une enquete complete dans une transaction ;
- `GET /api/mes-reponses` : historique de l'agent connecte, trie par date
  decroissante et pagine par 20 ;
- `GET /api/reponses/{reponse}` : detail accessible a l'auteur ou a un admin,
  sinon `403`.

La soumission contient le point visite, son type, les coordonnees GPS et, si
disponible, `precision_metres`. Le questionnaire doit toujours etre celui qui
est actif au moment de la soumission ; un changement intervenu pendant la
saisie retourne `422`. Les reponses sont controlees selon le type de chaque
question et les choix doivent appartenir au questionnaire actif.

Chaque soumission utilise l'utilisateur authentifie comme `agent_id` et fixe
`date_soumission` au moment de l'enregistrement. Les details sont crees avec
les valeurs texte, booleennes ou choix uniques, et la table pivot
`reponse_detail_options` pour les choix multiples.

### Interface Agent

Les pages frontend sont :

- `resources/js/pages/agent/NouvelleEnquete.vue` : formulaire mobile-first,
  etape 1/2 avec informations du point visite, choix client/prospect et
  capture GPS obligatoire ;
- `resources/js/pages/agent/RemplirQuestionnaire.vue` : etape 2/2 avec stepper
  par groupe, controles adaptes aux cinq types de question et soumission ;
- `resources/js/pages/agent/MesEnquetes.vue` : historique mobile-first en
  cartes empilees, pagine par 20 et accessible depuis un bouton flottant ;
- `resources/js/pages/agent/ReponseDetail.vue` : consultation du detail d'une
  enquete soumise.

Routes frontend : `/agent`, `/agent/nouvelle-enquete`,
`/agent/nouvelle-enquete/questionnaire`, `/agent/mes-enquetes/:id` et
`/agent/parametres`. L'etape 2 est inaccessible directement si l'etape 1 n'a
pas ete completee.

`resources/js/composables/useGeolocation.js` encapsule les etats
`inactif`, `en_cours`, `succes` et `erreur`, ainsi que les messages distincts
pour les erreurs de permission, de position indisponible et de delai depasse.
La position est capturee avec `navigator.geolocation.getCurrentPosition` en
haute precision ; l'envoi reste bloque tant qu'une latitude et une longitude
n'ont pas ete obtenues. `resources/js/stores/nouvelleEnquete.js` conserve les
donnees du point et la position entre les deux etapes, uniquement en memoire.

## Modele De Donnees

Toutes les cles primaires applicatives sont des UUID. Les models Eloquent
utilisent le trait `HasUuids`.

### users

Utilisateurs de l'application.

- `id` : UUID, cle primaire
- `name`
- `email`
- `email_verified_at`
- `password`
- `role` : `RoleEnum`, defaut `agent`
- `telephone`, nullable
- `actif`, defaut `true`
- `remember_token`
- timestamps

Relations principales :

- `questionnaires()` : questionnaires crees par l'utilisateur
- `reponses()` : visites soumises par l'agent

### questionnaires

Questionnaires dynamiques crees par l'administration.

- `id` : UUID
- `titre`
- `description`, nullable
- `actif`, defaut `false`
- `cree_par` : utilisateur createur, nullable
- timestamps

Regle metier : un seul questionnaire actif a la fois dans toute l'application.
La contrainte n'est pas dans la migration, elle est tenue par
`App\Services\QuestionnaireService` (voir « Administration Des Questionnaires »).

Relations principales :

- `creePar()`
- `groupesQuestions()`
- `questions()` : `hasManyThrough` vers toutes les questions, tous groupes
  confondus, utilise pour le compteur de la liste
- `reponses()`

### groupes_questions

Groupes ordonnes de questions dans un questionnaire.

- `id` : UUID
- `questionnaire_id`
- `titre`
- `description`, nullable
- `ordre`, defaut `0`
- timestamps

Relations principales :

- `questionnaire()`
- `questions()`

### questions

Questions rattachees a un groupe.

- `id` : UUID
- `groupe_id`
- `libelle`
- `type_reponse` : `TypeReponseEnum`
- `obligatoire`, defaut `true`
- `ordre`, defaut `0`
- timestamps

Relations principales :

- `groupe()`
- `optionsQuestion()`
- `reponseDetails()`

### options_question

Options proposees pour les questions a choix.

- `id` : UUID
- `question_id`
- `libelle`
- `ordre`, defaut `0`
- timestamps

Relations principales :

- `question()`
- `reponseDetails()`
- `selectionneeDans()`

### reponses

Soumissions terrain. Une ligne correspond a une visite d'un agent dans un point
client ou prospect.

- `id` : UUID
- `questionnaire_id`
- `agent_id`
- `nom_point`
- `type_point` : `TypePointEnum`
- `quartier`, nullable
- `commune`, nullable
- `latitude` : decimal `10,7`
- `longitude` : decimal `10,7`
- `date_soumission`
- timestamps

Index :

- `agent_id`
- `questionnaire_id`
- `type_point`

Relations principales :

- `questionnaire()`
- `agent()`
- `reponseDetails()`

### reponse_details

Valeur saisie pour une question dans une soumission.

- `id` : UUID
- `reponse_id`
- `question_id`
- `valeur_texte`, nullable
- `valeur_bool`, nullable
- `option_id`, nullable
- timestamps

Contrainte :

- unique `reponse_id`, `question_id`

Relations principales :

- `reponse()`
- `question()`
- `option()`
- `optionsSelectionnees()`

### reponse_detail_options

Table pivot pour les questions a choix multiple.

- `id` : UUID
- `reponse_detail_id`
- `option_id`
- timestamps

Contrainte :

- unique `reponse_detail_id`, `option_id`

## Fichiers De L'Administration Des Questionnaires

Les responsabilites de l'administration des questionnaires sont reparties de
la maniere suivante :

- `app/Http/Controllers/Api/QuestionnaireController.php` : sept actions API,
  avec un controller volontairement mince ;
- `app/Http/Requests/QuestionnaireRequest.php` : validation commune de la
  creation et de la mise a jour ;
- `app/Services/QuestionnaireService.php` : ecriture transactionnelle,
  synchronisation de l'arbre, exclusivite, verrouillage et duplication ;
- `app/Http/Resources/QuestionnaireResource.php` : representation complete de
  l'arbre ;
- `app/Http/Resources/QuestionnaireListeResource.php` : representation legere
  pour les listes et les actions ;
- `resources/js/stores/questionnaires.js` : etat frontend et appels API ;
- `resources/js/lib/questionnaire.js` : types, fabriques, conversion formulaire
  et construction du payload.

## Verification De La Fonctionnalite

La fonctionnalite a ete verifiee sur les parcours backend et frontend :

- backend : 38 assertions via `curl`/`fetch`, sans echec ;
- frontend : 18 assertions dans Chrome headless via CDP, sans echec ni erreur
  console ;
- `php ./vendor/bin/pint --test` : reussi ;
- `npm run build` : reussi.

Les tests backend couvrent notamment le CRUD, l'arbre imbrique, la
synchronisation a trois niveaux, la validation, l'exclusivite du questionnaire
actif, le verrouillage apres reponse, la duplication, la suppression refusee,
ainsi que les reponses `401` et `403`.

Les tests navigateur couvrent la connexion, la liste, la creation et la
reedition, le changement de type avec generation d'options, les erreurs serveur,
le mode verrouille et le theme sombre.

## Seeders

Deux seeders preparent l'environnement local.

### AdminUserSeeder

Cree ou met a jour le compte administrateur initial.

```sh
php artisan db:seed --class=AdminUserSeeder
```

Les valeurs peuvent etre surchargees via l'environnement via les variables
`CSTM_ADMIN_NAME`, `CSTM_ADMIN_EMAIL`, `CSTM_ADMIN_PASSWORD`,
`CSTM_ADMIN_TELEPHONE`. Par defaut : email `admin@cstm.sn`, mot de passe
`password`. **Toujours modifier le mot de passe avant la production.**

Localise dans `database/seeders/AdminUserSeeder.php`.

### DemoEnqueteSeeder

Genere des donnees realistes pour le developpement et le test : un
questionnaire actif, trois agents, et 10+ reponses avec geolocalisation
authentique pour les quartiers de Dakar (Fann, Plateau, etc.).

```sh
php artisan db:seed --class=DemoEnqueteSeeder
```

Ce seeder cree un questionnaire avec trois groupes de questions (informations
boutique, produits, retours) combinant les cinq types de reponse. Les reponses
sont distribuees entre clients et prospects avec des positions GPS variees et
des niveaux de precision realistes.

Utile pour :

- valider le fonctionnement des cartes et des points de visite ;
- generer des statistiques et graphiques pour le tableau de bord ;
- tester les filtres d'administration ;
- verifier les regles metier de collecte.

Localise dans `database/seeders/DemoEnqueteSeeder.php`.

## Conventions De Developpement

- Ne pas ajouter de controllers, routes, vues ou seeders sans besoin explicite.
- Garder les UUID partout pour les cles primaires applicatives.
- Utiliser des backed enums PHP natifs pour les valeurs enumerees.
- Declarer les casts Eloquent pour les enums, booleens, dates et decimals.
- Garder des migrations separees et ordonnees selon les dependances de cles
  etrangeres.
- Enregistrer les structures imbriquees en une seule requete plutot qu'avec un
  controller par niveau, et placer la logique d'ecriture dans un service.
- Restreindre les routes par middleware aliase plutot que par des controles
  repetes dans chaque action.
- Ce fichier est redige en francais sans accents ; les textes affiches a
  l'utilisateur, eux, sont accentues.
