Amélioration de la splash_page et du login

This commit is contained in:
d6soft
2025-06-04 16:51:40 +02:00
parent 8c9e9a21c4
commit bcfdbb2c8b
168 changed files with 153842 additions and 6183 deletions

135
.cline Normal file
View File

@@ -0,0 +1,135 @@
{
"memoryBank": {
"enabled": true,
"path": "./docs",
"maxContextSize": 100000
},
"contextSettings": {
"maxTokens": 100000,
"cline.maxAutoApprovedRequests": 100,
"cline.enableMemoryBank": true,
"cline.includeSnippetsFromMemory": true,
"cline.contextLength": 10000,
"cline.autoFormat": true
},
"mcpServers": {
"github.com/modelcontextprotocol/servers/tree/main/src/git": {
"command": "python",
"args": [
"-m",
"mcp_server_git"
],
"disabled": false,
"autoApprove": []
},
"github.com/GLips/Figma-Context-MCP": {
"command": "npx",
"args": [
"-y",
"figma-developer-mcp",
"--figma-api-key=figd_2SyOIL_LeFVIIpUuRFT6F2tNWPQl89lBmUWAnOsy",
"--stdio"
],
"disabled": false,
"autoApprove": []
}
},
"projectStructure": {
"api": {
"type": "php",
"version": "8.3",
"structure": [
"Controllers",
"Core",
"Services",
"Config",
"Utils"
],
"conventions": {
"classes": "PascalCase",
"methods": "camelCase",
"namespaces": "App\\*"
}
},
"app": {
"type": "flutter",
"version": "3.19",
"structure": [
"core",
"presentation",
"shared",
"chat"
],
"conventions": {
"classes": "PascalCase",
"methods": "camelCase",
"variables": "camelCase"
}
},
"web": {
"type": "svelte",
"version": "5",
"structure": [
"src/components",
"src/lib",
"src/pages"
],
"conventions": {
"components": "PascalCase.svelte",
"functions": "camelCase",
"stores": "camelCase"
}
}
},
"gitWorkflow": {
"mainBranch": "main",
"developBranch": "develop",
"featureBranchPrefix": "feature/",
"bugfixBranchPrefix": "bugfix/",
"releaseBranchPrefix": "release/",
"commitMessageFormat": "type(scope): message",
"commitTypes": [
"feat",
"fix",
"docs",
"style",
"refactor",
"test",
"chore"
],
"mergeStrategy": "fast-forward",
"requireCodeReview": true
},
"security": {
"sensitiveDataPatterns": [
"encrypted_email",
"encrypted_name",
"encrypted_phone",
"encrypted_mobile",
"encrypted_stripe_id",
"user_pass_hash"
],
"requireEncryption": true,
"avoidHardcodedCredentials": true,
"inputValidation": "required"
},
"documentation": {
"requireForPublicAPI": true,
"includeExamples": true,
"updateChangelogOnRelease": true,
"primaryDocumentationFile": "CONTEXT-AI.md",
"referenceFiles": {
"database": "docs/geo_app.dump",
"apiEndpoints": "docs/api_endpoints.md",
"architecture": "docs/architecture.md"
}
},
"codeQuality": {
"phpStandard": "PSR-12",
"dartAnalyzer": "strong-mode",
"eslintConfig": "recommended",
"maximumMethodLength": 50,
"maximumFileLength": 500,
"preferImmutability": true
}
}

90
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,90 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": null,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
},
"[php]": {
"editor.defaultFormatter": "bmewburn.vscode-intelephense-client",
"editor.tabSize": 4,
"editor.insertSpaces": true
},
"[dart]": {
"editor.defaultFormatter": "Dart-Code.dart-code",
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.rulers": [
80
],
"editor.selectionHighlight": false,
"editor.suggest.snippetsPreventQuickSuggestions": false,
"editor.suggestSelection": "first",
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": "off"
},
"[svelte]": {
"editor.defaultFormatter": "svelte.svelte-vscode",
"editor.tabSize": 2
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2
},
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.wordWrap": "on"
},
"php.validate.executablePath": "/usr/bin/php",
"php.suggest.basic": false,
"intelephense.environment.phpVersion": "8.3.0",
"dart.flutterSdkPath": "/Users/pierre/dev/flutter",
"dart.lineLength": 80,
"svelte.enable-ts-plugin": true,
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/node_modules": true,
"**/build": true,
"**/.dart_tool": true,
"**/.flutter-plugins": true,
"**/.flutter-plugins-dependencies": true
},
"files.associations": {
"*.php": "php",
"*.dart": "dart",
"*.svelte": "svelte"
},
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/build": true,
"**/.dart_tool": true,
"**/vendor": true
},
"cline.autoApproveRequests": true,
"cline.enableMemoryBank": true,
"cline.includeSnippetsFromMemory": true,
"cline.contextLength": 10000,
"cline.autoFormat": true,
"cline.primaryDocumentationFile": "CONTEXT-AI.md",
"cline.gitIntegration": true,
"cline.projectStructure": {
"api": "php",
"app": "flutter",
"web": "svelte"
},
"cline.referenceFiles": {
"database": "docs/geo_app.dump",
"apiEndpoints": "docs/api_endpoints.md",
"architecture": "docs/architecture.md"
},
"cline.databaseSchema": "docs/geo_app.dump"
}

View File

@@ -120,36 +120,115 @@
### Branches GitLab
- **main/master**: [Production-ready code]
- **develop**: [Integration branch for features]
- **feature/[feature-name]**: [Feature development]
- **bugfix/[bug-name]**: [Bug fixes]
- **release/[version]**: [Release preparation]
- **main**: Code stable prêt pour la production
- **develop**: Branche d'intégration pour les fonctionnalités en cours de développement
- **feature/[feature-name]**: Branches de développement pour les nouvelles fonctionnalités
- Exemple: `feature/geolocalisation-casernes` pour l'ajout de la géolocalisation des casernes
- **bugfix/[bug-name]**: Branches pour les corrections de bugs
- **release/[version]**: Branches de préparation des versions
### Processus de Merge Request
1. [Créer une branche à partir de develop]
2. [Développer la fonctionnalité/correction]
3. [Soumettre une MR vers develop]
4. [Code review]
5. [CI/CD validation]
6. [Merge]
1. Créer une branche à partir de `main` ou `develop` selon la nature du changement
```bash
git checkout -b feature/nom-de-la-fonctionnalite main
```
2. Développer la fonctionnalité ou correction avec des commits atomiques
```bash
git add fichier1 fichier2
git commit -m "Description claire du changement"
```
3. Pousser la branche vers le dépôt distant
```bash
git push -u origin feature/nom-de-la-fonctionnalite
```
4. Créer une Merge Request via l'interface GitLab ou en utilisant l'URL fournie
- URL: `http://51.68.36.203/d6soft/geosector/-/merge_requests/new?merge_request%5Bsource_branch%5D=feature/nom-de-la-fonctionnalite`
5. Attendre la revue de code et les validations CI/CD
6. Une fois approuvée, fusionner la branche:
```bash
git checkout main
git merge feature/nom-de-la-fonctionnalite
git push origin main
```
### CI/CD Pipeline
[Description de votre pipeline CI/CD dans GitLab]
Le projet utilise un pipeline CI/CD GitLab pour automatiser les tests et le déploiement:
1. **Build**: Compilation du code et vérification de la syntaxe
- PHP: Vérification de la syntaxe et des dépendances Composer
- Flutter: Compilation et génération des assets
2. **Test**: Exécution des tests automatisés
- Tests unitaires pour l'API PHP
- Tests de widgets pour l'application Flutter
3. **Deploy**: Déploiement automatique vers les environnements
- Déploiement vers DEV après chaque merge dans `develop`
- Déploiement vers RECETTE après validation manuelle
- Déploiement vers PROD après validation manuelle sur une MR vers `main`
## Intégration avec GitLab
### Issues et Kanban
- **Labels**: [Liste des labels principaux et leur signification]
- **Milestones**: [Comment les milestones sont utilisées]
- **Boards**: [Description des tableaux Kanban]
- **Labels**:
- `feature`: Nouvelles fonctionnalités
- `bug`: Corrections de bugs
- `enhancement`: Améliorations de fonctionnalités existantes
- `documentation`: Mises à jour de la documentation
- `api`: Modifications de l'API
- `ui`: Modifications de l'interface utilisateur
- `priority:high`: Priorité élevée
- `priority:medium`: Priorité moyenne
- `priority:low`: Priorité basse
- **Milestones**:
- Organisées par versions majeures (1.0, 1.1, etc.)
- Chaque milestone contient les issues prévues pour la version
- Date d'échéance définie pour chaque milestone
- **Boards**:
- **Backlog**: Issues à traiter dans le futur
- **To Do**: Issues prêtes à être développées
- **In Progress**: Issues en cours de développement
- **Review**: Issues en attente de revue de code
- **Done**: Issues terminées et déployées
### Automatisations
[Description des automatisations GitLab utilisées]
- **Webhooks**: Notifications automatiques dans Slack pour les événements importants
- Nouvelles Merge Requests
- Commentaires sur les MRs
- Builds échoués
- Déploiements réussis
- **Merge Request Templates**: Templates prédéfinis pour les MRs avec:
- Description de la fonctionnalité
- Checklist de vérification
- Instructions de test
- Captures d'écran (si applicable)
- **CI/CD Automatisé**: Déclenchement automatique des pipelines sur:
- Push vers une branche
- Création d'une Merge Request
- Mise à jour d'une Merge Request
## Déploiement

View File

@@ -1,17 +1,19 @@
{
"window.zoomLevel": 1, // Permet de zoomer, pratique si vous faites une présentation
// Apparence
// -- Editeur
"workbench.startupEditor": "none", // On ne veut pas une page d'accueil chargée
"editor.minimap.enabled": false, // On veut voir la minimap
"editor.minimap.enabled": true, // On veut voir la minimap
"editor.minimap.showSlider": "always", // On veut voir la minimap
"editor.minimap.size": "fill", // On veut voir la minimap
"editor.minimap.scale": 2,
"editor.minimap.scale": 1,
"editor.tokenColorCustomizations": {
"textMateRules": [
{
"scope": ["storage.type.function", "storage.type.class"],
"scope": [
"storage.type.function",
"storage.type.class"
],
"settings": {
"fontStyle": "bold",
"foreground": "#4B9CD3"
@@ -27,7 +29,6 @@
"workbench.editor.tabSizing": "shrink", // On veut voir les tabs
"workbench.editor.pinnedTabSizing": "compact",
"workbench.editor.enablePreview": false, // Un clic sur un fichier l'ouvre
// -- Sidebar
"workbench.tree.indent": 15, // Indente plus pour plus de clarté dans la sidebar
"workbench.tree.renderIndentGuides": "always",
@@ -41,7 +42,6 @@
"editor.fontSize": 13,
"editor.lineHeight": 22,
"editor.guides.bracketPairs": "active",
// Ergonomie
"editor.wordWrap": "off",
"editor.rulers": [],
@@ -52,7 +52,6 @@
"editor.linkedEditing": true, // Quand on change un élément HTML, change la balise fermante
"editor.tabSize": 2,
"editor.unicodeHighlight.nonBasicASCII": false,
"[php]": {
"editor.defaultFormatter": "bmewburn.vscode-intelephense-client",
"editor.formatOnSave": true,
@@ -60,7 +59,8 @@
},
"intelephense.format.braces": "k&r",
"intelephense.format.enable": true,
"php.validate.executablePath": "/opt/homebrew/opt/php@8.3/bin/php",
"php.executablePath": "/opt/homebrew/opt/php@8.3/bin/php",
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
@@ -71,11 +71,22 @@
"prettier.singleQuote": true,
"prettier.tabWidth": 2,
"prettier.trailingComma": "es5",
"explorer.autoReveal": false,
"explorer.confirmDragAndDrop": false,
"emmet.triggerExpansionOnTab": true,
"emmet.includeLanguages": {
"javascript": "javascript",
"php": "php",
"svelte": "html",
"dart": "dart"
},
"problems.decorations.enabled": true,
"explorer.decorations.colors": true,
"explorer.decorations.badges": true,
"php.validate.enable": true,
"php.suggest.basic": false,
"dart.analysisExcludedFolders": [],
"dart.enableSdkFormatter": true,
// Fichiers
"files.defaultLanguage": "markdown",
"files.autoSaveWorkspaceFilesOnly": true,
@@ -85,40 +96,58 @@
// Languages
"javascript.preferences.importModuleSpecifierEnding": "js",
"typescript.preferences.importModuleSpecifierEnding": "js",
// Extensions
"tailwindCSS.experimental.configFile": "frontend/tailwind.config.js",
"tailwindCSS.experimental.configFile": "web/tailwind.config.js",
"editor.quickSuggestions": {
"strings": true
},
"[svelte]": {
"editor.defaultFormatter": "svelte.svelte-vscode",
"editor.formatOnSave": true
},
"prettier.documentSelectors": ["**/*.svelte"],
"prettier.documentSelectors": [
"**/*.svelte"
],
"svelte.plugin.svelte.diagnostics.enable": false,
"problems.decorations.enabled": false,
"js/ts.implicitProjectConfig.checkJs": false,
"svelte.enable-ts-plugin": false,
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#ff6433",
"activityBar.background": "#ff6433",
"activityBar.foreground": "#15202b",
"activityBar.inactiveForeground": "#15202b99",
"activityBarBadge.background": "#00ff3d",
"activityBarBadge.foreground": "#15202b",
"commandCenter.border": "#e7e7e799",
"sash.hoverBorder": "#ff6433",
"statusBar.background": "#ff3d00",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#ff6433",
"statusBarItem.remoteBackground": "#ff3d00",
"statusBarItem.remoteForeground": "#e7e7e7",
"titleBar.activeBackground": "#ff3d00",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#ff3d0099",
"titleBar.inactiveForeground": "#e7e7e799"
"cline.autoApproveLimit": 100,
"cline.autoApproveRequests": true,
"cline.enableMemoryBank": true,
"cline.includeSnippetsFromMemory": true,
"cline.contextLength": 10000,
"cline.autoFormat": true,
"cline.primaryDocumentationFile": ".cline",
"cline.gitIntegration": true,
"cline.projectStructure": {
"api": "php",
"app": "flutter",
"web": "svelte"
},
"peacock.color": "#ff3d00"
"cline.referenceFiles": {
"database": "docs/db-resalice.dump",
"apiEndpoints": "docs/api_endpoints.md",
"architecture": "docs/architecture.md"
},
"cline.databaseSchema": "docs/db-resalice.dump",
"peacock.color": "#dd0531",
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#fa1b49",
"activityBar.background": "#fa1b49",
"activityBar.foreground": "#e7e7e7",
"activityBar.inactiveForeground": "#e7e7e799",
"activityBarBadge.background": "#155e02",
"activityBarBadge.foreground": "#e7e7e7",
"commandCenter.border": "#e7e7e799",
"sash.hoverBorder": "#fa1b49",
"statusBar.background": "#dd0531",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#fa1b49",
"statusBarItem.remoteBackground": "#dd0531",
"statusBarItem.remoteForeground": "#e7e7e7",
"titleBar.activeBackground": "#dd0531",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#dd053199",
"titleBar.inactiveForeground": "#e7e7e799"
}
}

View File

@@ -1,612 +0,0 @@
# API D6MON - Documentation
## Introduction
L'API D6MON est une interface RESTful permettant d'interagir avec l'application D6MON. Cette API gère l'authentification des utilisateurs, la gestion des profils utilisateurs et la gestion des entités.
## Configuration
### Base URL
```
https://app.d6mon.com/api/mon
```
### En-têtes requis
Pour toutes les requêtes à l'API, les en-têtes suivants sont requis :
```
Content-Type: application/json
X-App-Identifier: app.d6mon.com
X-Client-Type: mobile
```
Pour les endpoints protégés (nécessitant une authentification), ajoutez également :
```
Authorization: Bearer {token}
```
`{token}` est le jeton d'authentification obtenu lors de la connexion.
## Authentification
### Connexion
**Endpoint :** `POST /login`
**Corps de la requête :**
```json
{
"email": "utilisateur@exemple.com",
"password": "motdepasse"
}
```
**Réponse réussie :**
```json
{
"success": true,
"data": {
"token": "session_token_here",
"user": {
"id": 123,
"email": "utilisateur@exemple.com",
"last_name": "Nom",
"first_name": "Prénom",
"display_name": "Nom d'affichage",
"entity_id": 456
}
}
}
```
### Inscription
**Endpoint :** `POST /register`
**Corps de la requête :**
```json
{
"display_name": "Nom d'affichage",
"email": "utilisateur@exemple.com",
"first_name": "Prénom",
"last_name": "Nom",
"entity_id": 456
}
```
**Réponse réussie :**
```json
{
"success": true,
"message": "Inscription réussie. Un email contenant vos identifiants vous a été envoyé.",
"data": {
"user": {
"id": 123,
"email": "utilisateur@exemple.com",
"display_name": "Nom d'affichage"
}
}
}
```
### Mot de passe oublié
**Endpoint :** `POST /lost-password`
**Corps de la requête :**
```json
{
"email": "utilisateur@exemple.com"
}
```
**Réponse réussie :**
```json
{
"success": true,
"message": "Un nouveau mot de passe a été envoyé à votre adresse email."
}
```
### Déconnexion
**Endpoint :** `POST /logout`
**Réponse réussie :**
```json
{
"success": true,
"message": "Déconnecté avec succès"
}
```
## Gestion des utilisateurs
### Récupérer le profil de l'utilisateur connecté
**Endpoint :** `GET /user/profile`
**Réponse réussie :**
```json
{
"success": true,
"data": {
"id": 123,
"entity_id": 456,
"display_name": "Nom d'affichage",
"first_name": "Prénom",
"last_name": "Nom",
"avatar": "url_avatar",
"email": "utilisateur@exemple.com",
"phone": "+33612345678",
"address1": "Adresse ligne 1",
"address2": "Adresse ligne 2",
"code_postal": "75000",
"city": "Paris",
"country": "France",
"seat_name": "Siège",
"created_at": "2023-01-01T00:00:00Z",
"updated_at": "2023-01-01T00:00:00Z",
"connected_at": "2023-01-01T00:00:00Z",
"is_active": true,
"entity": {
"id": 456,
"name": "Nom de l'entité",
"email": "entite@exemple.com",
"phone": "+33123456789",
"address1": "Adresse ligne 1",
"address2": "Adresse ligne 2",
"code_postal": "75000",
"city": "Paris",
"country": "France",
"created_at": "2023-01-01T00:00:00Z",
"updated_at": "2023-01-01T00:00:00Z",
"is_active": true
}
}
}
```
### Mettre à jour le profil de l'utilisateur connecté
**Endpoint :** `PUT /user/profile`
**Corps de la requête :**
```json
{
"display_name": "Nouveau nom d'affichage",
"first_name": "Nouveau prénom",
"last_name": "Nouveau nom",
"phone": "+33612345678",
"address1": "Nouvelle adresse ligne 1",
"address2": "Nouvelle adresse ligne 2",
"code_postal": "75001",
"city": "Paris",
"country": "France",
"seat_name": "Nouveau siège"
}
```
**Réponse réussie :**
Même format que `GET /user/profile` avec les données mises à jour.
### Changer le mot de passe
**Endpoint :** `POST /user/change-password`
**Corps de la requête :**
```json
{
"current_password": "ancien_mot_de_passe",
"new_password": "nouveau_mot_de_passe"
}
```
**Réponse réussie :**
```json
{
"success": true,
"message": "Mot de passe changé avec succès"
}
```
### Récupérer un utilisateur par ID
**Endpoint :** `GET /user/{id}`
**Réponse réussie :**
```json
{
"success": true,
"data": {
"id": 123,
"entity_id": 456,
"display_name": "Nom d'affichage",
"first_name": "Prénom",
"last_name": "Nom",
"avatar": "url_avatar",
"email": "utilisateur@exemple.com",
"phone": "+33612345678",
"address1": "Adresse ligne 1",
"address2": "Adresse ligne 2",
"code_postal": "75000",
"city": "Paris",
"country": "France",
"seat_name": "Siège",
"created_at": "2023-01-01T00:00:00Z",
"updated_at": "2023-01-01T00:00:00Z",
"connected_at": "2023-01-01T00:00:00Z",
"is_active": true
}
}
```
### Récupérer la liste des utilisateurs
**Endpoint :** `GET /users`
**Paramètres de requête :**
- `page` (optionnel) : Numéro de page (défaut : 1)
- `limit` (optionnel) : Nombre d'éléments par page (défaut : 20)
- `entity_id` (optionnel) : Filtrer par ID d'entité
**Réponse réussie :**
```json
{
"success": true,
"data": {
"users": [
{
"id": 123,
"entity_id": 456,
"display_name": "Nom d'affichage",
"first_name": "Prénom",
"last_name": "Nom",
"avatar": "url_avatar",
"email": "utilisateur@exemple.com",
"address1": "Adresse ligne 1",
"city": "Paris",
"country": "France",
"created_at": "2023-01-01T00:00:00Z",
"updated_at": "2023-01-01T00:00:00Z",
"connected_at": "2023-01-01T00:00:00Z",
"is_active": true
}
],
"pagination": {
"total": 100,
"page": 1,
"limit": 20,
"pages": 5
}
}
}
```
### Créer un nouvel utilisateur
**Endpoint :** `POST /user`
**Corps de la requête :**
```json
{
"display_name": "Nom d'affichage",
"email": "utilisateur@exemple.com",
"first_name": "Prénom",
"last_name": "Nom",
"entity_id": 456,
"phone": "+33612345678",
"address1": "Adresse ligne 1",
"address2": "Adresse ligne 2",
"code_postal": "75000",
"city": "Paris",
"country": "France",
"seat_name": "Siège"
}
```
**Réponse réussie :**
```json
{
"success": true,
"message": "Utilisateur créé avec succès. Un email avec les identifiants a été envoyé.",
"data": {
"id": 123,
"display_name": "Nom d'affichage",
"email": "utilisateur@exemple.com"
}
}
```
### Désactiver un utilisateur
**Endpoint :** `DELETE /user/{id}`
**Réponse réussie :**
```json
{
"success": true,
"message": "Utilisateur désactivé avec succès"
}
```
## Gestion des entités
### Récupérer toutes les entités
**Endpoint :** `GET /entities`
**Paramètres de requête :**
- `page` (optionnel) : Numéro de page (défaut : 1)
- `limit` (optionnel) : Nombre d'éléments par page (défaut : 20)
- `search` (optionnel) : Terme de recherche
**Réponse réussie :**
```json
{
"success": true,
"data": {
"entities": [
{
"id": 456,
"name": "Nom de l'entité",
"email": "entite@exemple.com",
"phone": "+33123456789",
"address1": "Adresse ligne 1",
"address2": "Adresse ligne 2",
"code_postal": "75000",
"city": "Paris",
"country": "France",
"created_at": "2023-01-01T00:00:00Z",
"updated_at": "2023-01-01T00:00:00Z",
"is_active": true
}
],
"pagination": {
"total": 50,
"page": 1,
"limit": 20,
"pages": 3
}
}
}
```
### Récupérer une entité par ID
**Endpoint :** `GET /entity/{id}`
**Réponse réussie :**
```json
{
"success": true,
"data": {
"id": 456,
"name": "Nom de l'entité",
"email": "entite@exemple.com",
"phone": "+33123456789",
"address1": "Adresse ligne 1",
"address2": "Adresse ligne 2",
"code_postal": "75000",
"city": "Paris",
"country": "France",
"created_at": "2023-01-01T00:00:00Z",
"updated_at": "2023-01-01T00:00:00Z",
"is_active": true,
"users": [
{
"id": 123,
"display_name": "Nom d'affichage",
"first_name": "Prénom",
"last_name": "Nom",
"avatar": "url_avatar",
"email": "utilisateur@exemple.com",
"created_at": "2023-01-01T00:00:00Z",
"is_active": true
}
]
}
}
```
### Créer une nouvelle entité
**Endpoint :** `POST /entity`
**Corps de la requête :**
```json
{
"name": "Nom de l'entité",
"email": "entite@exemple.com",
"phone": "+33123456789",
"address1": "Adresse ligne 1",
"address2": "Adresse ligne 2",
"code_postal": "75000",
"city": "Paris",
"country": "France"
}
```
**Réponse réussie :**
```json
{
"success": true,
"message": "Entité créée avec succès",
"data": {
"id": 456,
"name": "Nom de l'entité"
}
}
```
### Mettre à jour une entité
**Endpoint :** `PUT /entity/{id}`
**Corps de la requête :**
```json
{
"name": "Nouveau nom de l'entité",
"email": "nouvelle-entite@exemple.com",
"phone": "+33987654321",
"address1": "Nouvelle adresse ligne 1",
"address2": "Nouvelle adresse ligne 2",
"code_postal": "75001",
"city": "Paris",
"country": "France"
}
```
**Réponse réussie :**
Même format que `GET /entity/{id}` avec les données mises à jour.
### Désactiver une entité
**Endpoint :** `DELETE /entity/{id}`
**Réponse réussie :**
```json
{
"success": true,
"message": "Entité désactivée avec succès"
}
```
### Récupérer les utilisateurs d'une entité
**Endpoint :** `GET /entity/{id}/users`
**Paramètres de requête :**
- `page` (optionnel) : Numéro de page (défaut : 1)
- `limit` (optionnel) : Nombre d'éléments par page (défaut : 20)
**Réponse réussie :**
```json
{
"success": true,
"data": {
"users": [
{
"id": 123,
"display_name": "Nom d'affichage",
"first_name": "Prénom",
"last_name": "Nom",
"avatar": "url_avatar",
"email": "utilisateur@exemple.com",
"phone": "+33612345678",
"created_at": "2023-01-01T00:00:00Z",
"updated_at": "2023-01-01T00:00:00Z",
"connected_at": "2023-01-01T00:00:00Z",
"is_active": true
}
],
"pagination": {
"total": 25,
"page": 1,
"limit": 20,
"pages": 2
}
}
}
```
## Structure des données
### Table `users`
```sql
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
entity_id INT,
display_name VARCHAR(100) NOT NULL,
first_name VARCHAR(100),
encrypted_last_name VARCHAR(512),
avatar VARCHAR(255),
encrypted_email VARCHAR(512),
encrypted_phone VARCHAR(255),
address1 VARCHAR(255),
address2 VARCHAR(255),
code_postal VARCHAR(20),
city VARCHAR(100),
country VARCHAR(100),
seat_name VARCHAR(20),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
connected_at DATETIME,
is_active BOOLEAN DEFAULT TRUE,
FOREIGN KEY (entity_id) REFERENCES entities(id)
);
```
### Table `entities`
```sql
CREATE TABLE entities (
id INT AUTO_INCREMENT PRIMARY KEY,
encrypted_name VARCHAR(512) NOT NULL,
encrypted_email VARCHAR(512),
encrypted_phone VARCHAR(255),
address1 VARCHAR(255),
address2 VARCHAR(255),
code_postal VARCHAR(20),
city VARCHAR(100),
country VARCHAR(100),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT TRUE
);
```
## Sécurité
L'API utilise plusieurs mécanismes pour assurer la sécurité des données :
1. **Authentification par jeton** : Un jeton d'authentification est requis pour accéder aux endpoints protégés.
2. **Chiffrement des données sensibles** : Les données sensibles comme les noms, emails et numéros de téléphone sont chiffrées en base de données.
3. **Validation des entrées** : Toutes les entrées utilisateur sont validées avant traitement.
4. **Gestion des erreurs** : Les erreurs sont gérées de manière sécurisée sans divulguer d'informations sensibles.
## Codes d'erreur
- `400 Bad Request` : Requête invalide ou données manquantes
- `401 Unauthorized` : Authentification requise ou échouée
- `403 Forbidden` : Accès non autorisé à la ressource
- `404 Not Found` : Ressource non trouvée
- `409 Conflict` : Conflit avec l'état actuel de la ressource
- `500 Internal Server Error` : Erreur serveur
## Notes d'implémentation
- Les mots de passe sont hachés avec l'algorithme bcrypt.
- Les données sensibles sont chiffrées avec AES-256-CBC.
- Les emails sont envoyés pour les opérations importantes (inscription, réinitialisation de mot de passe).
- Les sessions sont gérées côté serveur avec un délai d'expiration.

View File

@@ -1,23 +0,0 @@
{
"memoryBank": {
"enabled": true,
"path": "./docs",
"maxContextSize": 100000
},
"contextSettings": {
"maxTokens": 100000,
"cline.maxAutoApprovedRequests": 100,
"cline.enableMemoryBank": true,
"cline.includeSnippetsFromMemory": true,
"cline.contextLength": 10000,
"cline.autoFormat": true
},
"mcpServers": {
"github.com/modelcontextprotocol/servers/tree/main/src/git": {
"command": "python",
"args": ["-m", "mcp_server_git"],
"disabled": false,
"autoApprove": []
}
}
}

View File

@@ -0,0 +1,2 @@
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_daemon-4.0.4/lib/fake.dart
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_runner-2.4.15/lib/fake.dart

View File

@@ -0,0 +1,54 @@
// @dart=3.6
// ignore_for_file: directives_ordering
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:build_runner_core/build_runner_core.dart' as _i1;
import 'package:hive_generator/hive_generator.dart' as _i2;
import 'package:source_gen/builder.dart' as _i3;
import 'package:build_resolvers/builder.dart' as _i4;
import 'dart:isolate' as _i5;
import 'package:build_runner/build_runner.dart' as _i6;
import 'dart:io' as _i7;
final _builders = <_i1.BuilderApplication>[
_i1.apply(
r'hive_generator:hive_generator',
[_i2.getBuilder],
_i1.toDependentsOf(r'hive_generator'),
hideOutput: true,
appliesBuilders: const [r'source_gen:combining_builder'],
),
_i1.apply(
r'source_gen:combining_builder',
[_i3.combiningBuilder],
_i1.toNoneByDefault(),
hideOutput: false,
appliesBuilders: const [r'source_gen:part_cleanup'],
),
_i1.apply(
r'build_resolvers:transitive_digests',
[_i4.transitiveDigestsBuilder],
_i1.toAllPackages(),
isOptional: true,
hideOutput: true,
appliesBuilders: const [r'build_resolvers:transitive_digest_cleanup'],
),
_i1.applyPostProcess(
r'build_resolvers:transitive_digest_cleanup',
_i4.transitiveDigestCleanup,
),
_i1.applyPostProcess(
r'source_gen:part_cleanup',
_i3.partCleanup,
),
];
void main(
List<String> args, [
_i5.SendPort? sendPort,
]) async {
var result = await _i6.run(
args,
_builders,
);
sendPort?.send(result);
_i7.exitCode = result;
}

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
<EFBFBD>C><3E>N<EFBFBD>a<EFBFBD><61><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"<22>

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>5z<EFBFBD><EFBFBD><EFBFBD>k<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

View File

@@ -0,0 +1 @@
<EFBFBD>ũ<EFBFBD><EFBFBD> <0C><><EFBFBD>U/!<21><>W<EFBFBD>

View File

@@ -0,0 +1 @@
<EFBFBD> n<><6E>A<EFBFBD><41><08><13>+<2B><><EFBFBD>

View File

@@ -0,0 +1 @@
X<EFBFBD>͊?<3F>sSφ?/^<5E>-k

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD>0)9#<23>D<EFBFBD><<3C>Ѹ #

View File

@@ -0,0 +1 @@
!iT<69>IeS<65> 2d<32>.<2E>n

View File

@@ -0,0 +1 @@
<05><><EFBFBD>[<5B><>,<2C><><EFBFBD><EFBFBD><18><><EFBFBD>

View File

@@ -0,0 +1 @@
e<EFBFBD>v<EFBFBD><EFBFBD><EFBFBD>K4I<34><>:

View File

@@ -0,0 +1,55 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class AnonymousUserModelAdapter extends TypeAdapter<AnonymousUserModel> {
@override
final int typeId = 24;
@override
AnonymousUserModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return AnonymousUserModel(
id: fields[0] as String,
deviceId: fields[1] as String,
name: fields[2] as String?,
email: fields[3] as String?,
createdAt: fields[4] as DateTime,
convertedToUserId: fields[5] as String?,
metadata: (fields[6] as Map?)?.cast<String, dynamic>(),
);
}
@override
void write(BinaryWriter writer, AnonymousUserModel obj) {
writer
..writeByte(7)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.deviceId)
..writeByte(2)
..write(obj.name)
..writeByte(3)
..write(obj.email)
..writeByte(4)
..write(obj.createdAt)
..writeByte(5)
..write(obj.convertedToUserId)
..writeByte(6)
..write(obj.metadata);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AnonymousUserModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,55 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class AudienceTargetModelAdapter extends TypeAdapter<AudienceTargetModel> {
@override
final int typeId = 23;
@override
AudienceTargetModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return AudienceTargetModel(
id: fields[0] as String,
conversationId: fields[1] as String,
targetType: fields[2] as String,
targetId: fields[3] as String?,
createdAt: fields[4] as DateTime,
roleFilter: fields[5] as String?,
entityFilter: fields[6] as String?,
);
}
@override
void write(BinaryWriter writer, AudienceTargetModel obj) {
writer
..writeByte(7)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.conversationId)
..writeByte(2)
..write(obj.targetType)
..writeByte(3)
..write(obj.targetId)
..writeByte(4)
..write(obj.createdAt)
..writeByte(5)
..write(obj.roleFilter)
..writeByte(6)
..write(obj.entityFilter);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AudienceTargetModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,64 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class ConversationModelAdapter extends TypeAdapter<ConversationModel> {
@override
final int typeId = 20;
@override
ConversationModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return ConversationModel(
id: fields[0] as String,
type: fields[1] as String,
title: fields[2] as String?,
createdAt: fields[3] as DateTime,
updatedAt: fields[4] as DateTime,
participants: (fields[5] as List).cast<ParticipantModel>(),
isSynced: fields[6] as bool,
replyPermission: fields[7] as String,
isPinned: fields[8] as bool,
expiryDate: fields[9] as DateTime?,
);
}
@override
void write(BinaryWriter writer, ConversationModel obj) {
writer
..writeByte(10)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.type)
..writeByte(2)
..write(obj.title)
..writeByte(3)
..write(obj.createdAt)
..writeByte(4)
..write(obj.updatedAt)
..writeByte(5)
..write(obj.participants)
..writeByte(6)
..write(obj.isSynced)
..writeByte(7)
..write(obj.replyPermission)
..writeByte(8)
..write(obj.isPinned)
..writeByte(9)
..write(obj.expiryDate);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ConversationModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,67 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class MessageModelAdapter extends TypeAdapter<MessageModel> {
@override
final int typeId = 21;
@override
MessageModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return MessageModel(
id: fields[0] as String,
conversationId: fields[1] as String,
senderId: fields[2] as String?,
senderType: fields[3] as String,
content: fields[4] as String,
contentType: fields[5] as String,
createdAt: fields[6] as DateTime,
deliveredAt: fields[7] as DateTime?,
readAt: fields[8] as DateTime?,
status: fields[9] as String,
isAnnouncement: fields[10] as bool,
);
}
@override
void write(BinaryWriter writer, MessageModel obj) {
writer
..writeByte(11)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.conversationId)
..writeByte(2)
..write(obj.senderId)
..writeByte(3)
..write(obj.senderType)
..writeByte(4)
..write(obj.content)
..writeByte(5)
..write(obj.contentType)
..writeByte(6)
..write(obj.createdAt)
..writeByte(7)
..write(obj.deliveredAt)
..writeByte(8)
..write(obj.readAt)
..writeByte(9)
..write(obj.status)
..writeByte(10)
..write(obj.isAnnouncement);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MessageModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,64 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class NotificationSettingsAdapter extends TypeAdapter<NotificationSettings> {
@override
final int typeId = 25;
@override
NotificationSettings read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return NotificationSettings(
enableNotifications: fields[0] as bool,
soundEnabled: fields[1] as bool,
vibrationEnabled: fields[2] as bool,
mutedConversations: (fields[3] as List).cast<String>(),
showPreview: fields[4] as bool,
conversationNotifications: (fields[5] as Map).cast<String, bool>(),
doNotDisturb: fields[6] as bool,
doNotDisturbStart: fields[7] as DateTime?,
doNotDisturbEnd: fields[8] as DateTime?,
deviceToken: fields[9] as String?,
);
}
@override
void write(BinaryWriter writer, NotificationSettings obj) {
writer
..writeByte(10)
..writeByte(0)
..write(obj.enableNotifications)
..writeByte(1)
..write(obj.soundEnabled)
..writeByte(2)
..write(obj.vibrationEnabled)
..writeByte(3)
..write(obj.mutedConversations)
..writeByte(4)
..write(obj.showPreview)
..writeByte(5)
..write(obj.conversationNotifications)
..writeByte(6)
..write(obj.doNotDisturb)
..writeByte(7)
..write(obj.doNotDisturbStart)
..writeByte(8)
..write(obj.doNotDisturbEnd)
..writeByte(9)
..write(obj.deviceToken);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is NotificationSettingsAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,61 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class ParticipantModelAdapter extends TypeAdapter<ParticipantModel> {
@override
final int typeId = 22;
@override
ParticipantModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return ParticipantModel(
id: fields[0] as String,
conversationId: fields[1] as String,
userId: fields[2] as String?,
anonymousId: fields[3] as String?,
role: fields[4] as String,
joinedAt: fields[5] as DateTime,
lastReadMessageId: fields[6] as String?,
viaTarget: fields[7] as bool,
canReply: fields[8] as bool?,
);
}
@override
void write(BinaryWriter writer, ParticipantModel obj) {
writer
..writeByte(9)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.conversationId)
..writeByte(2)
..write(obj.userId)
..writeByte(3)
..write(obj.anonymousId)
..writeByte(4)
..write(obj.role)
..writeByte(5)
..write(obj.joinedAt)
..writeByte(6)
..write(obj.lastReadMessageId)
..writeByte(7)
..write(obj.viaTarget)
..writeByte(8)
..write(obj.canReply);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ParticipantModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,100 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class AmicaleModelAdapter extends TypeAdapter<AmicaleModel> {
@override
final int typeId = 11;
@override
AmicaleModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return AmicaleModel(
id: fields[0] as int,
name: fields[1] as String,
adresse1: fields[2] as String,
adresse2: fields[3] as String,
codePostal: fields[4] as String,
ville: fields[5] as String,
fkRegion: fields[6] as int?,
libRegion: fields[7] as String?,
fkType: fields[8] as int?,
phone: fields[9] as String,
mobile: fields[10] as String,
email: fields[11] as String,
gpsLat: fields[12] as String,
gpsLng: fields[13] as String,
stripeId: fields[14] as String,
chkDemo: fields[15] as bool,
chkCopieMailRecu: fields[16] as bool,
chkAcceptSms: fields[17] as bool,
chkActive: fields[18] as bool,
chkStripe: fields[19] as bool,
createdAt: fields[20] as DateTime?,
updatedAt: fields[21] as DateTime?,
);
}
@override
void write(BinaryWriter writer, AmicaleModel obj) {
writer
..writeByte(22)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.name)
..writeByte(2)
..write(obj.adresse1)
..writeByte(3)
..write(obj.adresse2)
..writeByte(4)
..write(obj.codePostal)
..writeByte(5)
..write(obj.ville)
..writeByte(6)
..write(obj.fkRegion)
..writeByte(7)
..write(obj.libRegion)
..writeByte(8)
..write(obj.fkType)
..writeByte(9)
..write(obj.phone)
..writeByte(10)
..write(obj.mobile)
..writeByte(11)
..write(obj.email)
..writeByte(12)
..write(obj.gpsLat)
..writeByte(13)
..write(obj.gpsLng)
..writeByte(14)
..write(obj.stripeId)
..writeByte(15)
..write(obj.chkDemo)
..writeByte(16)
..write(obj.chkCopieMailRecu)
..writeByte(17)
..write(obj.chkAcceptSms)
..writeByte(18)
..write(obj.chkActive)
..writeByte(19)
..write(obj.chkStripe)
..writeByte(20)
..write(obj.createdAt)
..writeByte(21)
..write(obj.updatedAt);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AmicaleModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,91 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class ClientModelAdapter extends TypeAdapter<ClientModel> {
@override
final int typeId = 10;
@override
ClientModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return ClientModel(
id: fields[0] as int,
name: fields[1] as String,
adresse1: fields[2] as String?,
adresse2: fields[3] as String?,
codePostal: fields[4] as String?,
ville: fields[5] as String?,
fkRegion: fields[6] as int?,
libRegion: fields[7] as String?,
fkType: fields[8] as int?,
phone: fields[9] as String?,
mobile: fields[10] as String?,
email: fields[11] as String?,
gpsLat: fields[12] as String?,
gpsLng: fields[13] as String?,
stripeId: fields[14] as String?,
chkDemo: fields[15] as bool?,
chkCopieMailRecu: fields[16] as bool?,
chkAcceptSms: fields[17] as bool?,
chkActive: fields[18] as bool?,
);
}
@override
void write(BinaryWriter writer, ClientModel obj) {
writer
..writeByte(19)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.name)
..writeByte(2)
..write(obj.adresse1)
..writeByte(3)
..write(obj.adresse2)
..writeByte(4)
..write(obj.codePostal)
..writeByte(5)
..write(obj.ville)
..writeByte(6)
..write(obj.fkRegion)
..writeByte(7)
..write(obj.libRegion)
..writeByte(8)
..write(obj.fkType)
..writeByte(9)
..write(obj.phone)
..writeByte(10)
..write(obj.mobile)
..writeByte(11)
..write(obj.email)
..writeByte(12)
..write(obj.gpsLat)
..writeByte(13)
..write(obj.gpsLng)
..writeByte(14)
..write(obj.stripeId)
..writeByte(15)
..write(obj.chkDemo)
..writeByte(16)
..write(obj.chkCopieMailRecu)
..writeByte(17)
..write(obj.chkAcceptSms)
..writeByte(18)
..write(obj.chkActive);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ClientModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,67 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class MembreModelAdapter extends TypeAdapter<MembreModel> {
@override
final int typeId = 5;
@override
MembreModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return MembreModel(
id: fields[0] as int,
fkRole: fields[1] as int,
fkTitre: fields[2] as int,
firstName: fields[3] as String,
sectName: fields[4] as String?,
dateNaissance: fields[5] as DateTime?,
dateEmbauche: fields[6] as DateTime?,
chkActive: fields[7] as int,
name: fields[8] as String,
username: fields[9] as String,
email: fields[10] as String,
);
}
@override
void write(BinaryWriter writer, MembreModel obj) {
writer
..writeByte(11)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.fkRole)
..writeByte(2)
..write(obj.fkTitre)
..writeByte(3)
..write(obj.firstName)
..writeByte(4)
..write(obj.sectName)
..writeByte(5)
..write(obj.dateNaissance)
..writeByte(6)
..write(obj.dateEmbauche)
..writeByte(7)
..write(obj.chkActive)
..writeByte(8)
..write(obj.name)
..writeByte(9)
..write(obj.username)
..writeByte(10)
..write(obj.email);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MembreModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,55 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class OperationModelAdapter extends TypeAdapter<OperationModel> {
@override
final int typeId = 1;
@override
OperationModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return OperationModel(
id: fields[0] as int,
name: fields[1] as String,
dateDebut: fields[2] as DateTime,
dateFin: fields[3] as DateTime,
lastSyncedAt: fields[4] as DateTime,
isActive: fields[5] as bool,
isSynced: fields[6] as bool,
);
}
@override
void write(BinaryWriter writer, OperationModel obj) {
writer
..writeByte(7)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.name)
..writeByte(2)
..write(obj.dateDebut)
..writeByte(3)
..write(obj.dateFin)
..writeByte(4)
..write(obj.lastSyncedAt)
..writeByte(5)
..write(obj.isActive)
..writeByte(6)
..write(obj.isSynced);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is OperationModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,121 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class PassageModelAdapter extends TypeAdapter<PassageModel> {
@override
final int typeId = 4;
@override
PassageModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return PassageModel(
id: fields[0] as int,
fkOperation: fields[1] as int,
fkSector: fields[2] as int,
fkUser: fields[3] as int,
fkType: fields[4] as int,
fkAdresse: fields[5] as String,
passedAt: fields[6] as DateTime,
numero: fields[7] as String,
rue: fields[8] as String,
rueBis: fields[9] as String,
ville: fields[10] as String,
residence: fields[11] as String,
fkHabitat: fields[12] as int,
appt: fields[13] as String,
niveau: fields[14] as String,
gpsLat: fields[15] as String,
gpsLng: fields[16] as String,
nomRecu: fields[17] as String,
remarque: fields[18] as String,
montant: fields[19] as String,
fkTypeReglement: fields[20] as int,
emailErreur: fields[21] as String,
nbPassages: fields[22] as int,
name: fields[23] as String,
email: fields[24] as String,
phone: fields[25] as String,
lastSyncedAt: fields[26] as DateTime,
isActive: fields[27] as bool,
isSynced: fields[28] as bool,
);
}
@override
void write(BinaryWriter writer, PassageModel obj) {
writer
..writeByte(29)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.fkOperation)
..writeByte(2)
..write(obj.fkSector)
..writeByte(3)
..write(obj.fkUser)
..writeByte(4)
..write(obj.fkType)
..writeByte(5)
..write(obj.fkAdresse)
..writeByte(6)
..write(obj.passedAt)
..writeByte(7)
..write(obj.numero)
..writeByte(8)
..write(obj.rue)
..writeByte(9)
..write(obj.rueBis)
..writeByte(10)
..write(obj.ville)
..writeByte(11)
..write(obj.residence)
..writeByte(12)
..write(obj.fkHabitat)
..writeByte(13)
..write(obj.appt)
..writeByte(14)
..write(obj.niveau)
..writeByte(15)
..write(obj.gpsLat)
..writeByte(16)
..write(obj.gpsLng)
..writeByte(17)
..write(obj.nomRecu)
..writeByte(18)
..write(obj.remarque)
..writeByte(19)
..write(obj.montant)
..writeByte(20)
..write(obj.fkTypeReglement)
..writeByte(21)
..write(obj.emailErreur)
..writeByte(22)
..write(obj.nbPassages)
..writeByte(23)
..write(obj.name)
..writeByte(24)
..write(obj.email)
..writeByte(25)
..write(obj.phone)
..writeByte(26)
..write(obj.lastSyncedAt)
..writeByte(27)
..write(obj.isActive)
..writeByte(28)
..write(obj.isSynced);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PassageModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,55 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class RegionModelAdapter extends TypeAdapter<RegionModel> {
@override
final int typeId = 7;
@override
RegionModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return RegionModel(
id: fields[0] as int,
fkPays: fields[1] as int,
libelle: fields[2] as String,
libelleLong: fields[3] as String?,
tableOsm: fields[4] as String?,
departements: fields[5] as String?,
chkActive: fields[6] as bool,
);
}
@override
void write(BinaryWriter writer, RegionModel obj) {
writer
..writeByte(7)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.fkPays)
..writeByte(2)
..write(obj.libelle)
..writeByte(3)
..write(obj.libelleLong)
..writeByte(4)
..write(obj.tableOsm)
..writeByte(5)
..write(obj.departements)
..writeByte(6)
..write(obj.chkActive);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RegionModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,46 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class SectorModelAdapter extends TypeAdapter<SectorModel> {
@override
final int typeId = 3;
@override
SectorModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return SectorModel(
id: fields[0] as int,
libelle: fields[1] as String,
color: fields[2] as String,
sector: fields[3] as String,
);
}
@override
void write(BinaryWriter writer, SectorModel obj) {
writer
..writeByte(4)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.libelle)
..writeByte(2)
..write(obj.color)
..writeByte(3)
..write(obj.sector);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SectorModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,94 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class UserModelAdapter extends TypeAdapter<UserModel> {
@override
final int typeId = 0;
@override
UserModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return UserModel(
id: fields[0] as int,
email: fields[1] as String,
name: fields[2] as String?,
username: fields[11] as String?,
firstName: fields[10] as String?,
role: fields[3] as int,
createdAt: fields[4] as DateTime,
lastSyncedAt: fields[5] as DateTime,
isActive: fields[6] as bool,
isSynced: fields[7] as bool,
sessionId: fields[8] as String?,
sessionExpiry: fields[9] as DateTime?,
lastPath: fields[12] as String?,
sectName: fields[13] as String?,
fkEntite: fields[14] as int?,
fkTitre: fields[15] as int?,
phone: fields[16] as String?,
mobile: fields[17] as String?,
dateNaissance: fields[18] as DateTime?,
dateEmbauche: fields[19] as DateTime?,
);
}
@override
void write(BinaryWriter writer, UserModel obj) {
writer
..writeByte(20)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.email)
..writeByte(2)
..write(obj.name)
..writeByte(11)
..write(obj.username)
..writeByte(10)
..write(obj.firstName)
..writeByte(3)
..write(obj.role)
..writeByte(4)
..write(obj.createdAt)
..writeByte(5)
..write(obj.lastSyncedAt)
..writeByte(6)
..write(obj.isActive)
..writeByte(7)
..write(obj.isSynced)
..writeByte(8)
..write(obj.sessionId)
..writeByte(9)
..write(obj.sessionExpiry)
..writeByte(12)
..write(obj.lastPath)
..writeByte(13)
..write(obj.sectName)
..writeByte(14)
..write(obj.fkEntite)
..writeByte(15)
..write(obj.fkTitre)
..writeByte(16)
..write(obj.phone)
..writeByte(17)
..write(obj.mobile)
..writeByte(18)
..write(obj.dateNaissance)
..writeByte(19)
..write(obj.dateEmbauche);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is UserModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1,49 @@
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class UserSectorModelAdapter extends TypeAdapter<UserSectorModel> {
@override
final int typeId = 7;
@override
UserSectorModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return UserSectorModel(
id: fields[0] as int,
firstName: fields[1] as String?,
sectName: fields[2] as String?,
fkSector: fields[3] as int,
name: fields[4] as String?,
);
}
@override
void write(BinaryWriter writer, UserSectorModel obj) {
writer
..writeByte(5)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.firstName)
..writeByte(2)
..write(obj.sectName)
..writeByte(3)
..write(obj.fkSector)
..writeByte(4)
..write(obj.name);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is UserSectorModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -0,0 +1 @@
C<EFBFBD><EFBFBD><EFBFBD>F}<7D><EFBFBD>7<><37><EFBFBD><EFBFBD>9

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD>E>`<60>e0<65>sl<73><6C> <0C>

View File

@@ -0,0 +1 @@
<EFBFBD>FJ6<EFBFBD><EFBFBD>6<EFBFBD><EFBFBD><EFBFBD><EFBFBD>e<EFBFBD><EFBFBD>-b

View File

@@ -0,0 +1 @@
<EFBFBD>[ڀJ<DA80>n<EFBFBD><6E>(<28>v/<2F>~<7E>

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD>ulM<6C>L<EFBFBD><4C><EFBFBD><EFBFBD>M

View File

@@ -0,0 +1 @@
<11><><1D>;<3B><> <0B><>^<5E>:p

View File

@@ -0,0 +1 @@
o~<7E>+x<>\6<1B><><EFBFBD>o<EFBFBD><6F>a

View File

@@ -0,0 +1,2 @@
Q<EFBFBD>;<14><><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD>)<29>j<EFBFBD>

View File

@@ -0,0 +1 @@
<EFBFBD>I<EFBFBD>4<EFBFBD><17>]7<12>{Rf<52>

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD>ve<EFBFBD>wz<EFBFBD>z::O<><4F><EFBFBD>

Binary file not shown.

View File

@@ -0,0 +1 @@
{"sdk":"3.8.1 (stable) (Wed May 28 00:47:25 2025 -0700) on \"macos_arm64\"","analyzer":"/Users/pierre/.pub-cache/hosted/pub.dev/analyzer-6.11.0","build_resolvers":"/Users/pierre/.pub-cache/hosted/pub.dev/build_resolvers-2.4.4"}

View File

@@ -0,0 +1,22 @@
// Flutter web plugin registrant file.
//
// Generated file. Do not edit.
//
// @dart = 2.13
// ignore_for_file: type=lint
import 'package:connectivity_plus/src/connectivity_plus_web.dart';
import 'package:geolocator_web/geolocator_web.dart';
import 'package:package_info_plus/src/package_info_plus_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
void registerPlugins([final Registrar? pluginRegistrar]) {
final Registrar registrar = pluginRegistrar ?? webPluginRegistrar;
ConnectivityPlusWebPlugin.registerWith(registrar);
GeolocatorPlugin.registerWith(registrar);
PackageInfoPlusWebPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);
registrar.registerMessageHandler();
}

View File

@@ -0,0 +1,31 @@
Extension Discovery Cache
=========================
This folder is used by `package:extension_discovery` to cache lists of
packages that contains extensions for other packages.
DO NOT USE THIS FOLDER
----------------------
* Do not read (or rely) the contents of this folder.
* Do write to this folder.
If you're interested in the lists of extensions stored in this folder use the
API offered by package `extension_discovery` to get this information.
If this package doesn't work for your use-case, then don't try to read the
contents of this folder. It may change, and will not remain stable.
Use package `extension_discovery`
---------------------------------
If you want to access information from this folder.
Feel free to delete this folder
-------------------------------
Files in this folder act as a cache, and the cache is discarded if the files
are older than the modification time of `.dart_tool/package_config.json`.
Hence, it should never be necessary to clear this cache manually, if you find a
need to do please file a bug.

View File

@@ -0,0 +1 @@
{"version":2,"entries":[{"package":"geosector_app","rootUri":"../","packageUri":"lib/"}]}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"inputs":[],"outputs":[]}

View File

@@ -0,0 +1,30 @@
// @dart=3.0
// Flutter web bootstrap script for package:geosector_app/main.dart.
//
// Generated file. Do not edit.
//
// ignore_for_file: type=lint
import 'dart:ui_web' as ui_web;
import 'dart:async';
import 'package:geosector_app/main.dart' as entrypoint;
import 'web_plugin_registrant.dart' as pluginRegistrant;
typedef _UnaryFunction = dynamic Function(List<String> args);
typedef _NullaryFunction = dynamic Function();
Future<void> main() async {
await ui_web.bootstrapEngine(
runApp: () {
if (entrypoint.main is _UnaryFunction) {
return (entrypoint.main as _UnaryFunction)(<String>[]);
}
return (entrypoint.main as _NullaryFunction)();
},
registerPlugins: () {
pluginRegistrant.registerPlugins();
},
);
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
["/Users/pierre/dev/geosector/app/build/web/*/index.html","/Users/pierre/dev/geosector/app/build/web/flutter_bootstrap.js","/Users/pierre/dev/geosector/app/build/web/main.dart.js","/Users/pierre/dev/geosector/app/build/web/assets/assets/images/geosector-logo.png","/Users/pierre/dev/geosector/app/build/web/assets/assets/images/logo-geosector-1024.png","/Users/pierre/dev/geosector/app/build/web/assets/assets/images/icon-geosector.svg","/Users/pierre/dev/geosector/app/build/web/assets/assets/images/logo_recu.png","/Users/pierre/dev/geosector/app/build/web/assets/assets/animations/geo_main.json","/Users/pierre/dev/geosector/app/build/web/assets/assets/fonts/Figtree-VariableFont_wght.ttf","/Users/pierre/dev/geosector/app/build/web/assets/packages/cupertino_icons/assets/CupertinoIcons.ttf","/Users/pierre/dev/geosector/app/build/web/assets/packages/flutter_map/lib/assets/flutter_map_logo.png","/Users/pierre/dev/geosector/app/build/web/assets/fonts/MaterialIcons-Regular.otf","/Users/pierre/dev/geosector/app/build/web/assets/shaders/ink_sparkle.frag","/Users/pierre/dev/geosector/app/build/web/assets/AssetManifest.json","/Users/pierre/dev/geosector/app/build/web/assets/AssetManifest.bin","/Users/pierre/dev/geosector/app/build/web/assets/AssetManifest.bin.json","/Users/pierre/dev/geosector/app/build/web/assets/FontManifest.json","/Users/pierre/dev/geosector/app/build/web/assets/NOTICES","/Users/pierre/dev/geosector/app/build/web/.DS_Store","/Users/pierre/dev/geosector/app/build/web/favicon-64.png","/Users/pierre/dev/geosector/app/build/web/favicon-16.png","/Users/pierre/dev/geosector/app/build/web/favicon.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-192.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-maskable-192.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-152.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-180.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-167.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-maskable-512.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-512.png","/Users/pierre/dev/geosector/app/build/web/manifest.json","/Users/pierre/dev/geosector/app/build/web/favicon-32.png","/Users/pierre/dev/geosector/app/build/web/flutter_service_worker.js"]

View File

@@ -0,0 +1 @@
/Users/pierre/dev/geosector/app/build/web/flutter_service_worker.js: /Users/pierre/dev/geosector/app/build/web/flutter_bootstrap.js /Users/pierre/dev/geosector/app/build/web/version.json /Users/pierre/dev/geosector/app/build/web/index.html /Users/pierre/dev/geosector/app/build/web/favicon-64.png /Users/pierre/dev/geosector/app/build/web/favicon-16.png /Users/pierre/dev/geosector/app/build/web/main.dart.js /Users/pierre/dev/geosector/app/build/web/flutter.js /Users/pierre/dev/geosector/app/build/web/favicon.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-192.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-maskable-192.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-152.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-180.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-167.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-maskable-512.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-512.png /Users/pierre/dev/geosector/app/build/web/manifest.json /Users/pierre/dev/geosector/app/build/web/favicon-32.png /Users/pierre/dev/geosector/app/build/web/assets/AssetManifest.json /Users/pierre/dev/geosector/app/build/web/assets/NOTICES /Users/pierre/dev/geosector/app/build/web/assets/FontManifest.json /Users/pierre/dev/geosector/app/build/web/assets/AssetManifest.bin.json /Users/pierre/dev/geosector/app/build/web/assets/packages/cupertino_icons/assets/CupertinoIcons.ttf /Users/pierre/dev/geosector/app/build/web/assets/packages/flutter_map/lib/assets/flutter_map_logo.png /Users/pierre/dev/geosector/app/build/web/assets/shaders/ink_sparkle.frag /Users/pierre/dev/geosector/app/build/web/assets/AssetManifest.bin /Users/pierre/dev/geosector/app/build/web/assets/fonts/MaterialIcons-Regular.otf /Users/pierre/dev/geosector/app/build/web/assets/assets/images/geosector-logo.png /Users/pierre/dev/geosector/app/build/web/assets/assets/images/logo-geosector-1024.png /Users/pierre/dev/geosector/app/build/web/assets/assets/images/icon-geosector.svg /Users/pierre/dev/geosector/app/build/web/assets/assets/images/logo_recu.png /Users/pierre/dev/geosector/app/build/web/assets/assets/fonts/Figtree-VariableFont_wght.ttf /Users/pierre/dev/geosector/app/build/web/assets/assets/animations/geo_main.json /Users/pierre/dev/geosector/app/build/web/canvaskit/skwasm.js /Users/pierre/dev/geosector/app/build/web/canvaskit/skwasm.js.symbols /Users/pierre/dev/geosector/app/build/web/canvaskit/canvaskit.js.symbols /Users/pierre/dev/geosector/app/build/web/canvaskit/skwasm.wasm /Users/pierre/dev/geosector/app/build/web/canvaskit/chromium/canvaskit.js.symbols /Users/pierre/dev/geosector/app/build/web/canvaskit/chromium/canvaskit.js /Users/pierre/dev/geosector/app/build/web/canvaskit/chromium/canvaskit.wasm /Users/pierre/dev/geosector/app/build/web/canvaskit/canvaskit.js /Users/pierre/dev/geosector/app/build/web/canvaskit/canvaskit.wasm

View File

@@ -0,0 +1 @@
{"inputs":["/Users/pierre/dev/flutter/packages/flutter_tools/lib/src/build_system/targets/web.dart"],"outputs":["/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/main.dart"]}

View File

@@ -0,0 +1,22 @@
// Flutter web plugin registrant file.
//
// Generated file. Do not edit.
//
// @dart = 2.13
// ignore_for_file: type=lint
import 'package:connectivity_plus/src/connectivity_plus_web.dart';
import 'package:geolocator_web/geolocator_web.dart';
import 'package:package_info_plus/src/package_info_plus_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
void registerPlugins([final Registrar? pluginRegistrar]) {
final Registrar registrar = pluginRegistrar ?? webPluginRegistrar;
ConnectivityPlusWebPlugin.registerWith(registrar);
GeolocatorPlugin.registerWith(registrar);
PackageInfoPlusWebPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);
registrar.registerMessageHandler();
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
/Users/pierre/dev/geosector/app/build/web/.DS_Store /Users/pierre/dev/geosector/app/build/web/favicon-64.png /Users/pierre/dev/geosector/app/build/web/favicon-16.png /Users/pierre/dev/geosector/app/build/web/favicon.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-192.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-maskable-192.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-152.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-180.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-167.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-maskable-512.png /Users/pierre/dev/geosector/app/build/web/icons/Icon-512.png /Users/pierre/dev/geosector/app/build/web/manifest.json /Users/pierre/dev/geosector/app/build/web/favicon-32.png: /Users/pierre/dev/geosector/app/web/index.html /Users/pierre/dev/geosector/app/web/.DS_Store /Users/pierre/dev/geosector/app/web/favicon-64.png /Users/pierre/dev/geosector/app/web/favicon-16.png /Users/pierre/dev/geosector/app/web/favicon.png /Users/pierre/dev/geosector/app/web/icons/Icon-192.png /Users/pierre/dev/geosector/app/web/icons/Icon-maskable-192.png /Users/pierre/dev/geosector/app/web/icons/Icon-152.png /Users/pierre/dev/geosector/app/web/icons/Icon-180.png /Users/pierre/dev/geosector/app/web/icons/Icon-167.png /Users/pierre/dev/geosector/app/web/icons/Icon-maskable-512.png /Users/pierre/dev/geosector/app/web/icons/Icon-512.png /Users/pierre/dev/geosector/app/web/manifest.json /Users/pierre/dev/geosector/app/web/favicon-32.png

View File

@@ -0,0 +1 @@
{"inputs":["/Users/pierre/dev/geosector/app/build/web/flutter_bootstrap.js","/Users/pierre/dev/geosector/app/build/web/version.json","/Users/pierre/dev/geosector/app/build/web/index.html","/Users/pierre/dev/geosector/app/build/web/favicon-64.png","/Users/pierre/dev/geosector/app/build/web/favicon-16.png","/Users/pierre/dev/geosector/app/build/web/main.dart.js","/Users/pierre/dev/geosector/app/build/web/flutter.js","/Users/pierre/dev/geosector/app/build/web/favicon.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-192.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-maskable-192.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-152.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-180.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-167.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-maskable-512.png","/Users/pierre/dev/geosector/app/build/web/icons/Icon-512.png","/Users/pierre/dev/geosector/app/build/web/manifest.json","/Users/pierre/dev/geosector/app/build/web/favicon-32.png","/Users/pierre/dev/geosector/app/build/web/assets/AssetManifest.json","/Users/pierre/dev/geosector/app/build/web/assets/NOTICES","/Users/pierre/dev/geosector/app/build/web/assets/FontManifest.json","/Users/pierre/dev/geosector/app/build/web/assets/AssetManifest.bin.json","/Users/pierre/dev/geosector/app/build/web/assets/packages/cupertino_icons/assets/CupertinoIcons.ttf","/Users/pierre/dev/geosector/app/build/web/assets/packages/flutter_map/lib/assets/flutter_map_logo.png","/Users/pierre/dev/geosector/app/build/web/assets/shaders/ink_sparkle.frag","/Users/pierre/dev/geosector/app/build/web/assets/AssetManifest.bin","/Users/pierre/dev/geosector/app/build/web/assets/fonts/MaterialIcons-Regular.otf","/Users/pierre/dev/geosector/app/build/web/assets/assets/images/geosector-logo.png","/Users/pierre/dev/geosector/app/build/web/assets/assets/images/logo-geosector-1024.png","/Users/pierre/dev/geosector/app/build/web/assets/assets/images/icon-geosector.svg","/Users/pierre/dev/geosector/app/build/web/assets/assets/images/logo_recu.png","/Users/pierre/dev/geosector/app/build/web/assets/assets/fonts/Figtree-VariableFont_wght.ttf","/Users/pierre/dev/geosector/app/build/web/assets/assets/animations/geo_main.json","/Users/pierre/dev/geosector/app/build/web/canvaskit/skwasm.js","/Users/pierre/dev/geosector/app/build/web/canvaskit/skwasm.js.symbols","/Users/pierre/dev/geosector/app/build/web/canvaskit/canvaskit.js.symbols","/Users/pierre/dev/geosector/app/build/web/canvaskit/skwasm.wasm","/Users/pierre/dev/geosector/app/build/web/canvaskit/chromium/canvaskit.js.symbols","/Users/pierre/dev/geosector/app/build/web/canvaskit/chromium/canvaskit.js","/Users/pierre/dev/geosector/app/build/web/canvaskit/chromium/canvaskit.wasm","/Users/pierre/dev/geosector/app/build/web/canvaskit/canvaskit.js","/Users/pierre/dev/geosector/app/build/web/canvaskit/canvaskit.wasm"],"outputs":["/Users/pierre/dev/geosector/app/build/web/flutter_service_worker.js"]}

View File

@@ -0,0 +1 @@
{"inputs":["/Users/pierre/dev/flutter/bin/cache/engine.stamp"],"outputs":["/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/flutter.js","/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/canvaskit/skwasm.js","/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/canvaskit/skwasm.js.symbols","/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/canvaskit/canvaskit.js.symbols","/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/canvaskit/skwasm.wasm","/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/canvaskit/chromium/canvaskit.js.symbols","/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/canvaskit/chromium/canvaskit.js","/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/canvaskit/chromium/canvaskit.wasm","/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/canvaskit/canvaskit.js","/Users/pierre/dev/geosector/app/.dart_tool/flutter_build/41acb28aedc1da36af63ba5cb8859018/canvaskit/canvaskit.wasm"]}

View File

@@ -0,0 +1 @@
{"inputs":["/Users/pierre/dev/geosector/app/web/*/index.html","/Users/pierre/dev/geosector/app/web/flutter_bootstrap.js","/Users/pierre/dev/flutter/bin/cache/engine.stamp"],"outputs":["/Users/pierre/dev/geosector/app/build/web/*/index.html","/Users/pierre/dev/geosector/app/build/web/flutter_bootstrap.js"],"buildKey":"[{\"compileTarget\":\"dart2js\",\"renderer\":\"canvaskit\",\"mainJsPath\":\"main.dart.js\"}]"}

View File

@@ -0,0 +1,958 @@
{
"configVersion": 2,
"packages": [
{
"name": "_fe_analyzer_shared",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-76.0.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "_macros",
"rootUri": "file:///Users/pierre/dev/flutter/bin/cache/dart-sdk/pkg/_macros",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "analyzer",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/analyzer-6.11.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "archive",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/archive-4.0.5",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "args",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/args-2.7.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "async",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/async-2.13.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "boolean_selector",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/boolean_selector-2.1.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "build",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/build-2.4.2",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "build_config",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/build_config-1.1.2",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "build_daemon",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/build_daemon-4.0.4",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "build_resolvers",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/build_resolvers-2.4.4",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "build_runner",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/build_runner-2.4.15",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "build_runner_core",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/build_runner_core-8.0.0",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "built_collection",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/built_collection-5.1.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "built_value",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/built_value-8.9.5",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "characters",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/characters-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "charcode",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/charcode-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "checked_yaml",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/checked_yaml-2.0.3",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "cli_util",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/cli_util-0.4.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "clock",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/clock-1.1.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "code_builder",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/code_builder-4.10.1",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "collection",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/collection-1.19.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "connectivity_plus",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.3",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "connectivity_plus_platform_interface",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interface-2.0.1",
"packageUri": "lib/",
"languageVersion": "2.18"
},
{
"name": "convert",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/convert-3.1.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "crypto",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "csslib",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/csslib-1.0.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "cupertino_icons",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/cupertino_icons-1.0.8",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "dart_earcut",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/dart_earcut-1.2.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "dart_style",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/dart_style-2.3.8",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "dbus",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/dbus-0.7.11",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "dio",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1",
"packageUri": "lib/",
"languageVersion": "2.18"
},
{
"name": "dio_web_adapter",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/dio_web_adapter-2.1.1",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "equatable",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/equatable-2.0.7",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "event_bus",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/event_bus-2.0.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "fake_async",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/fake_async-1.3.3",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "ffi",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/ffi-2.1.4",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "file",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/file-7.0.1",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "fixnum",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/fixnum-1.1.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "fl_chart",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/fl_chart-0.70.2",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "flutter",
"rootUri": "file:///Users/pierre/dev/flutter/packages/flutter",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "flutter_launcher_icons",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_launcher_icons-0.13.1",
"packageUri": "lib/",
"languageVersion": "2.18"
},
{
"name": "flutter_lints",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_lints-3.0.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "flutter_local_notifications",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications-19.1.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "flutter_local_notifications_linux",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications_linux-6.0.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "flutter_local_notifications_platform_interface",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications_platform_interface-9.0.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "flutter_local_notifications_windows",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications_windows-1.0.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "flutter_map",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_map-8.1.1",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "flutter_svg",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_svg-2.0.17",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "flutter_test",
"rootUri": "file:///Users/pierre/dev/flutter/packages/flutter_test",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "flutter_web_plugins",
"rootUri": "file:///Users/pierre/dev/flutter/packages/flutter_web_plugins",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "frontend_server_client",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/frontend_server_client-4.0.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "geolocator",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator-13.0.4",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "geolocator_android",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_android-4.6.2",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "geolocator_apple",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_apple-2.3.13",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "geolocator_platform_interface",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_platform_interface-4.2.6",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "geolocator_web",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_web-4.1.3",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "geolocator_windows",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_windows-0.2.5",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "glob",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/glob-2.1.3",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "go_router",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/go_router-14.8.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "google_fonts",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/google_fonts-6.2.1",
"packageUri": "lib/",
"languageVersion": "2.14"
},
{
"name": "graphs",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/graphs-2.3.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "hive",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/hive-2.2.3",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "hive_flutter",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "hive_generator",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/hive_generator-2.0.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "html",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/html-0.15.6",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "http",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/http-1.3.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "http_multi_server",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/http_multi_server-3.2.2",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "http_parser",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "image",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/image-4.5.4",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "intl",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "io",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/io-1.0.5",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "js",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/js-0.6.7",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "json_annotation",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/json_annotation-4.9.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "latlong2",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/latlong2-0.9.1",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "leak_tracker",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/leak_tracker-10.0.9",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "leak_tracker_flutter_testing",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/leak_tracker_flutter_testing-3.0.9",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "leak_tracker_testing",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/leak_tracker_testing-3.0.1",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "lints",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/lints-3.0.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "lists",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/lists-1.0.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "logger",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/logger-2.5.0",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "logging",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/logging-1.3.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "macros",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/macros-0.1.3-main.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "matcher",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/matcher-0.12.17",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "material_color_utilities",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/material_color_utilities-0.11.1",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "meta",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/meta-1.16.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "mgrs_dart",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "mime",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "mqtt5_client",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/mqtt5_client-4.11.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "nested",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/nested-1.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "nm",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/nm-0.5.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "package_config",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/package_config-2.2.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "package_info_plus",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "package_info_plus_platform_interface",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/package_info_plus_platform_interface-3.2.0",
"packageUri": "lib/",
"languageVersion": "2.18"
},
{
"name": "path",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/path-1.9.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "path_parsing",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/path_parsing-1.1.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "path_provider",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider-2.1.5",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "path_provider_android",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_android-2.2.16",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "path_provider_foundation",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "path_provider_linux",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "path_provider_platform_interface",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_platform_interface-2.1.2",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "path_provider_windows",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "petitparser",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/petitparser-6.1.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "platform",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/platform-3.1.6",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "plugin_platform_interface",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "polylabel",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/polylabel-1.0.1",
"packageUri": "lib/",
"languageVersion": "2.13"
},
{
"name": "pool",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/pool-1.5.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "posix",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/posix-6.0.1",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "proj4dart",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/proj4dart-2.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "provider",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/provider-6.1.5",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "pub_semver",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/pub_semver-2.2.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "pubspec_parse",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/pubspec_parse-1.5.0",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "retry",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/retry-3.1.2",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "shelf",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/shelf-1.4.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "shelf_web_socket",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/shelf_web_socket-3.0.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "sky_engine",
"rootUri": "file:///Users/pierre/dev/flutter/bin/cache/pkg/sky_engine",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "source_gen",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/source_gen-1.5.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "source_helper",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/source_helper-1.3.5",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "source_span",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/source_span-1.10.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "sprintf",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/sprintf-7.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "stack_trace",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/stack_trace-1.12.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "stream_channel",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/stream_channel-2.1.4",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "stream_transform",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/stream_transform-2.1.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "string_scanner",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "syncfusion_flutter_charts",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-29.1.35",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "syncfusion_flutter_core",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-29.1.35",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "term_glyph",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/term_glyph-1.2.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "test_api",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/test_api-0.7.4",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "timezone",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/timezone-0.10.1",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "timing",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/timing-1.0.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "typed_data",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/typed_data-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "unicode",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/unicode-0.3.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "universal_html",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/universal_html-2.2.4",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "universal_io",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/universal_io-2.2.2",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "url_launcher",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher-6.3.1",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "url_launcher_android",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.15",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "url_launcher_ios",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.2",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "url_launcher_linux",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "url_launcher_macos",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.2",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "url_launcher_platform_interface",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_platform_interface-2.3.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "url_launcher_web",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.0",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "url_launcher_windows",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.4",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "uuid",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/uuid-4.5.1",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "vector_graphics",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_graphics-1.1.18",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "vector_graphics_codec",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_graphics_codec-1.1.13",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "vector_graphics_compiler",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_graphics_compiler-1.1.16",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "vector_math",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4",
"packageUri": "lib/",
"languageVersion": "2.14"
},
{
"name": "vm_service",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/vm_service-15.0.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "watcher",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/watcher-1.1.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "web",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/web-1.1.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "web_socket",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/web_socket-0.1.6",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "web_socket_channel",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/web_socket_channel-3.0.2",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "win32",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/win32-5.13.0",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "wkt_parser",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/wkt_parser-2.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "xdg_directories",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/xdg_directories-1.1.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "xml",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/xml-6.5.0",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "yaml",
"rootUri": "file:///Users/pierre/.pub-cache/hosted/pub.dev/yaml-3.1.3",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "geosector_app",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "3.0"
}
],
"generator": "pub",
"generatorVersion": "3.8.1",
"flutterRoot": "file:///Users/pierre/dev/flutter",
"flutterVersion": "3.32.1",
"pubCache": "file:///Users/pierre/.pub-cache"
}

View File

@@ -0,0 +1,633 @@
_fe_analyzer_shared
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-76.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-76.0.0/lib/
analyzer
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/analyzer-6.11.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/analyzer-6.11.0/lib/
archive
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/archive-4.0.5/
file:///Users/pierre/.pub-cache/hosted/pub.dev/archive-4.0.5/lib/
args
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/args-2.7.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/args-2.7.0/lib/
async
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/async-2.13.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/async-2.13.0/lib/
boolean_selector
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/boolean_selector-2.1.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/boolean_selector-2.1.2/lib/
build
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/build-2.4.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/build-2.4.2/lib/
build_config
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_config-1.1.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_config-1.1.2/lib/
build_daemon
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_daemon-4.0.4/
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_daemon-4.0.4/lib/
build_resolvers
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_resolvers-2.4.4/
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_resolvers-2.4.4/lib/
build_runner
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_runner-2.4.15/
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_runner-2.4.15/lib/
build_runner_core
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_runner_core-8.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/build_runner_core-8.0.0/lib/
built_collection
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/built_collection-5.1.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/built_collection-5.1.1/lib/
built_value
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/built_value-8.9.5/
file:///Users/pierre/.pub-cache/hosted/pub.dev/built_value-8.9.5/lib/
characters
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/characters-1.4.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/characters-1.4.0/lib/
charcode
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/charcode-1.4.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/charcode-1.4.0/lib/
checked_yaml
2.19
file:///Users/pierre/.pub-cache/hosted/pub.dev/checked_yaml-2.0.3/
file:///Users/pierre/.pub-cache/hosted/pub.dev/checked_yaml-2.0.3/lib/
cli_util
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/cli_util-0.4.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/cli_util-0.4.2/lib/
clock
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/clock-1.1.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/clock-1.1.2/lib/
code_builder
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/code_builder-4.10.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/code_builder-4.10.1/lib/
collection
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/collection-1.19.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/collection-1.19.1/lib/
connectivity_plus
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.3/
file:///Users/pierre/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.3/lib/
connectivity_plus_platform_interface
2.18
file:///Users/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interface-2.0.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interface-2.0.1/lib/
convert
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/convert-3.1.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/convert-3.1.2/lib/
crypto
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6/
file:///Users/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6/lib/
csslib
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/csslib-1.0.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/csslib-1.0.2/lib/
cupertino_icons
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/cupertino_icons-1.0.8/
file:///Users/pierre/.pub-cache/hosted/pub.dev/cupertino_icons-1.0.8/lib/
dart_earcut
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/dart_earcut-1.2.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/dart_earcut-1.2.0/lib/
dart_style
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/dart_style-2.3.8/
file:///Users/pierre/.pub-cache/hosted/pub.dev/dart_style-2.3.8/lib/
dbus
2.17
file:///Users/pierre/.pub-cache/hosted/pub.dev/dbus-0.7.11/
file:///Users/pierre/.pub-cache/hosted/pub.dev/dbus-0.7.11/lib/
dio
2.18
file:///Users/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/
dio_web_adapter
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/dio_web_adapter-2.1.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/dio_web_adapter-2.1.1/lib/
equatable
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/equatable-2.0.7/
file:///Users/pierre/.pub-cache/hosted/pub.dev/equatable-2.0.7/lib/
event_bus
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/event_bus-2.0.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/event_bus-2.0.1/lib/
fake_async
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/fake_async-1.3.3/
file:///Users/pierre/.pub-cache/hosted/pub.dev/fake_async-1.3.3/lib/
ffi
3.7
file:///Users/pierre/.pub-cache/hosted/pub.dev/ffi-2.1.4/
file:///Users/pierre/.pub-cache/hosted/pub.dev/ffi-2.1.4/lib/
file
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/file-7.0.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/file-7.0.1/lib/
fixnum
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/fixnum-1.1.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/fixnum-1.1.1/lib/
fl_chart
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/fl_chart-0.70.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/fl_chart-0.70.2/lib/
flutter_launcher_icons
2.18
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_launcher_icons-0.13.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_launcher_icons-0.13.1/lib/
flutter_lints
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_lints-3.0.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_lints-3.0.2/lib/
flutter_local_notifications
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications-19.1.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications-19.1.0/lib/
flutter_local_notifications_linux
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications_linux-6.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications_linux-6.0.0/lib/
flutter_local_notifications_platform_interface
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications_platform_interface-9.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications_platform_interface-9.0.0/lib/
flutter_local_notifications_windows
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications_windows-1.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_local_notifications_windows-1.0.0/lib/
flutter_map
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_map-8.1.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_map-8.1.1/lib/
flutter_svg
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_svg-2.0.17/
file:///Users/pierre/.pub-cache/hosted/pub.dev/flutter_svg-2.0.17/lib/
frontend_server_client
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/frontend_server_client-4.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/frontend_server_client-4.0.0/lib/
geolocator
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator-13.0.4/
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator-13.0.4/lib/
geolocator_android
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_android-4.6.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_android-4.6.2/lib/
geolocator_apple
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_apple-2.3.13/
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_apple-2.3.13/lib/
geolocator_platform_interface
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_platform_interface-4.2.6/
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_platform_interface-4.2.6/lib/
geolocator_web
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_web-4.1.3/
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_web-4.1.3/lib/
geolocator_windows
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_windows-0.2.5/
file:///Users/pierre/.pub-cache/hosted/pub.dev/geolocator_windows-0.2.5/lib/
glob
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/glob-2.1.3/
file:///Users/pierre/.pub-cache/hosted/pub.dev/glob-2.1.3/lib/
go_router
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/go_router-14.8.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/go_router-14.8.1/lib/
google_fonts
2.14
file:///Users/pierre/.pub-cache/hosted/pub.dev/google_fonts-6.2.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/google_fonts-6.2.1/lib/
graphs
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/graphs-2.3.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/graphs-2.3.2/lib/
hive
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/hive-2.2.3/
file:///Users/pierre/.pub-cache/hosted/pub.dev/hive-2.2.3/lib/
hive_flutter
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0/lib/
hive_generator
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/hive_generator-2.0.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/hive_generator-2.0.1/lib/
html
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/html-0.15.6/
file:///Users/pierre/.pub-cache/hosted/pub.dev/html-0.15.6/lib/
http
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/http-1.3.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/http-1.3.0/lib/
http_multi_server
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/http_multi_server-3.2.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/http_multi_server-3.2.2/lib/
http_parser
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2/lib/
image
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/image-4.5.4/
file:///Users/pierre/.pub-cache/hosted/pub.dev/image-4.5.4/lib/
intl
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/lib/
io
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/io-1.0.5/
file:///Users/pierre/.pub-cache/hosted/pub.dev/io-1.0.5/lib/
js
2.19
file:///Users/pierre/.pub-cache/hosted/pub.dev/js-0.6.7/
file:///Users/pierre/.pub-cache/hosted/pub.dev/js-0.6.7/lib/
json_annotation
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/json_annotation-4.9.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/json_annotation-4.9.0/lib/
latlong2
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/latlong2-0.9.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/latlong2-0.9.1/lib/
leak_tracker
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/leak_tracker-10.0.9/
file:///Users/pierre/.pub-cache/hosted/pub.dev/leak_tracker-10.0.9/lib/
leak_tracker_flutter_testing
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/leak_tracker_flutter_testing-3.0.9/
file:///Users/pierre/.pub-cache/hosted/pub.dev/leak_tracker_flutter_testing-3.0.9/lib/
leak_tracker_testing
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/leak_tracker_testing-3.0.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/leak_tracker_testing-3.0.1/lib/
lints
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/lints-3.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/lints-3.0.0/lib/
lists
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/lists-1.0.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/lists-1.0.1/lib/
logger
2.17
file:///Users/pierre/.pub-cache/hosted/pub.dev/logger-2.5.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/logger-2.5.0/lib/
logging
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/logging-1.3.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/logging-1.3.0/lib/
macros
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/macros-0.1.3-main.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/macros-0.1.3-main.0/lib/
matcher
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/matcher-0.12.17/
file:///Users/pierre/.pub-cache/hosted/pub.dev/matcher-0.12.17/lib/
material_color_utilities
2.17
file:///Users/pierre/.pub-cache/hosted/pub.dev/material_color_utilities-0.11.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/material_color_utilities-0.11.1/lib/
meta
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/meta-1.16.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/meta-1.16.0/lib/
mgrs_dart
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0/lib/
mime
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/
mqtt5_client
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/mqtt5_client-4.11.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/mqtt5_client-4.11.0/lib/
nested
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/nested-1.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/nested-1.0.0/lib/
nm
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/nm-0.5.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/nm-0.5.0/lib/
package_config
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/package_config-2.2.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/package_config-2.2.0/lib/
package_info_plus
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/lib/
package_info_plus_platform_interface
2.18
file:///Users/pierre/.pub-cache/hosted/pub.dev/package_info_plus_platform_interface-3.2.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/package_info_plus_platform_interface-3.2.0/lib/
path
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/path-1.9.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/path-1.9.1/lib/
path_parsing
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_parsing-1.1.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_parsing-1.1.0/lib/
path_provider
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider-2.1.5/
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider-2.1.5/lib/
path_provider_android
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_android-2.2.16/
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_android-2.2.16/lib/
path_provider_foundation
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/lib/
path_provider_linux
2.19
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/lib/
path_provider_platform_interface
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_platform_interface-2.1.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_platform_interface-2.1.2/lib/
path_provider_windows
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/lib/
petitparser
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/petitparser-6.1.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/petitparser-6.1.0/lib/
platform
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/platform-3.1.6/
file:///Users/pierre/.pub-cache/hosted/pub.dev/platform-3.1.6/lib/
plugin_platform_interface
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8/
file:///Users/pierre/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8/lib/
polylabel
2.13
file:///Users/pierre/.pub-cache/hosted/pub.dev/polylabel-1.0.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/polylabel-1.0.1/lib/
pool
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/pool-1.5.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/pool-1.5.1/lib/
posix
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/posix-6.0.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/posix-6.0.1/lib/
proj4dart
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/proj4dart-2.1.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/proj4dart-2.1.0/lib/
provider
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/provider-6.1.5/
file:///Users/pierre/.pub-cache/hosted/pub.dev/provider-6.1.5/lib/
pub_semver
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/pub_semver-2.2.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/pub_semver-2.2.0/lib/
pubspec_parse
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/pubspec_parse-1.5.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/pubspec_parse-1.5.0/lib/
retry
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/retry-3.1.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/retry-3.1.2/lib/
shelf
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/shelf-1.4.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/shelf-1.4.2/lib/
shelf_web_socket
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/shelf_web_socket-3.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/shelf_web_socket-3.0.0/lib/
source_gen
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/source_gen-1.5.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/source_gen-1.5.0/lib/
source_helper
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/source_helper-1.3.5/
file:///Users/pierre/.pub-cache/hosted/pub.dev/source_helper-1.3.5/lib/
source_span
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/source_span-1.10.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/source_span-1.10.1/lib/
sprintf
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/sprintf-7.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/sprintf-7.0.0/lib/
stack_trace
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/stack_trace-1.12.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/stack_trace-1.12.1/lib/
stream_channel
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/stream_channel-2.1.4/
file:///Users/pierre/.pub-cache/hosted/pub.dev/stream_channel-2.1.4/lib/
stream_transform
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/stream_transform-2.1.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/stream_transform-2.1.1/lib/
string_scanner
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/
syncfusion_flutter_charts
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-29.1.35/
file:///Users/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-29.1.35/lib/
syncfusion_flutter_core
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-29.1.35/
file:///Users/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-29.1.35/lib/
term_glyph
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/term_glyph-1.2.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/term_glyph-1.2.2/lib/
test_api
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/test_api-0.7.4/
file:///Users/pierre/.pub-cache/hosted/pub.dev/test_api-0.7.4/lib/
timezone
2.19
file:///Users/pierre/.pub-cache/hosted/pub.dev/timezone-0.10.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/timezone-0.10.1/lib/
timing
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/timing-1.0.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/timing-1.0.2/lib/
typed_data
3.5
file:///Users/pierre/.pub-cache/hosted/pub.dev/typed_data-1.4.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/typed_data-1.4.0/lib/
unicode
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/unicode-0.3.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/unicode-0.3.1/lib/
universal_html
2.17
file:///Users/pierre/.pub-cache/hosted/pub.dev/universal_html-2.2.4/
file:///Users/pierre/.pub-cache/hosted/pub.dev/universal_html-2.2.4/lib/
universal_io
2.17
file:///Users/pierre/.pub-cache/hosted/pub.dev/universal_io-2.2.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/universal_io-2.2.2/lib/
url_launcher
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher-6.3.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher-6.3.1/lib/
url_launcher_android
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.15/
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.15/lib/
url_launcher_ios
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.2/lib/
url_launcher_linux
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/lib/
url_launcher_macos
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.2/lib/
url_launcher_platform_interface
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_platform_interface-2.3.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_platform_interface-2.3.2/lib/
url_launcher_web
3.6
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.0/lib/
url_launcher_windows
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.4/
file:///Users/pierre/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.4/lib/
uuid
3.0
file:///Users/pierre/.pub-cache/hosted/pub.dev/uuid-4.5.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/uuid-4.5.1/lib/
vector_graphics
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_graphics-1.1.18/
file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_graphics-1.1.18/lib/
vector_graphics_codec
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_graphics_codec-1.1.13/
file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_graphics_codec-1.1.13/lib/
vector_graphics_compiler
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_graphics_compiler-1.1.16/
file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_graphics_compiler-1.1.16/lib/
vector_math
2.14
file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4/
file:///Users/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4/lib/
vm_service
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/vm_service-15.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/vm_service-15.0.0/lib/
watcher
3.1
file:///Users/pierre/.pub-cache/hosted/pub.dev/watcher-1.1.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/watcher-1.1.1/lib/
web
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/web-1.1.1/
file:///Users/pierre/.pub-cache/hosted/pub.dev/web-1.1.1/lib/
web_socket
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/web_socket-0.1.6/
file:///Users/pierre/.pub-cache/hosted/pub.dev/web_socket-0.1.6/lib/
web_socket_channel
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/web_socket_channel-3.0.2/
file:///Users/pierre/.pub-cache/hosted/pub.dev/web_socket_channel-3.0.2/lib/
win32
3.7
file:///Users/pierre/.pub-cache/hosted/pub.dev/win32-5.13.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/win32-5.13.0/lib/
wkt_parser
2.12
file:///Users/pierre/.pub-cache/hosted/pub.dev/wkt_parser-2.0.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/wkt_parser-2.0.0/lib/
xdg_directories
3.3
file:///Users/pierre/.pub-cache/hosted/pub.dev/xdg_directories-1.1.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/xdg_directories-1.1.0/lib/
xml
3.2
file:///Users/pierre/.pub-cache/hosted/pub.dev/xml-6.5.0/
file:///Users/pierre/.pub-cache/hosted/pub.dev/xml-6.5.0/lib/
yaml
3.4
file:///Users/pierre/.pub-cache/hosted/pub.dev/yaml-3.1.3/
file:///Users/pierre/.pub-cache/hosted/pub.dev/yaml-3.1.3/lib/
_macros
3.5
file:///Users/pierre/dev/flutter/bin/cache/dart-sdk/pkg/_macros/
file:///Users/pierre/dev/flutter/bin/cache/dart-sdk/pkg/_macros/lib/
sky_engine
3.7
file:///Users/pierre/dev/flutter/bin/cache/pkg/sky_engine/
file:///Users/pierre/dev/flutter/bin/cache/pkg/sky_engine/lib/
flutter
3.7
file:///Users/pierre/dev/flutter/packages/flutter/
file:///Users/pierre/dev/flutter/packages/flutter/lib/
flutter_test
3.7
file:///Users/pierre/dev/flutter/packages/flutter_test/
file:///Users/pierre/dev/flutter/packages/flutter_test/lib/
flutter_web_plugins
3.7
file:///Users/pierre/dev/flutter/packages/flutter_web_plugins/
file:///Users/pierre/dev/flutter/packages/flutter_web_plugins/lib/
geosector_app
3.0
file:///Users/pierre/dev/geosector/app/
file:///Users/pierre/dev/geosector/app/lib/
2

File diff suppressed because it is too large Load Diff

1
app/.dart_tool/version Normal file
View File

@@ -0,0 +1 @@
3.32.1

File diff suppressed because one or more lines are too long

47
app/.gitignore vendored
View File

@@ -1,47 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
sync_config.jsonc

View File

@@ -6,7 +6,7 @@
"editor.minimap.enabled": true, // On veut voir la minimap
"editor.minimap.showSlider": "always", // On veut voir la minimap
"editor.minimap.size": "fill", // On veut voir la minimap
"editor.minimap.scale": 2,
"editor.minimap.scale": 1,
"editor.tokenColorCustomizations": {
"textMateRules": [
{
@@ -23,12 +23,14 @@
},
"editor.minimap.renderCharacters": true,
"editor.minimap.maxColumn": 120,
"breadcrumbs.enabled": false,
"breadcrumbs.enabled": true,
// -- Tabs
"workbench.editor.wrapTabs": true, // On veut voir les tabs
"workbench.editor.tabSizing": "shrink", // On veut voir les tabs
"workbench.editor.pinnedTabSizing": "compact",
"workbench.editor.enablePreview": false, // Un clic sur un fichier l'ouvre
// -- Sidebar
"workbench.tree.indent": 15, // Indente plus pour plus de clarté dans la sidebar
"workbench.tree.renderIndentGuides": "always",
@@ -44,7 +46,8 @@
"editor.guides.bracketPairs": "active",
// Ergonomie
"editor.wordWrap": "off",
"editor.rulers": [],
"editor.rulers": [300],
"editor.wordWrapColumn": 300,
"editor.suggest.insertMode": "replace", // L'autocomplétion remplace le mot en cours
"editor.acceptSuggestionOnCommitCharacter": false, // Evite que l'autocomplétion soit accepté lors d'un . par exemple
"editor.formatOnSave": true,
@@ -59,21 +62,13 @@
},
"intelephense.format.braces": "k&r",
"intelephense.format.enable": true,
"php.validate.executablePath": "/opt/homebrew/opt/php@8.3/bin/php",
"php.executablePath": "/opt/homebrew/opt/php@8.3/bin/php",
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnPaste": true
},
"[dart]": {
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.rulers": [
80
],
"editor.selectionHighlight": false,
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": "off"
},
"prettier.printWidth": 360,
"prettier.semi": true,
"prettier.singleQuote": true,
@@ -82,6 +77,19 @@
"explorer.autoReveal": false,
"explorer.confirmDragAndDrop": false,
"emmet.triggerExpansionOnTab": true,
"emmet.includeLanguages": {
"javascript": "javascript",
"php": "php",
"svelte": "html",
"dart": "dart"
},
"problems.decorations.enabled": true,
"explorer.decorations.colors": true,
"explorer.decorations.badges": true,
"php.validate.enable": true,
"php.suggest.basic": false,
"dart.analysisExcludedFolders": [],
"dart.enableSdkFormatter": true,
// Fichiers
"files.defaultLanguage": "markdown",
"files.autoSaveWorkspaceFilesOnly": true,
@@ -92,7 +100,7 @@
"javascript.preferences.importModuleSpecifierEnding": "js",
"typescript.preferences.importModuleSpecifierEnding": "js",
// Extensions
"tailwindCSS.experimental.configFile": "frontend/tailwind.config.js",
"tailwindCSS.experimental.configFile": "web/tailwind.config.js",
"editor.quickSuggestions": {
"strings": true
},
@@ -104,27 +112,45 @@
"**/*.svelte"
],
"svelte.plugin.svelte.diagnostics.enable": false,
"problems.decorations.enabled": false,
"js/ts.implicitProjectConfig.checkJs": false,
"svelte.enable-ts-plugin": false,
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#405978",
"activityBar.background": "#405978",
"activityBar.foreground": "#e7e7e7",
"activityBar.inactiveForeground": "#e7e7e799",
"activityBarBadge.background": "#bc829c",
"activityBarBadge.foreground": "#15202b",
"commandCenter.border": "#e7e7e799",
"sash.hoverBorder": "#405978",
"statusBar.background": "#2e4057",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#405978",
"statusBarItem.remoteBackground": "#2e4057",
"statusBarItem.remoteForeground": "#e7e7e7",
"titleBar.activeBackground": "#2e4057",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#2e405799",
"titleBar.inactiveForeground": "#e7e7e799"
"cline.autoApproveLimit": 100,
"cline.autoApproveRequests": true,
"cline.enableMemoryBank": true,
"cline.includeSnippetsFromMemory": true,
"cline.contextLength": 10000,
"cline.autoFormat": true,
"cline.primaryDocumentationFile": ".cline",
"cline.gitIntegration": true,
"cline.projectStructure": {
"api": "php",
"app": "flutter",
"web": "svelte"
},
"peacock.color": "#2E4057",
"cline.referenceFiles": {
"database": "docs/db-resalice.dump",
"apiEndpoints": "docs/api_endpoints.md",
"architecture": "docs/architecture.md"
},
"cline.databaseSchema": "docs/db-resalice.dump",
"peacock.color": "#42b883",
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#65c89b",
"activityBar.background": "#65c89b",
"activityBar.foreground": "#15202b",
"activityBar.inactiveForeground": "#15202b99",
"activityBarBadge.background": "#945bc4",
"activityBarBadge.foreground": "#e7e7e7",
"commandCenter.border": "#15202b99",
"sash.hoverBorder": "#65c89b",
"statusBar.background": "#42b883",
"statusBar.foreground": "#15202b",
"statusBarItem.hoverBackground": "#359268",
"statusBarItem.remoteBackground": "#42b883",
"statusBarItem.remoteForeground": "#15202b",
"titleBar.activeBackground": "#42b883",
"titleBar.activeForeground": "#15202b",
"titleBar.inactiveBackground": "#42b88399",
"titleBar.inactiveForeground": "#15202b99"
}
}

428
app/README-APP.md Normal file
View File

@@ -0,0 +1,428 @@
# GEOSECTOR
Une application puissante et intuitive de gestion de vos distributions de calendriers par secteurs géographiques pour les amicales de pompiers.
## 📱 Présentation
GEOSECTOR est une application Flutter qui permet aux amicales de pompiers de gérer efficacement la distribution de calendriers par secteurs géographiques.
### Fonctionnalités principales
- **Gestion des secteurs géographiques** avec cartes interactives (Flutter Map)
- **Suivi des passages** avec géolocalisation
- **Interface différenciée** : utilisateur et administrateur
- **Authentification sécurisée** avec gestion des rôles@
- **Chat intégré** avec système de notifications
- **Stockage local** avec synchronisation en ligne
- **Support multi-plateforme** : Web, iOS, Android
## 🏗️ Architecture
### Stack technique
- **Framework** : Flutter 3.32
- **Routing** : Go Router
- **Base de données locale** : Hive
- **Gestion d'état** : Provider + Repository Pattern
- **Cartes** : Flutter Map avec Mapbox
- **API** : Dio pour les requêtes HTTP
- **Chat** : MQTT5 Client
### Architecture de données : Hive + Provider + Repository
L'application utilise une architecture en couches pour la gestion des données :
```
UI Layer (Widgets)
Provider Layer (State Management)
Repository Layer (Business Logic)
Data Layer (Hive + API)
```
#### Couches de l'architecture
1. **UI Layer** : Widgets Flutter qui affichent les données
2. **Provider Layer** : Gestion d'état avec ChangeNotifier
3. **Repository Layer** : Logique métier et orchestration des sources de données
4. **Data Layer** : Stockage local (Hive) et API distante
### Structure des dossiers
```
lib/
├── core/
│ ├── config/
│ ├── constants/
│ ├── errors/
│ ├── network/
│ └── utils/
├── data/
│ ├── datasources/
│ ├── models/
│ └── repositories/
├── domain/
│ ├── entities/
│ ├── repositories/
│ └── usecases/
├── presentation/
│ ├── pages/
│ ├── providers/
│ ├── widgets/
│ └── theme/
└── main.dart
```
## 🚀 Installation et configuration
### Prérequis
- Flutter 3.x
- Dart SDK
- Android Studio / VS Code
- Émulateur ou appareil physique
### Installation
1. **Cloner le repository**
```bash
git clone https://github.com/votre-repo/geosector.git
cd geosector
```
2. **Installer les dépendances**
```bash
flutter pub get
```
3. **Configuration des clés API**
Créer un fichier `.env` à la racine :
```env
MAPBOX_ACCESS_TOKEN=your_mapbox_token
API_BASE_URL=https://your-api.com
MQTT_BROKER_URL=your-mqtt-broker
```
4. **Lancer l'application**
```bash
flutter run
```
## 📦 Dépendances principales
```yaml
dependencies:
flutter:
sdk: flutter
# État et navigation
provider: ^6.1.1
go_router: ^12.1.3
# Stockage local
hive: ^2.2.3
hive_flutter: ^1.1.0
# Réseau
dio: ^5.4.0
# Cartes
flutter_map: ^6.1.0
geolocator: ^10.1.0
# Chat
mqtt5_client: ^4.2.0
# UI
flutter_screenutil: ^5.9.0
cached_network_image: ^3.3.0
dev_dependencies:
hive_generator: ^2.0.1
build_runner: ^2.4.7
```
## 🗄️ Modèles de données
### Secteur
```dart
@HiveType(typeId: 0)
class Secteur extends HiveObject {
@HiveField(0)
String id;
@HiveField(1)
String nom;
@HiveField(2)
List<LatLng> polygone;
@HiveField(3)
String couleur;
@HiveField(4)
int nombreCalendriers;
@HiveField(5)
DateTime dateCreation;
}
```
### Passage
```dart
@HiveType(typeId: 1)
class Passage extends HiveObject {
@HiveField(0)
String id;
@HiveField(1)
String secteurId;
@HiveField(2)
String utilisateurId;
@HiveField(3)
DateTime datePassage;
@HiveField(4)
LatLng position;
@HiveField(5)
int calendriersDistribues;
@HiveField(6)
String commentaire;
@HiveField(7)
List<String> photos;
}
```
### Utilisateur
```dart
@HiveType(typeId: 2)
class Utilisateur extends HiveObject {
@HiveField(0)
String id;
@HiveField(1)
String nom;
@HiveField(2)
String email;
@HiveField(3)
String role; // 'admin' ou 'user'
@HiveField(4)
List<String> secteursAssignes;
@HiveField(5)
DateTime dernierLogin;
}
```
## 🔧 Gestion d'état avec Provider
### SecteurProvider
```dart
class SecteurProvider extends ChangeNotifier {
final SecteurRepository _repository;
List<Secteur> _secteurs = [];
bool _isLoading = false;
List<Secteur> get secteurs => _secteurs;
bool get isLoading => _isLoading;
Future<void> loadSecteurs() async {
_isLoading = true;
notifyListeners();
try {
_secteurs = await _repository.getAllSecteurs();
} catch (e) {
// Gestion d'erreur
} finally {
_isLoading = false;
notifyListeners();
}
}
}
```
## 🗺️ Cartes et géolocalisation
### Configuration Flutter Map
```dart
FlutterMap(
options: MapOptions(
center: LatLng(46.2276, 2.2137), // Centre de la France
zoom: 6.0,
),
children: [
TileLayer(
urlTemplate: 'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}',
additionalOptions: {
'accessToken': mapboxToken,
'id': 'mapbox/streets-v11',
},
),
PolygonLayer(
polygons: secteurs.map((secteur) => Polygon(
points: secteur.polygone,
color: Color(int.parse(secteur.couleur)),
borderColor: Colors.black,
borderStrokeWidth: 2.0,
)).toList(),
),
],
)
```
## 💬 Chat intégré
### Configuration MQTT
```dart
class ChatService {
late MqttClient client;
Future<void> connect() async {
client = MqttClient('mqtt://broker-url', 'client-id');
await client.connect();
client.subscribe('geosector/chat', MqttQos.atLeastOnce);
client.updates!.listen((messages) {
// Traitement des messages
});
}
}
```
## 🔐 Authentification
### Gestion des rôles
```dart
class AuthProvider extends ChangeNotifier {
Utilisateur? _currentUser;
bool get isAdmin => _currentUser?.role == 'admin';
bool get isAuthenticated => _currentUser != null;
Future<void> login(String email, String password) async {
// Logique d'authentification
}
void logout() {
_currentUser = null;
notifyListeners();
}
}
```
## 📱 Interface utilisateur
### Thème de l'application
```dart
class AppTheme {
static ThemeData get lightTheme => ThemeData(
primarySwatch: Colors.red,
appBarTheme: AppBarTheme(
backgroundColor: Colors.red[700],
elevation: 0,
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red[700],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
);
}
```
## 🔄 Synchronisation des données
### Repository Pattern
```dart
class SecteurRepository {
final SecteurLocalDataSource _localDataSource;
final SecteurRemoteDataSource _remoteDataSource;
Future<List<Secteur>> getAllSecteurs() async {
try {
// Essayer de récupérer depuis l'API
final remoteSecteurs = await _remoteDataSource.getAllSecteurs();
await _localDataSource.cacheSecteurs(remoteSecteurs);
return remoteSecteurs;
} catch (e) {
// Fallback sur le cache local
return await _localDataSource.getAllSecteurs();
}
}
}
```
## 🧪 Tests
### Tests unitaires
```bash
flutter test
```
### Tests d'intégration
```bash
flutter test integration_test/
```
## 📚 Documentation
- [Guide d'utilisation](docs/guide-utilisation.md)
- [API Reference](docs/api-reference.md)
- [Architecture détaillée](docs/architecture.md)
- [Contribution](docs/contributing.md)
## 🤝 Contribution
1. Fork le projet
2. Créer une branche feature (`git checkout -b feature/AmazingFeature`)
3. Commit vos changements (`git commit -m 'Add some AmazingFeature'`)
4. Push vers la branche (`git push origin feature/AmazingFeature`)
5. Ouvrir une Pull Request
## 📄 Licence
Distribué sous la licence MIT. Voir `LICENSE` pour plus d'informations.
## 👥 Équipe
- **Développeur Principal** - [@votre-username](https://github.com/votre-username)
- **Designer UI/UX** - [@designer-username](https://github.com/designer-username)
## 📞 Support
Pour toute question ou problème :
- 📧 Email : support@geosector.com
- 🐛 Issues : [GitHub Issues](https://github.com/votre-repo/geosector/issues)
- 📖 Documentation : [Wiki](https://github.com/votre-repo/geosector/wiki)
---
**GEOSECTOR** - Simplifiant la gestion des distributions de calendriers pour les amicales de pompiers 🚒

View File

@@ -1,16 +0,0 @@
# geosector_app
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android" name="Android">
<configuration>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/gen" />
<option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/gen" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/app/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/app/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/app/src/main/assets" />
<option name="LIBS_FOLDER_RELATIVE_PATH" value="/app/src/main/libs" />
<option name="PROGUARD_LOGS_FOLDER_RELATIVE_PATH" value="/app/src/main/proguard_logs" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/app/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/app/src/main/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
</content>
<orderEntry type="jdk" jdkName="Android API 29 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Flutter for Android" level="project" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

36
app/geosector_app.iml Normal file
View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/connectivity_plus/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/connectivity_plus/.pub" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/connectivity_plus/build" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/connectivity_plus/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/connectivity_plus/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/connectivity_plus/example/build" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/path_provider_foundation/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/path_provider_foundation/.pub" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/path_provider_foundation/build" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/path_provider_foundation/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/path_provider_foundation/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/path_provider_foundation/example/build" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/url_launcher_ios/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/url_launcher_ios/.pub" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/url_launcher_ios/build" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/url_launcher_ios/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/url_launcher_ios/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks/plugins/url_launcher_ios/example/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@@ -1,67 +0,0 @@
#!/bin/bash
# Script to create a new branch from main/origin
# Check if branch name is provided
if [ $# -eq 0 ]; then
echo "Error: Branch name is required"
echo "Usage: $0 <branch-name>"
exit 1
fi
# Store branch name from parameter
BRANCH_NAME=$1
# Ensure we have the latest from origin
echo "Fetching latest changes from origin..."
git fetch origin
# Check if we're already on main, if not switch to it
CURRENT_BRANCH=$(git symbolic-ref --short HEAD)
if [ "$CURRENT_BRANCH" != "main" ]; then
echo "Switching to main branch..."
git checkout main
fi
# Pull latest changes from main
echo "Pulling latest changes from main..."
git pull origin main
# Create and checkout the new branch
echo "Creating and checking out new branch: $BRANCH_NAME"
git checkout -b "$BRANCH_NAME"
# Stage all changes
echo "Staging all changes..."
git add .
# Ask if user wants to make an initial commit
read -p "Do you want to make an initial commit? (Y/n): " COMMIT_CHOICE
# Default to Yes if Enter is pressed without input
COMMIT_CHOICE=${COMMIT_CHOICE:-Y}
if [[ $COMMIT_CHOICE =~ ^[Yy]$ ]]; then
# Ask for commit message
read -p "Enter commit message: " COMMIT_MESSAGE
# Check if commit message is provided
if [ -n "$COMMIT_MESSAGE" ]; then
# Make the commit
echo "Creating commit with message: '$COMMIT_MESSAGE'"
git commit -m "$COMMIT_MESSAGE"
# Push to remote with upstream tracking
echo "Pushing to origin and setting upstream tracking..."
git push -u origin "$BRANCH_NAME"
echo "Branch '$BRANCH_NAME' has been pushed to origin with tracking."
else
echo "No commit message provided. Skipping commit."
fi
else
echo "Skipping initial commit. You can commit changes later."
fi
echo "Success! You are now on branch: $BRANCH_NAME"
echo "Ready to start working!"

View File

@@ -1,76 +0,0 @@
#!/bin/bash
# Check if a branch name was provided
if [ -z "$1" ]; then
echo "Error: Please provide the name of the branch to merge"
echo "Usage: ./git-merge.sh branch_name"
exit 1
fi
BRANCH_NAME=$1
# Check if the branch exists
if ! git show-ref --verify --quiet refs/heads/$BRANCH_NAME; then
echo "Error: Branch '$BRANCH_NAME' does not exist"
exit 1
fi
# Display the steps that will be executed
echo "=== Starting merge process ==="
echo "1. Checkout to main"
echo "2. Pull latest changes"
echo "3. Merge branch $BRANCH_NAME"
echo "4. Push to origin"
echo "5. Delete local and remote branches"
# Ask for confirmation
read -p "Do you want to continue? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Operation cancelled"
exit 1
fi
# Execute commands
echo -e "\n=== Checking out to main ==="
git checkout main
if [ $? -ne 0 ]; then
echo "Error during checkout to main"
exit 1
fi
echo -e "\n=== Pulling latest changes ==="
git pull origin main
if [ $? -ne 0 ]; then
echo "Error during pull"
exit 1
fi
echo -e "\n=== Merging branch $BRANCH_NAME ==="
git merge $BRANCH_NAME
if [ $? -ne 0 ]; then
echo "Error during merge. Please resolve conflicts manually"
exit 1
fi
echo -e "\n=== Pushing to origin ==="
git push origin main
if [ $? -ne 0 ]; then
echo "Error during push"
exit 1
fi
echo -e "\n=== Deleting local branch ==="
git branch -d $BRANCH_NAME
if [ $? -ne 0 ]; then
echo "Warning: Unable to delete local branch"
echo "If you are sure everything is properly merged, use: git branch -D $BRANCH_NAME"
fi
echo -e "\n=== Deleting remote branch ==="
git push origin --delete $BRANCH_NAME
if [ $? -ne 0 ]; then
echo "Warning: Unable to delete remote branch"
fi
echo -e "\n=== Merge process completed successfully ==="

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:geosector_app/core/theme/app_theme.dart';
import 'package:go_router/go_router.dart';
import 'package:geosector_app/core/services/api_service.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/core/repositories/operation_repository.dart';
@@ -13,7 +13,6 @@ import 'package:geosector_app/core/services/sync_service.dart';
import 'package:geosector_app/core/services/connectivity_service.dart';
import 'package:geosector_app/presentation/auth/splash_page.dart';
import 'package:geosector_app/presentation/auth/login_page.dart';
import 'package:geosector_app/presentation/auth/register_page.dart';
import 'package:geosector_app/presentation/admin/admin_dashboard_page.dart';
import 'package:geosector_app/presentation/user/user_dashboard_page.dart';
@@ -28,239 +27,202 @@ final amicaleRepository = AmicaleRepository(apiService);
final syncService = SyncService(userRepository: userRepository);
final connectivityService = ConnectivityService();
class GeoSectorApp extends StatelessWidget {
const GeoSectorApp({super.key});
class GeosectorApp extends StatelessWidget {
const GeosectorApp({super.key});
@override
Widget build(BuildContext context) {
// Utiliser directement le router sans provider
final router = GoRouter(
return MaterialApp.router(
title: 'GeoSector',
theme: AppTheme.lightTheme,
darkTheme: AppTheme.darkTheme,
themeMode: ThemeMode.system,
routerConfig: _createRouter(),
debugShowCheckedModeBanner: false,
);
}
/// Création du routeur avec configuration pour URLs propres
GoRouter _createRouter() {
return GoRouter(
initialLocation: '/',
debugLogDiagnostics: true,
refreshListenable:
userRepository, // Écouter les changements d'état d'authentification
// Gestionnaire de redirection global - intercepte toutes les navigations
redirect: (context, state) {
// Détection manuelle des paramètres d'URL pour le Web
if (kIsWeb && state.uri.path == '/login') {
try {
// Obtenir le paramètre 'type' de l'URL actuelle
final typeParam = state.uri.queryParameters['type'];
// Obtenir l'URL brute du navigateur pour comparer
final rawUri = Uri.parse(Uri.base.toString());
final rawTypeParam = rawUri.queryParameters['type'];
print('APP ROUTER: state.uri = ${state.uri}, type = $typeParam');
print('APP ROUTER: rawUri = $rawUri, type = $rawTypeParam');
// Pas de redirection si on a déjà le paramètre type
if (typeParam != null) {
print('APP ROUTER: Param type déjà présent, pas de redirection');
return null; // Pas de redirection
}
// Si un paramètre type=user est présent dans l'URL brute mais pas dans l'état
if (rawTypeParam == 'user' && typeParam == null) {
print(
'APP ROUTER: Paramètre détecté dans l\'URL brute, redirection vers /login?type=user');
return '/login?type=user';
}
} catch (e) {
print('Erreur lors de la récupération des paramètres d\'URL: $e');
}
}
// Sauvegarder le chemin actuel pour l'utilisateur connecté, sauf pour la page de splash
if (state.uri.toString() != '/' && userRepository.isLoggedIn) {
// Ne pas sauvegarder les chemins de login/register
if (!state.uri.toString().startsWith('/login') &&
!state.uri.toString().startsWith('/register')) {
userRepository.updateLastPath(state.uri.toString());
}
}
// Vérifier si l'utilisateur est sur la page de splash
if (state.uri.toString() == '/') {
// Laisser l'utilisateur sur la page de splash, la redirection sera gérée par SplashPage
return null;
}
// Vérifier si l'utilisateur est sur une page d'authentification
final isLoggedIn = userRepository.isLoggedIn;
final isOnLoginPage = state.uri.toString().startsWith('/login');
final isOnRegisterPage = state.uri.toString() == '/register';
final isOnAdminRegisterPage = state.uri.toString() == '/admin-register';
// Si l'utilisateur n'est pas connecté et n'est pas sur une page d'authentification, rediriger vers la page de connexion
if (!isLoggedIn &&
!isOnLoginPage &&
!isOnRegisterPage &&
!isOnAdminRegisterPage) {
return '/login';
}
// Si l'utilisateur est connecté et se trouve sur une page d'authentification, rediriger vers le tableau de bord approprié
if (isLoggedIn &&
(isOnLoginPage || isOnRegisterPage || isOnAdminRegisterPage)) {
// Récupérer le rôle de l'utilisateur directement
final user = userRepository.getCurrentUser();
if (user != null) {
// Convertir le rôle en int si nécessaire
int roleValue;
if (user.role is String) {
roleValue = int.tryParse(user.role as String) ?? 1;
} else {
roleValue = user.role as int;
}
// Redirection simple basée sur le rôle
if (roleValue > 1) {
debugPrint(
'Router: Redirection vers /admin (rôle $roleValue > 1)');
return '/admin';
} else {
debugPrint(
'Router: Redirection vers /user (rôle $roleValue = 1)');
return '/user';
}
}
}
// Si l'utilisateur est connecté mais essaie d'accéder à la mauvaise page selon son rôle
if (isLoggedIn) {
final user = userRepository.getCurrentUser();
if (user != null) {
// Convertir le rôle en int si nécessaire
int roleValue;
if (user.role is String) {
roleValue = int.tryParse(user.role as String) ?? 1;
} else {
roleValue = user.role as int;
}
// Vérifier si l'utilisateur est sur la bonne page en fonction de son rôle
final isOnUserPage = state.uri.toString().startsWith('/user');
final isOnAdminPage = state.uri.toString().startsWith('/admin');
// Admin (rôle > 1) essayant d'accéder à une page utilisateur
if (roleValue > 1 && isOnUserPage) {
debugPrint(
'Router: Redirection d\'admin (rôle $roleValue) vers /admin');
return '/admin';
}
// Utilisateur standard (rôle = 1) essayant d'accéder à une page admin
if (roleValue == 1 && isOnAdminPage) {
debugPrint(
'Router: Redirection d\'utilisateur (rôle $roleValue) vers /user');
return '/user';
}
}
}
return null;
},
routes: [
// Splash screen
GoRoute(
path: '/',
builder: (context, state) => const SplashPage(),
),
// Page de connexion utilisateur dédiée
GoRoute(
path: '/login/user',
name: 'splash',
builder: (context, state) {
print('ROUTER: Accès direct à la route login user');
return const LoginPage(
key: Key('login_page_user'),
loginType: 'user',
);
debugPrint('GoRoute: Affichage de SplashPage');
return const SplashPage();
},
),
// Pages d'authentification standard
GoRoute(
path: '/login',
name: 'login',
builder: (context, state) {
// Ajouter des logs de débogage détaillés pour comprendre les paramètres
print('ROUTER DEBUG: Uri complète = ${state.uri}');
print('ROUTER DEBUG: Path = ${state.uri.path}');
print('ROUTER DEBUG: Query params = ${state.uri.queryParameters}');
print(
'ROUTER DEBUG: Has type? ${state.uri.queryParameters.containsKey("type")}');
// Récupérer le type depuis les query parameters ou extra data
final type = state.uri.queryParameters['type'] ??
(state.extra as Map<String, dynamic>?)?['type'] as String?;
// Donner la priorité aux paramètres d'URL puis aux extras
String? loginType;
// 1. Essayer d'abord les paramètres d'URL (pour les liens externes)
final queryParams = state.uri.queryParameters;
loginType = queryParams['type'];
print('ROUTER DEBUG: Type from query params = $loginType');
// 2. Si aucun type dans les paramètres d'URL, vérifier les extras (pour la navigation interne)
if (loginType == null &&
state.extra != null &&
state.extra is Map<String, dynamic>) {
final extras = state.extra as Map<String, dynamic>;
loginType = extras['type']?.toString();
print('ROUTER DEBUG: Type from extras = $loginType');
}
// 3. Normaliser et valider le type
if (loginType != null) {
loginType = loginType.trim().toLowerCase();
// Vérifier explicitement que c'est 'user', sinon mettre 'admin'
if (loginType != 'user') {
loginType = 'admin';
}
} else {
// Si aucun type n'est spécifié, retourner la page de splash
print(
'ROUTER: Aucun type spécifié, utilisation de la page splash');
return const SplashPage();
}
print('ROUTER: Type de connexion final: $loginType');
return LoginPage(
key: Key('login_page_${loginType}'),
loginType: loginType,
);
debugPrint('GoRoute: Affichage de LoginPage avec type: $type');
return LoginPage(loginType: type);
},
),
// Routes spécifiques pour chaque type de login
GoRoute(
path: '/login/user',
name: 'login-user',
builder: (context, state) {
debugPrint('GoRoute: Affichage de LoginPage pour utilisateur');
return const LoginPage(loginType: 'user');
},
),
GoRoute(
path: '/login/admin',
name: 'login-admin',
builder: (context, state) {
debugPrint('GoRoute: Affichage de LoginPage pour admin');
return const LoginPage(loginType: 'admin');
},
),
GoRoute(
path: '/register',
builder: (context, state) => const RegisterPage(),
name: 'register',
builder: (context, state) {
debugPrint('GoRoute: Affichage de RegisterPage');
// Retournez votre page d'inscription ici
return const Scaffold(
body: Center(
child: Text('Page d\'inscription - À implémenter'),
),
// Pages administrateur
GoRoute(
path: '/admin',
builder: (context, state) => const AdminDashboardPage(),
routes: [
// Ajouter d'autres routes admin ici
],
);
},
),
// Pages utilisateur
GoRoute(
path: '/user',
builder: (context, state) => const UserDashboardPage(),
routes: [
// Ajouter d'autres routes utilisateur ici
],
name: 'user',
builder: (context, state) {
debugPrint('GoRoute: Affichage de UserDashboardPage');
return const UserDashboardPage();
},
),
GoRoute(
path: '/admin',
name: 'admin',
builder: (context, state) {
debugPrint('GoRoute: Affichage de AdminDashboardPage');
return const AdminDashboardPage();
},
),
],
);
redirect: (context, state) {
final currentPath = state.uri.path;
debugPrint('GoRouter.redirect: currentPath = $currentPath');
return MaterialApp.router(
debugShowCheckedModeBanner: false,
title: 'GEOSECTOR',
theme: AppTheme.lightTheme,
darkTheme: AppTheme.darkTheme,
themeMode: ThemeMode.system,
routerConfig: router,
// Pour la page racine, toujours autoriser l'affichage de la splash page
if (currentPath == '/') {
debugPrint('GoRouter.redirect: Autorisation splash page');
return null;
}
// Pages publiques qui ne nécessitent pas d'authentification
final publicPaths = [
'/login',
'/login/user',
'/login/admin',
'/register'
];
if (publicPaths.any((path) => currentPath.startsWith(path))) {
debugPrint(
'GoRouter.redirect: Page publique autorisée: $currentPath');
return null;
}
// Vérifier l'authentification pour les pages protégées
try {
final isAuthenticated = userRepository.isLoggedIn;
final currentUser = userRepository.currentUser;
debugPrint('GoRouter.redirect: isAuthenticated = $isAuthenticated');
debugPrint('GoRouter.redirect: currentUser = ${currentUser?.email}');
// Si pas authentifié, rediriger vers la splash page
if (!isAuthenticated) {
debugPrint(
'GoRouter.redirect: Non authentifié, redirection vers /');
return '/';
}
// Vérifier les permissions pour les pages admin
if (currentPath.startsWith('/admin')) {
final userRole = userRepository.getUserRole();
final isAdmin = userRole > 1; // Admin = rôle 2 ou plus
debugPrint(
'GoRouter.redirect: userRole = $userRole, isAdmin = $isAdmin');
if (!isAdmin) {
debugPrint(
'GoRouter.redirect: Pas admin, redirection vers /user');
return '/user';
}
}
// Si on arrive ici, l'utilisateur a les permissions nécessaires
debugPrint('GoRouter.redirect: Accès autorisé à $currentPath');
return null;
} catch (e) {
debugPrint(
'GoRouter.redirect: Erreur lors de la vérification auth: $e');
// En cas d'erreur, rediriger vers la splash page pour sécurité
return '/';
}
},
// Listener pour déboguer les changements de route
refreshListenable:
userRepository, // Écouter les changements dans userRepository
debugLogDiagnostics: true, // Activer les logs de débogage
errorBuilder: (context, state) {
debugPrint('GoRouter.errorBuilder: Erreur pour ${state.uri.path}');
return Scaffold(
appBar: AppBar(
title: const Text('Erreur de navigation'),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 64, color: Colors.red),
const SizedBox(height: 16),
Text(
'Page non trouvée',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
Text(
'Chemin: ${state.uri.path}',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey[600],
),
),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: () {
debugPrint('GoRouter.errorBuilder: Retour vers /');
context.go('/');
},
icon: const Icon(Icons.home),
label: const Text('Retour à l\'accueil'),
),
],
),
),
),
);
},
);
}
}

View File

@@ -123,7 +123,7 @@ class AppKeys {
},
1: {
'titre': 'Espèce',
'couleur': 0xFFB87333, // Couleur cuivrée
'couleur': 0xFFDAA520, // Goldenrod
'icon_data': Icons.payments_outlined,
},
2: {

View File

@@ -169,21 +169,13 @@ class UserRepository extends ChangeNotifier {
try {
// Vérifier d'abord si la boîte est ouverte
if (!Hive.isBoxOpen(AppKeys.usersBoxName)) {
try {
Hive.openBox<UserModel>(AppKeys.usersBoxName);
} catch (e) {
debugPrint(
'Erreur lors de l\'ouverture de la boîte utilisateurs: $e');
return null;
}
debugPrint('Boîte users non ouverte, tentative d\'ouverture...');
return null; // Retourner null plutôt que d'essayer d'ouvrir ici
}
// Chercher un utilisateur avec une session active - Il suffit qu'il ait un sessionId
// Chercher un utilisateur avec une session active
final activeUsers = _userBox.values
.where((user) =>
user.sessionId != null && // Vérifier que sessionId n'est pas null
user.sessionId!
.isNotEmpty) // Vérifier que sessionId n'est pas vide
.where((user) => user.sessionId != null && user.sessionId!.isNotEmpty)
.toList();
// S'il y a des utilisateurs actifs, retourner le premier
@@ -267,6 +259,16 @@ class UserRepository extends ChangeNotifier {
}
}
/// Navigation après connexion réussie
void navigateAfterLogin(BuildContext context) {
final user = currentUser;
if (user != null && context.mounted) {
final isAdmin = user.role == 1 || user.role == 2;
context.go(isAdmin ? '/admin' : '/user');
}
}
// Méthode d'inscription (uniquement pour les administrateurs)
Future<bool> register(String email, String password, String name,
String amicaleName, String postalCode, String cityName) async {
@@ -1064,28 +1066,8 @@ class UserRepository extends ChangeNotifier {
}
}
/// Méthode de déconnexion avec affichage d'un overlay de chargement
/// et redirection vers la page de démarrage
/// Cette méthode remplace AuthService.logout
Future<bool> logoutWithUI(BuildContext context) async {
final bool result = await LoadingOverlay.show(
context: context,
spinnerSize: 80.0, // Spinner plus grand
strokeWidth: 6.0, // Trait plus épais
future: logout(),
);
// Si la déconnexion a réussi, rediriger vers la page de démarrage
if (result && context.mounted) {
// Utiliser GoRouter pour naviguer vers la page de démarrage
GoRouter.of(context).go('/');
}
return result;
}
// Logout complet (sans UI)
Future<bool> logout() async {
// Méthode de déconnexion unique avec navigation vers / splash_page
Future<bool> logout(BuildContext context) async {
_isLoading = true;
notifyListeners();
@@ -1097,9 +1079,15 @@ class UserRepository extends ChangeNotifier {
final currentUser = getCurrentUser();
if (currentUser == null) {
debugPrint('Aucun utilisateur connecté, déconnexion terminée');
// Nettoyage en profondeur même si aucun utilisateur n'est connecté
await _deepCleanHiveBoxes();
debugPrint('État isLoggedIn après nettoyage: $isLoggedIn');
// Toujours rediriger avec pushAndRemoveUntil pour forcer la navigation
if (context.mounted) {
debugPrint('Redirection forcée vers / après nettoyage');
context.go('/');
}
return true;
}
@@ -1112,13 +1100,9 @@ class UserRepository extends ChangeNotifier {
await logoutAPI();
} catch (e) {
debugPrint('Erreur lors de la déconnexion API, mais on continue: $e');
// Continuer le processus de déconnexion même si l'API échoue
}
}
// Effacer la session de l'utilisateur
debugPrint('Mise à jour de l\'utilisateur pour effacer la session...');
// Supprimer la session API
setSessionId(null);
@@ -1126,37 +1110,47 @@ class UserRepository extends ChangeNotifier {
_cachedCurrentUser = null;
debugPrint('Cache utilisateur réinitialisé (_cachedCurrentUser = null)');
// MODIFICATION IMPORTANTE: Nettoyage complet de toutes les boîtes Hive
// Nettoyage complet de toutes les boîtes Hive
debugPrint('Nettoyage profond des données Hive après déconnexion...');
await _deepCleanHiveBoxes();
// Vérifier l'état après nettoyage
debugPrint('État isLoggedIn après déconnexion: $isLoggedIn');
debugPrint(
'Valeur de currentUser après déconnexion: ${currentUser != null ? "non null" : "null"}');
// Vérifier si des utilisateurs restent dans la boîte
if (Hive.isBoxOpen(AppKeys.usersBoxName)) {
final remainingUsers = _userBox.values.toList();
debugPrint(
'Nombre d\'utilisateurs restants dans la boîte: ${remainingUsers.length}');
}
// Réinitialiser l'état de HiveResetStateService
hiveResetStateService.reset();
debugPrint('État de HiveResetStateService réinitialisé');
debugPrint('Déconnexion terminée avec succès');
// Forcer la navigation avec pushAndRemoveUntil et attendre
if (context.mounted) {
debugPrint('Navigation forcée vers / après déconnexion');
// Attendre que toutes les opérations asynchrones soient terminées
await Future.delayed(const Duration(milliseconds: 200));
// Navigation forcée qui supprime toute la pile de navigation
context.go('/');
// Alternative si pushAndRemoveUntil ne fonctionne pas
// context.pushReplacementNamed('/');
}
notifyListeners();
return true;
} catch (e) {
debugPrint('Erreur de déconnexion: $e');
// Même en cas d'erreur, essayer de naviguer vers la page d'accueil
if (context.mounted) {
debugPrint('Navigation d\'urgence vers / après erreur');
context.go(
'/',
);
}
return false;
} finally {
_isLoading = false;
notifyListeners();
// Vérification finale
debugPrint('État final isLoggedIn: $isLoggedIn');
}
}

View File

@@ -0,0 +1,13 @@
import 'package:package_info_plus/package_info_plus.dart';
class AppInfoService {
static PackageInfo? _packageInfo;
static Future<void> initialize() async {
_packageInfo = await PackageInfo.fromPlatform();
}
static String get version => _packageInfo?.version ?? '0.0.0';
static String get buildNumber => _packageInfo?.buildNumber ?? '0';
static String get fullVersion => 'v$version+$buildNumber';
}

View File

@@ -1,51 +0,0 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/presentation/widgets/loading_overlay.dart';
/// Service qui gère les opérations d'authentification avec affichage d'un overlay de chargement
class AuthService {
final UserRepository _userRepository;
AuthService(this._userRepository);
/// Méthode de connexion avec affichage d'un overlay de chargement
Future<bool> login(BuildContext context, String username, String password,
{required String type}) async {
return await LoadingOverlay.show(
context: context,
spinnerSize: 80.0, // Spinner plus grand
strokeWidth: 6.0, // Trait plus épais
future: _userRepository.login(username, password, type: type),
);
}
/// Méthode de déconnexion avec affichage d'un overlay de chargement
/// et redirection vers la page de démarrage
Future<bool> logout(BuildContext context) async {
final bool result = await LoadingOverlay.show(
context: context,
spinnerSize: 80.0, // Spinner plus grand
strokeWidth: 6.0, // Trait plus épais
future: _userRepository.logout(),
);
// Si la déconnexion a réussi, rediriger vers la page de démarrage
if (result && context.mounted) {
// Utiliser GoRouter pour naviguer vers la page de démarrage
GoRouter.of(context).go('/');
}
return result;
}
/// Vérifie si un utilisateur est connecté
bool isLoggedIn() {
return _userRepository.isLoggedIn;
}
/// Récupère le rôle de l'utilisateur connecté
int getUserRole() {
return _userRepository.getUserRole();
}
}

View File

@@ -7,18 +7,70 @@ class AppTheme {
static const Color accentColor = Color(0xFF00E09D); // Vert
static const Color errorColor = Color(0xFFE41B13); // Rouge
static const Color warningColor = Color(0xFFF7A278); // Orange
static const Color backgroundLightColor =
Color(0xFFF4F5F6); // Gris très clair
static const Color backgroundLightColor = Color(0xFFF4F5F6); // Gris très clair
static const Color backgroundDarkColor = Color(0xFF111827);
static const Color textLightColor = Color(0xFF000000); // Noir
static const Color textDarkColor = Color(0xFFF9FAFB);
// Couleurs de texte supplémentaires
static const Color textSecondaryColor = Color(0xFF7F8C8D);
static const Color textLightSecondaryColor = Color(0xFFBDC3C7);
// Couleurs des boutons
static const Color buttonSuccessColor = Color(0xFF2ECC71);
static const Color buttonDangerColor = Color(0xFFE74C3C);
// Couleurs des charts
static const List<Color> chartColors = [
primaryColor,
accentColor,
errorColor,
warningColor,
secondaryColor,
Color(0xFF9B59B6),
Color(0xFF1ABC9C),
];
// Ombres
static List<BoxShadow> cardShadow = [
BoxShadow(
color: Colors.black.withOpacity(0.05),
spreadRadius: 1,
blurRadius: 10,
offset: const Offset(0, 3),
),
];
static List<BoxShadow> buttonShadow = [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 2),
),
];
// Rayons des bordures
static const double borderRadiusSmall = 4.0;
static const double borderRadiusMedium = 8.0;
static const double borderRadiusLarge = 12.0;
static const double borderRadiusXL = 16.0;
static const double borderRadiusRounded = 50.0;
// Espacement
static const double spacingXS = 4.0;
static const double spacingS = 8.0;
static const double spacingM = 16.0;
static const double spacingL = 24.0;
static const double spacingXL = 32.0;
static const double spacingXXL = 48.0;
// Thème clair
static ThemeData get lightTheme {
return ThemeData(
useMaterial3: true,
brightness: Brightness.light,
fontFamily: 'Figtree', // Utilisation directe de la police locale
fontFamily: 'Figtree',
colorScheme: ColorScheme.light(
primary: primaryColor,
secondary: secondaryColor,
@@ -29,24 +81,10 @@ class AppTheme {
onSecondary: Colors.white,
onBackground: textLightColor,
onSurface: textLightColor,
error: errorColor,
),
textTheme: const TextTheme().copyWith(
displayLarge: const TextStyle(fontFamily: 'Figtree'),
displayMedium: const TextStyle(fontFamily: 'Figtree'),
displaySmall: const TextStyle(fontFamily: 'Figtree'),
headlineLarge: const TextStyle(fontFamily: 'Figtree'),
headlineMedium: const TextStyle(fontFamily: 'Figtree'),
headlineSmall: const TextStyle(fontFamily: 'Figtree'),
titleLarge: const TextStyle(fontFamily: 'Figtree'),
titleMedium: const TextStyle(fontFamily: 'Figtree'),
titleSmall: const TextStyle(fontFamily: 'Figtree'),
bodyLarge: const TextStyle(fontFamily: 'Figtree'),
bodyMedium: const TextStyle(fontFamily: 'Figtree'),
bodySmall: const TextStyle(fontFamily: 'Figtree'),
labelLarge: const TextStyle(fontFamily: 'Figtree'),
labelMedium: const TextStyle(fontFamily: 'Figtree'),
labelSmall: const TextStyle(fontFamily: 'Figtree'),
),
scaffoldBackgroundColor: backgroundLightColor,
textTheme: _getTextTheme(textLightColor),
appBarTheme: const AppBarTheme(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
@@ -56,9 +94,10 @@ class AppTheme {
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
padding: const EdgeInsets.symmetric(horizontal: spacingL, vertical: spacingM),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
borderRadius: BorderRadius.circular(borderRadiusRounded),
),
textStyle: const TextStyle(
fontFamily: 'Figtree',
@@ -67,35 +106,56 @@ class AppTheme {
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: primaryColor,
side: const BorderSide(color: primaryColor),
padding: const EdgeInsets.symmetric(horizontal: spacingL, vertical: spacingM),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadiusMedium),
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: primaryColor,
padding: const EdgeInsets.symmetric(horizontal: spacingM, vertical: spacingS),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: backgroundLightColor,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(borderRadiusMedium),
borderSide: BorderSide(
color: textLightColor.withOpacity(0.1),
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(borderRadiusMedium),
borderSide: BorderSide(
color: textLightColor.withOpacity(0.1),
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(borderRadiusMedium),
borderSide: const BorderSide(color: primaryColor, width: 2),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
contentPadding: const EdgeInsets.symmetric(horizontal: spacingM, vertical: spacingM),
),
cardTheme: CardTheme(
cardTheme: CardThemeData(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.circular(borderRadiusXL),
),
color: Colors.white,
),
dividerTheme: const DividerThemeData(
color: Color(0xFFECF0F1),
thickness: 1,
space: spacingM,
),
);
}
@@ -105,7 +165,7 @@ class AppTheme {
return ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
fontFamily: 'Figtree', // Utilisation directe de la police locale
fontFamily: 'Figtree',
colorScheme: ColorScheme.dark(
primary: primaryColor,
secondary: secondaryColor,
@@ -116,24 +176,10 @@ class AppTheme {
onSecondary: Colors.white,
onBackground: textDarkColor,
onSurface: textDarkColor,
error: errorColor,
),
textTheme: const TextTheme().copyWith(
displayLarge: const TextStyle(fontFamily: 'Figtree'),
displayMedium: const TextStyle(fontFamily: 'Figtree'),
displaySmall: const TextStyle(fontFamily: 'Figtree'),
headlineLarge: const TextStyle(fontFamily: 'Figtree'),
headlineMedium: const TextStyle(fontFamily: 'Figtree'),
headlineSmall: const TextStyle(fontFamily: 'Figtree'),
titleLarge: const TextStyle(fontFamily: 'Figtree'),
titleMedium: const TextStyle(fontFamily: 'Figtree'),
titleSmall: const TextStyle(fontFamily: 'Figtree'),
bodyLarge: const TextStyle(fontFamily: 'Figtree'),
bodyMedium: const TextStyle(fontFamily: 'Figtree'),
bodySmall: const TextStyle(fontFamily: 'Figtree'),
labelLarge: const TextStyle(fontFamily: 'Figtree'),
labelMedium: const TextStyle(fontFamily: 'Figtree'),
labelSmall: const TextStyle(fontFamily: 'Figtree'),
),
scaffoldBackgroundColor: backgroundDarkColor,
textTheme: _getTextTheme(textDarkColor),
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF1F2937),
foregroundColor: Colors.white,
@@ -143,9 +189,10 @@ class AppTheme {
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
padding: const EdgeInsets.symmetric(horizontal: spacingL, vertical: spacingM),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
borderRadius: BorderRadius.circular(borderRadiusRounded),
),
textStyle: const TextStyle(
fontFamily: 'Figtree',
@@ -154,37 +201,78 @@ class AppTheme {
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: primaryColor,
side: const BorderSide(color: primaryColor),
padding: const EdgeInsets.symmetric(horizontal: spacingL, vertical: spacingM),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadiusMedium),
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: primaryColor,
padding: const EdgeInsets.symmetric(horizontal: spacingM, vertical: spacingS),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: const Color(0xFF374151),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(borderRadiusMedium),
borderSide: BorderSide(
color: textDarkColor.withOpacity(0.1),
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(borderRadiusMedium),
borderSide: BorderSide(
color: textDarkColor.withOpacity(0.1),
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(borderRadiusMedium),
borderSide: const BorderSide(color: primaryColor, width: 2),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
contentPadding: const EdgeInsets.symmetric(horizontal: spacingM, vertical: spacingM),
),
cardTheme: CardTheme(
cardTheme: CardThemeData(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.circular(borderRadiusXL),
),
color: const Color(0xFF1F2937),
),
dividerTheme: DividerThemeData(
color: textDarkColor.withOpacity(0.1),
thickness: 1,
space: spacingM,
),
);
}
// Méthode helper pour générer le TextTheme
static TextTheme _getTextTheme(Color textColor) {
return TextTheme(
displayLarge: TextStyle(fontFamily: 'Figtree', color: textColor),
displayMedium: TextStyle(fontFamily: 'Figtree', color: textColor),
displaySmall: TextStyle(fontFamily: 'Figtree', color: textColor),
headlineLarge: TextStyle(fontFamily: 'Figtree', color: textColor),
headlineMedium: TextStyle(fontFamily: 'Figtree', color: textColor),
headlineSmall: TextStyle(fontFamily: 'Figtree', color: textColor),
titleLarge: TextStyle(fontFamily: 'Figtree', color: textColor),
titleMedium: TextStyle(fontFamily: 'Figtree', color: textColor),
titleSmall: TextStyle(fontFamily: 'Figtree', color: textColor),
bodyLarge: TextStyle(fontFamily: 'Figtree', color: textColor),
bodyMedium: TextStyle(fontFamily: 'Figtree', color: textColor),
bodySmall: TextStyle(fontFamily: 'Figtree', color: textColor.withOpacity(0.7)),
labelLarge: TextStyle(fontFamily: 'Figtree', color: textColor),
labelMedium: TextStyle(fontFamily: 'Figtree', color: textColor.withOpacity(0.7)),
labelSmall: TextStyle(fontFamily: 'Figtree', color: textColor.withOpacity(0.7)),
);
}
}

View File

@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter_web_plugins/url_strategy.dart';
import 'package:geosector_app/core/services/app_info_service.dart';
import 'package:geosector_app/app.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:geosector_app/core/data/models/user_model.dart';
@@ -13,88 +15,167 @@ import 'package:geosector_app/core/data/models/membre_model.dart';
import 'package:geosector_app/core/data/models/user_sector_model.dart';
import 'package:geosector_app/core/data/models/region_model.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/core/services/hive_reset_service.dart';
import 'package:geosector_app/core/services/hive_reset_state_service.dart';
// Import centralisé pour les modèles chat
import 'package:geosector_app/chat/models/chat_adapters.dart';
void main() async {
// IMPORTANT: Configurer l'URL strategy pour éviter les # dans les URLs
usePathUrlStrategy();
WidgetsFlutterBinding.ensureInitialized();
// Configurer le routage par chemin (URLs sans #)
setUrlStrategy(PathUrlStrategy());
// Initialiser les services essentiels
await _initializeServices();
// Initialiser Hive avec gestion des erreurs
bool hiveInitialized = false;
final hiveInitialized = await _initializeHive();
// TEMPORAIREMENT: Ne pas marquer l'erreur pour éviter la redirection
// if (!hiveInitialized) {
// debugPrint('Incompatibilité détectée dans les données Hive. Marquage pour affichage du dialogue...');
// hiveResetStateService.markAsReset();
// }
// Configurer l'orientation de l'application (mobile uniquement)
if (!kIsWeb) {
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
}
// Lancer l'application
runApp(const GeosectorApp());
}
/// Initialise les services essentiels
Future<void> _initializeServices() async {
try {
await AppInfoService.initialize();
debugPrint('Services initialisés avec succès');
} catch (e) {
debugPrint('Erreur lors de l\'initialisation des services: $e');
}
}
/// Initialise Hive et les adaptateurs
Future<bool> _initializeHive() async {
try {
// Initialiser Hive
await Hive.initFlutter();
// Enregistrer les adaptateurs Hive pour les modèles principaux
Hive.registerAdapter(UserModelAdapter());
Hive.registerAdapter(AmicaleModelAdapter());
Hive.registerAdapter(ClientModelAdapter());
Hive.registerAdapter(OperationModelAdapter());
Hive.registerAdapter(SectorModelAdapter());
Hive.registerAdapter(PassageModelAdapter());
Hive.registerAdapter(MembreModelAdapter());
Hive.registerAdapter(UserSectorModelAdapter());
// TODO: Décommenter après avoir généré le fichier region_model.g.dart
// Hive.registerAdapter(RegionModelAdapter());
// Enregistrer les adaptateurs Hive pour le chat
Hive.registerAdapter(ConversationModelAdapter());
Hive.registerAdapter(MessageModelAdapter());
Hive.registerAdapter(ParticipantModelAdapter());
Hive.registerAdapter(AnonymousUserModelAdapter());
Hive.registerAdapter(AudienceTargetModelAdapter());
Hive.registerAdapter(NotificationSettingsAdapter());
_registerHiveAdapters();
// Ouvrir uniquement les boîtes essentielles au démarrage
try {
// La boîte des utilisateurs est nécessaire pour vérifier si un utilisateur est déjà connecté
await Hive.openBox<UserModel>(AppKeys.usersBoxName);
// Boîte pour les amicales
await Hive.openBox<AmicaleModel>(AppKeys.amicaleBoxName);
// Boîte pour les clients
await Hive.openBox<ClientModel>(AppKeys.clientsBoxName);
// Boîte pour les préférences utilisateur générales
await Hive.openBox(AppKeys.settingsBoxName);
await _openEssentialHiveBoxes();
// Ouvrir les boîtes de chat également au démarrage pour le cache local
await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
await Hive.openBox<MessageModel>(AppKeys.chatMessagesBoxName);
hiveInitialized = true;
} catch (e) {
debugPrint('Erreur lors de l\'ouverture des boîtes Hive: $e');
// Une erreur s'est produite lors de l'ouverture des boîtes, probablement due à une incompatibilité
// Nous allons réinitialiser Hive
hiveInitialized = false;
}
debugPrint('Hive initialisé avec succès');
return true;
} catch (e) {
debugPrint('Erreur lors de l\'initialisation de Hive: $e');
hiveInitialized = false;
return false;
}
}
/// Enregistre tous les adaptateurs Hive
void _registerHiveAdapters() {
// Vérifier si les adaptateurs sont déjà enregistrés pour éviter les doublons
if (!Hive.isAdapterRegistered(0)) {
Hive.registerAdapter(UserModelAdapter());
}
if (!Hive.isAdapterRegistered(1)) {
Hive.registerAdapter(AmicaleModelAdapter());
}
if (!Hive.isAdapterRegistered(2)) {
Hive.registerAdapter(ClientModelAdapter());
}
if (!Hive.isAdapterRegistered(3)) {
Hive.registerAdapter(OperationModelAdapter());
}
if (!Hive.isAdapterRegistered(4)) {
Hive.registerAdapter(SectorModelAdapter());
}
if (!Hive.isAdapterRegistered(5)) {
Hive.registerAdapter(PassageModelAdapter());
}
if (!Hive.isAdapterRegistered(6)) {
Hive.registerAdapter(MembreModelAdapter());
}
if (!Hive.isAdapterRegistered(7)) {
Hive.registerAdapter(UserSectorModelAdapter());
}
if (!Hive.isAdapterRegistered(8)) {
Hive.registerAdapter(RegionModelAdapter());
}
// Modèles de chat
if (!Hive.isAdapterRegistered(9)) {
Hive.registerAdapter(ConversationModelAdapter());
}
if (!Hive.isAdapterRegistered(10)) {
Hive.registerAdapter(MessageModelAdapter());
}
if (!Hive.isAdapterRegistered(11)) {
Hive.registerAdapter(ParticipantModelAdapter());
}
if (!Hive.isAdapterRegistered(12)) {
Hive.registerAdapter(AnonymousUserModelAdapter());
}
if (!Hive.isAdapterRegistered(13)) {
Hive.registerAdapter(AudienceTargetModelAdapter());
}
if (!Hive.isAdapterRegistered(14)) {
Hive.registerAdapter(NotificationSettingsAdapter());
}
}
/// Ouvre les boîtes Hive essentielles
Future<void> _openEssentialHiveBoxes() async {
final boxesToOpen = [
{'name': AppKeys.usersBoxName, 'type': 'UserModel'},
{'name': AppKeys.amicaleBoxName, 'type': 'AmicaleModel'},
{'name': AppKeys.clientsBoxName, 'type': 'ClientModel'},
{'name': AppKeys.settingsBoxName, 'type': 'dynamic'},
{'name': AppKeys.chatConversationsBoxName, 'type': 'ConversationModel'},
{'name': AppKeys.chatMessagesBoxName, 'type': 'MessageModel'},
];
for (final box in boxesToOpen) {
try {
final boxName = box['name'] as String;
final boxType = box['type'] as String;
// Vérifier si la boîte est déjà ouverte
if (Hive.isBoxOpen(boxName)) {
debugPrint('Boîte $boxName déjà ouverte');
continue;
}
switch (boxType) {
case 'UserModel':
await Hive.openBox<UserModel>(boxName);
break;
case 'AmicaleModel':
await Hive.openBox<AmicaleModel>(boxName);
break;
case 'ClientModel':
await Hive.openBox<ClientModel>(boxName);
break;
case 'ConversationModel':
await Hive.openBox<ConversationModel>(boxName);
break;
case 'MessageModel':
await Hive.openBox<MessageModel>(boxName);
break;
default:
await Hive.openBox(boxName);
}
debugPrint('Boîte $boxName ouverte avec succès');
} catch (e) {
debugPrint('Erreur lors de l\'ouverture de la boîte ${box['name']}: $e');
// Ne pas lancer d'erreur, continuer avec les autres boîtes
}
}
// Si Hive n'a pas été initialisé correctement, marquer l'état pour afficher le dialogue
if (!hiveInitialized) {
debugPrint(
'Incompatibilité détectée dans les données Hive. Marquage pour affichage du dialogue...');
// Marquer Hive comme ayant été réinitialisé pour afficher le dialogue plus tard
hiveResetStateService.markAsReset();
}
// Les autres boîtes (operations, sectors, passages, user_sector) seront ouvertes après connexion
// dans UserRepository.login() via la méthode _ensureBoxIsOpen()
// Définir l'orientation de l'application
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
// Lancer l'application directement sans AppProviders
runApp(const GeoSectorApp());
}

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:geosector_app/shared/app_theme.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
import 'package:geosector_app/presentation/widgets/chat/chat_sidebar.dart';
import 'package:geosector_app/presentation/widgets/chat/chat_messages.dart';
import 'package:geosector_app/presentation/widgets/chat/chat_input.dart';

View File

@@ -3,18 +3,13 @@ import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:math' as math;
import 'package:hive_flutter/hive_flutter.dart';
import 'package:geosector_app/presentation/widgets/charts/activity_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/passage_pie_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/payment_pie_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/payment_data.dart';
import 'package:geosector_app/presentation/widgets/sector_distribution_card.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/core/repositories/passage_repository.dart';
import 'package:geosector_app/presentation/widgets/charts/charts.dart';
import 'package:geosector_app/core/data/models/passage_model.dart';
import 'package:geosector_app/core/data/models/operation_model.dart';
import 'package:geosector_app/core/data/models/sector_model.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/shared/app_theme.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
/// Class pour dessiner les petits points blancs sur le fond
class DotsPainter extends CustomPainter {
@@ -281,7 +276,7 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
0.0,
(sum, passage) =>
sum +
(passage.montant != null && passage.montant.isNotEmpty
(passage.montant.isNotEmpty
? double.tryParse(passage.montant) ?? 0.0
: 0.0));
@@ -310,10 +305,8 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
// Compter les passages par membre
for (final passage in passages) {
if (passage.fkUser != null) {
memberCounts[passage.fkUser!] =
(memberCounts[passage.fkUser!] ?? 0) + 1;
}
memberCounts[passage.fkUser] =
(memberCounts[passage.fkUser] ?? 0) + 1;
}
// Récupérer les informations des membres
@@ -504,7 +497,6 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
key: ValueKey(
'sector_distribution_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
height: 200,
forceRefresh: !isFirstLoad,
),
),
],
@@ -531,7 +523,6 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
key: ValueKey(
'sector_distribution_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
height: 200,
forceRefresh: !isFirstLoad,
),
],
),
@@ -550,12 +541,10 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
key: ValueKey(
'activity_chart_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
height: 350,
loadFromHive: true,
showAllPassages:
true, // Tous les passages, pas seulement ceux de l'utilisateur courant
title: 'Passages réalisés par jour (15 derniers jours)',
daysToShow: 15,
forceRefresh: !isFirstLoad,
),
// Si vous avez besoin de passer l'ID de l'opération en cours, décommentez les lignes suivantes
// child: ActivityChart(
@@ -607,7 +596,7 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
const Text(
'Actions sur cette opération',
style: TextStyle(
fontWeight: FontWeight.bold,
@@ -624,7 +613,7 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
context,
'Exporter les données',
Icons.file_download_outlined,
AppTheme.buttonPrimaryColor,
AppTheme.primaryColor,
() {},
),
_buildActionButton(
@@ -705,386 +694,54 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
);
}
Widget _buildChartCard(
BuildContext context,
String title,
Widget chart,
) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
boxShadow: AppTheme.cardShadow,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(AppTheme.spacingM),
child: Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
chart,
],
),
);
}
// Construit la carte de répartition par type de passage avec liste
Widget _buildPassageTypeCard(BuildContext context) {
return Container(
height: 300, // Hauteur fixe de 300px
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
boxShadow: AppTheme.cardShadow,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(AppTheme.spacingM),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Répartition par type de passage',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
Text(
'$totalPassages passages',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: AppTheme.primaryColor,
),
),
],
),
),
Expanded(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: AppTheme.spacingM),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Graphique à gauche
Expanded(
flex: 1,
child: SizedBox(
height: 180, // Taille réduite
child: Builder(
builder: (context) {
// Vérifier si nous avons des données de passages
if (passagesByType.isEmpty) {
debugPrint(
'AdminDashboardHomePage: Aucune donnée de passage disponible pour le graphique');
return const Center(
child: Text('Aucune donnée disponible'),
);
}
// Si nous avons des données, afficher le graphique
// Mais d'abord, vérifier si tous les passages sont de type 2 (à finaliser)
// qui est exclu par défaut dans PassagePieChart
bool hasNonType2Passages = passagesByType.entries.any(
(entry) => entry.key != 2 && entry.value > 0);
debugPrint(
'AdminDashboardHomePage: Données pour le graphique: $passagesByType');
// Créer un widget personnalisé pour afficher le graphique ou un message
// selon le contenu des données
if (passagesByType.isEmpty) {
debugPrint(
'AdminDashboardHomePage: Aucune donnée de passage disponible');
return const Center(
child: Text('Aucune donnée disponible'),
);
}
// Vérifier si nous avons des données pour au moins un type
int totalPassages = 0;
passagesByType
.forEach((_, count) => totalPassages += count);
if (totalPassages == 0) {
debugPrint(
'AdminDashboardHomePage: Aucun passage trouvé');
return const Center(
child: Text('Aucun passage trouvé'),
);
}
// Vérifier si tous les passages sont de type 2 (à finaliser)
if (!hasNonType2Passages) {
debugPrint(
'AdminDashboardHomePage: Tous les passages sont de type 2 (à finaliser)');
// Créer un widget personnalisé pour afficher un message
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.info_outline,
color: Colors.orange,
size: 40,
),
const SizedBox(height: 8),
const Text(
'Uniquement des passages à finaliser',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'${passagesByType[2] ?? 0} passages',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.orange,
fontWeight: FontWeight.bold,
),
),
],
);
}
// Sinon, afficher le graphique avec les données
debugPrint(
'AdminDashboardHomePage: Affichage du graphique avec ${passagesByType.length} types');
return PassagePieChart(
size: 180,
return PassageSummaryCard(
title: 'Répartition par type de passage',
titleColor: AppTheme.primaryColor,
titleIcon: Icons.route,
height: 300,
useValueListenable: false, // Utiliser les données statiques
showAllPassages: true,
excludePassageTypes: const [2], // Exclure "À finaliser"
passagesByType: passagesByType,
loadFromHive: false,
isDonut: true,
innerRadius: '50%',
showIcons: false,
showLegend: false,
);
},
),
),
),
// Liste des types à droite
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.only(left: AppTheme.spacingM),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.end, // Alignement à droite
mainAxisAlignment: MainAxisAlignment.center,
children: [
...AppKeys.typesPassages.entries.map((entry) {
final int typeId = entry.key;
final Map<String, dynamic> typeInfo = entry.value;
final int count = passagesByType[typeId] ?? 0;
final Color color =
Color(typeInfo['couleur2'] as int);
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment
.end, // Alignement à droite
children: [
Expanded(
child: Text(
'$count ${typeInfo['titres']}',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: color,
),
textAlign: TextAlign
.right, // Texte aligné à droite
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
Container(
width: 16,
height: 16,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
),
],
),
);
}).toList(),
],
),
),
),
],
),
),
),
],
),
customTotalDisplay: (total) => '$totalPassages passages',
isDesktop: MediaQuery.of(context).size.width > 800,
backgroundIcon: Icons.route,
backgroundIconColor: AppTheme.primaryColor,
backgroundIconOpacity: 0.07,
backgroundIconSize: 180,
);
}
// Construit la carte de répartition par mode de paiement
Widget _buildPaymentTypeCard(BuildContext context) {
return Container(
height: 300, // Hauteur fixe de 300px
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
boxShadow: AppTheme.cardShadow,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(AppTheme.spacingM),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Répartition par mode de paiement',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
Text(
'${totalAmounts.toStringAsFixed(2)}',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: AppTheme.buttonSuccessColor,
),
),
],
),
),
Expanded(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: AppTheme.spacingM),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Graphique à gauche
Expanded(
flex: 1,
child: SizedBox(
height: 180, // Taille réduite
child: PaymentPieChart(
size: 180,
payments: paymentData,
isDonut: true,
innerRadius: '50%',
showIcons: false,
showLegend: false,
enable3DEffect:
false, // Désactiver l'effet 3D pour conserver les couleurs originales
effect3DIntensity: 0.0, // Pas d'intensité 3D
enableEnhancedExplode: false, // Désactiver l'explosion
useGradient:
false, // Ne pas utiliser de dégradé pour conserver les couleurs originales
),
),
),
// Liste des types de règlement à droite
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.only(left: AppTheme.spacingM),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.end, // Alignement à droite
mainAxisAlignment: MainAxisAlignment.center,
children: [
...[1, 2, 3].map((typeId) {
// Uniquement les types 1, 2 et 3
if (!AppKeys.typesReglements.containsKey(typeId)) {
return const SizedBox
.shrink(); // Ignorer si le type n'existe pas
}
final Map<String, dynamic> typeInfo =
AppKeys.typesReglements[typeId]!;
// Calculer le montant total pour ce type de règlement
double amount = 0.0;
for (final payment in paymentData) {
if (payment.typeId == typeId) {
amount = payment.amount;
break;
}
}
// Ne pas afficher si le montant est 0
if (amount <= 0) {
return const SizedBox.shrink();
}
final Color color =
Color(typeInfo['couleur'] as int);
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment
.end, // Alignement à droite
children: [
Expanded(
child: Text(
'${amount.toStringAsFixed(2)}${typeInfo['titre']}',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: color,
),
textAlign: TextAlign
.right, // Texte aligné à droite
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
Container(
width: 16,
height: 16,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
),
],
),
);
}).toList(),
],
),
),
),
],
),
),
),
],
),
return PaymentSummaryCard(
title: 'Répartition par mode de paiement',
titleColor: AppTheme.buttonSuccessColor,
titleIcon: Icons.euro,
height: 300,
useValueListenable: false, // Utiliser les données statiques
showAllPayments: true,
paymentsByType: _convertPaymentDataToMap(paymentData),
customTotalDisplay: (total) => '${totalAmounts.toStringAsFixed(2)}',
isDesktop: MediaQuery.of(context).size.width > 800,
backgroundIcon: Icons.euro,
backgroundIconColor: AppTheme.primaryColor,
backgroundIconOpacity: 0.07,
backgroundIconSize: 180,
);
}
// Méthode helper pour convertir les PaymentData en Map
Map<int, double> _convertPaymentDataToMap(List<PaymentData> paymentDataList) {
final Map<int, double> result = {};
for (final payment in paymentDataList) {
result[payment.typeId] = payment.amount;
}
return result;
}
Widget _buildActionButton(
BuildContext context,
String label,

View File

@@ -2,11 +2,7 @@ import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:geosector_app/presentation/widgets/dashboard_layout.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/shared/app_theme.dart';
import 'package:geosector_app/presentation/widgets/loading_progress_overlay.dart';
import 'package:geosector_app/core/models/loading_state.dart';
import 'dart:math' as math;
// Import des pages admin
@@ -54,14 +50,114 @@ class _AdminDashboardPageState extends State<AdminDashboardPage>
// Liste des pages à afficher
late final List<Widget> _pages;
// Index de la page Amicale et membres
static const int entitePageIndex = 5;
// Référence à la boîte Hive pour les paramètres
late Box _settingsBox;
// Overlay pour afficher la progression du chargement
OverlayEntry? _progressOverlay;
// Liste des éléments de navigation de base (toujours visibles)
final List<_NavigationItem> _baseNavigationItems = [
const _NavigationItem(
label: 'Tableau de bord',
icon: Icons.dashboard_outlined,
selectedIcon: Icons.dashboard,
page: AdminDashboardHomePage(),
),
const _NavigationItem(
label: 'Statistiques',
icon: Icons.bar_chart_outlined,
selectedIcon: Icons.bar_chart,
page: AdminStatisticsPage(),
),
const _NavigationItem(
label: 'Historique',
icon: Icons.history_outlined,
selectedIcon: Icons.history,
page: AdminHistoryPage(),
),
const _NavigationItem(
label: 'Messages',
icon: Icons.chat_outlined,
selectedIcon: Icons.chat,
page: AdminCommunicationPage(),
),
const _NavigationItem(
label: 'Carte',
icon: Icons.map_outlined,
selectedIcon: Icons.map,
page: AdminMapPage(),
),
];
// Éléments de navigation supplémentaires pour le rôle 2
final List<_NavigationItem> _adminNavigationItems = [
const _NavigationItem(
label: 'Amicale & membres',
icon: Icons.business_outlined,
selectedIcon: Icons.business,
page: AdminEntitePage(),
requiredRole: 2,
),
const _NavigationItem(
label: 'Opérations',
icon: Icons.calendar_today_outlined,
selectedIcon: Icons.calendar_today,
page: Scaffold(body: Center(child: Text('Page Opérations'))),
requiredRole: 2,
),
];
// Construire la liste des destinations de navigation en fonction du rôle
List<NavigationDestination> _buildNavigationDestinations() {
final destinations = <NavigationDestination>[];
final currentUser = userRepository.getCurrentUser();
// Ajouter les éléments de base
for (final item in _baseNavigationItems) {
destinations.add(
NavigationDestination(
icon: Icon(item.icon),
selectedIcon: Icon(item.selectedIcon),
label: item.label,
),
);
}
// Ajouter les éléments admin si l'utilisateur a le rôle requis
if (currentUser?.role == 2) {
for (final item in _adminNavigationItems) {
if (item.requiredRole == null || item.requiredRole == 2) {
destinations.add(
NavigationDestination(
icon: Icon(item.icon),
selectedIcon: Icon(item.selectedIcon),
label: item.label,
),
);
}
}
}
return destinations;
}
// Construire la liste des pages en fonction du rôle
List<Widget> _buildPages() {
final pages = <Widget>[];
final currentUser = userRepository.getCurrentUser();
// Ajouter les pages de base
pages.addAll(_baseNavigationItems.map((item) => item.page));
// Ajouter les pages admin si l'utilisateur a le rôle requis
if (currentUser?.role == 2) {
for (final item in _adminNavigationItems) {
if (item.requiredRole == null || item.requiredRole == 2) {
pages.add(item.page);
}
}
}
return pages;
}
@override
void initState() {
@@ -72,35 +168,19 @@ class _AdminDashboardPageState extends State<AdminDashboardPage>
debugPrint('Initialisation de AdminDashboardPage');
// Vérifier que userRepository est correctement initialisé
if (userRepository == null) {
debugPrint('ERREUR: userRepository est null dans AdminDashboardPage');
} else {
debugPrint('userRepository est correctement initialisé');
// Vérifier l'utilisateur courant
final currentUser = userRepository.getCurrentUser();
if (currentUser == null) {
debugPrint(
'ERREUR: Aucun utilisateur connecté dans AdminDashboardPage',
);
'ERREUR: Aucun utilisateur connecté dans AdminDashboardPage');
} else {
debugPrint(
'Utilisateur connecté: ${currentUser.username} (${currentUser.id})',
);
'Utilisateur connecté: ${currentUser.username} (${currentUser.id})');
}
// Écouter les changements d'état du UserRepository
userRepository.addListener(_handleUserRepositoryChanges);
}
_pages = [
const AdminDashboardHomePage(),
const AdminStatisticsPage(),
const AdminHistoryPage(),
const AdminCommunicationPage(),
const AdminMapPage(),
const AdminEntitePage(),
];
// Initialiser les pages et les destinations
_pages = _buildPages();
// Initialiser et charger les paramètres
_initSettings();
@@ -117,10 +197,7 @@ class _AdminDashboardPageState extends State<AdminDashboardPage>
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
if (userRepository != null) {
userRepository.removeListener(_handleUserRepositoryChanges);
}
_removeProgressOverlay();
super.dispose();
}
@@ -134,19 +211,6 @@ class _AdminDashboardPageState extends State<AdminDashboardPage>
// La barre de progression est désactivée, ne rien faire
}
// Méthodes pour gérer l'overlay de progression (désactivées)
void _showProgressOverlay(LoadingState state) {
// La barre de progression est désactivée, ne rien faire
}
void _updateProgressOverlay(LoadingState state) {
// La barre de progression est désactivée, ne rien faire
}
void _removeProgressOverlay() {
// La barre de progression est désactivée, ne rien faire
}
// Initialiser la boîte de paramètres et charger les préférences
Future<void> _initSettings() async {
try {
@@ -233,47 +297,21 @@ class _AdminDashboardPageState extends State<AdminDashboardPage>
],
);
}
/// Construit la liste des destinations de navigation
List<NavigationDestination> _buildNavigationDestinations() {
// Destinations de base toujours présentes
final List<NavigationDestination> destinations = [
const NavigationDestination(
icon: Icon(Icons.dashboard_outlined),
selectedIcon: Icon(Icons.dashboard),
label: 'Tableau de bord',
),
const NavigationDestination(
icon: Icon(Icons.bar_chart_outlined),
selectedIcon: Icon(Icons.bar_chart),
label: 'Statistiques',
),
const NavigationDestination(
icon: Icon(Icons.history_outlined),
selectedIcon: Icon(Icons.history),
label: 'Historique',
),
const NavigationDestination(
icon: Icon(Icons.chat_outlined),
selectedIcon: Icon(Icons.chat),
label: 'Messages',
),
const NavigationDestination(
icon: Icon(Icons.map_outlined),
selectedIcon: Icon(Icons.map),
label: 'Carte',
),
];
// Ajouter la destination "Amicale et membres"
destinations.add(
const NavigationDestination(
icon: Icon(Icons.business_outlined),
selectedIcon: Icon(Icons.business),
label: 'Amicale',
),
);
return destinations;
}
}
// Classe pour représenter une destination de navigation avec sa page associée
class _NavigationItem {
final String label;
final IconData icon;
final IconData selectedIcon;
final Widget page;
final int? requiredRole; // null si accessible à tous les rôles
const _NavigationItem({
required this.label,
required this.icon,
required this.selectedIcon,
required this.page,
this.requiredRole,
});
}

View File

@@ -7,7 +7,7 @@ import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/core/services/location_service.dart';
import 'package:geosector_app/core/data/models/sector_model.dart';
import 'package:geosector_app/core/data/models/passage_model.dart';
import '../../shared/app_theme.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
class AdminMapPage extends StatefulWidget {
const AdminMapPage({Key? key}) : super(key: key);

View File

@@ -1,14 +1,6 @@
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
import 'package:flutter/material.dart';
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
import 'package:geosector_app/presentation/widgets/charts/activity_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/passage_pie_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/payment_pie_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/payment_data.dart';
import 'package:geosector_app/presentation/widgets/charts/combined_chart.dart';
import 'package:geosector_app/core/repositories/passage_repository.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import '../../shared/app_theme.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
import 'package:geosector_app/presentation/widgets/charts/charts.dart';
import 'dart:math' as math;
/// Class pour dessiner les petits points blancs sur le fond
@@ -186,7 +178,6 @@ class _AdminStatisticsPageState extends State<AdminStatisticsPage> {
const SizedBox(height: AppTheme.spacingM),
ActivityChart(
height: 350,
loadFromHive: true,
showAllPassages: true,
title: '',
daysToShow: _daysToShow,
@@ -210,13 +201,21 @@ class _AdminStatisticsPageState extends State<AdminStatisticsPage> {
Expanded(
child: _buildChartCard(
'Répartition par type de passage',
PassagePieChart(
size: 300,
loadFromHive: true,
PassageSummaryCard(
title: '',
titleColor: AppTheme.primaryColor,
titleIcon: Icons.pie_chart,
height: 300,
useValueListenable: true,
showAllPassages: true,
excludePassageTypes: const [
2
], // Exclure "À finaliser"
userId: _selectedUser != 'Tous'
? _getUserIdFromName(_selectedUser)
: null,
isDesktop:
MediaQuery.of(context).size.width > 800,
),
),
),
@@ -224,7 +223,7 @@ class _AdminStatisticsPageState extends State<AdminStatisticsPage> {
Expanded(
child: _buildChartCard(
'Répartition par mode de paiement',
PaymentPieChart(
const PaymentPieChart(
payments: [
PaymentData(
typeId: 1,
@@ -258,19 +257,26 @@ class _AdminStatisticsPageState extends State<AdminStatisticsPage> {
children: [
_buildChartCard(
'Répartition par type de passage',
PassagePieChart(
size: 300,
loadFromHive: true,
PassageSummaryCard(
title: '',
titleColor: AppTheme.primaryColor,
titleIcon: Icons.pie_chart,
height: 300,
useValueListenable: true,
showAllPassages: true,
excludePassageTypes: const [
2
], // Exclure "À finaliser"
userId: _selectedUser != 'Tous'
? _getUserIdFromName(_selectedUser)
: null,
isDesktop: MediaQuery.of(context).size.width > 800,
),
),
const SizedBox(height: AppTheme.spacingM),
_buildChartCard(
'Répartition par mode de paiement',
PaymentPieChart(
const PaymentPieChart(
payments: [
PaymentData(
typeId: 1,
@@ -357,7 +363,7 @@ class _AdminStatisticsPageState extends State<AdminStatisticsPage> {
icon: const Icon(Icons.print),
label: const Text('Imprimer'),
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.buttonSecondaryColor,
backgroundColor: AppTheme.secondaryColor,
foregroundColor: Colors.white,
),
),

View File

@@ -1,16 +1,17 @@
import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'dart:convert';
import 'package:flutter/foundation.dart' show kIsWeb, kDebugMode;
import 'dart:js' as js;
import 'package:go_router/go_router.dart';
import 'package:go_router/src/state.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:http/http.dart' as http;
import 'package:geosector_app/core/services/app_info_service.dart';
import 'package:geosector_app/presentation/widgets/custom_button.dart';
import 'package:geosector_app/presentation/widgets/custom_text_field.dart';
import 'package:geosector_app/core/services/location_service.dart';
import 'package:geosector_app/core/services/connectivity_service.dart';
import 'package:geosector_app/presentation/widgets/connectivity_indicator.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
class LoginPage extends StatefulWidget {
@@ -51,6 +52,7 @@ class _LoginPageState extends State<LoginPage> {
final _passwordController = TextEditingController();
final _usernameFocusNode = FocusNode();
bool _obscurePassword = true;
String _appVersion = '';
// Type de connexion (utilisateur ou administrateur)
late String _loginType;
@@ -63,6 +65,37 @@ class _LoginPageState extends State<LoginPage> {
// État de la connexion Internet
bool _isConnected = false;
Future<void> _getAppVersion() async {
try {
final packageInfo = await PackageInfo.fromPlatform();
if (mounted) {
setState(() {
_appVersion = packageInfo.version;
});
}
} catch (e) {
debugPrint('Erreur lors de la récupération de la version: $e');
// Fallback sur la version du AppInfoService si elle existe
if (mounted) {
setState(() {
_appVersion = AppInfoService.fullVersion
.split(' ')
.last; // Extraire juste le numéro
});
}
}
}
Future<void> _checkConnectivity() async {
await connectivityService.checkConnectivity();
if (mounted) {
setState(() {
_isConnected = connectivityService.isConnected;
});
}
}
@override
void initState() {
super.initState();
@@ -163,6 +196,12 @@ class _LoginPageState extends State<LoginPage> {
}
});
// Récupérer la version de l'application
_getAppVersion();
// Vérification de connectivité au démarrage
_checkConnectivity();
// Pré-remplir le champ username avec l'identifiant du dernier utilisateur connecté
// seulement si le rôle correspond au type de login
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -538,6 +577,14 @@ class _LoginPageState extends State<LoginPage> {
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
AppInfoService.fullVersion,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.primary.withOpacity(0.7),
),
),
const SizedBox(height: 8),
// Ajouter un texte de débogage uniquement en mode développement
if (kDebugMode)
Text(
@@ -558,7 +605,38 @@ class _LoginPageState extends State<LoginPage> {
const SizedBox(height: 16),
// Indicateur de connectivité
ConnectivityIndicator(),
const ConnectivityIndicator(),
const SizedBox(height: 16),
if (!kIsWeb && !_isConnected)
Container(
margin: const EdgeInsets.only(top: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: theme.colorScheme.error.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color:
theme.colorScheme.error.withOpacity(0.3),
),
),
child: Column(
children: [
Icon(Icons.signal_wifi_off,
color: theme.colorScheme.error, size: 32),
const SizedBox(height: 8),
Text('Connexion Internet requise',
style: theme.textTheme.titleMedium
?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.error)),
const SizedBox(height: 8),
const Text(
'Veuillez vous connecter à Internet (WiFi ou données mobiles) pour pouvoir vous connecter.'),
],
),
),
const SizedBox(height: 16),
// Formulaire de connexion
@@ -689,7 +767,7 @@ class _LoginPageState extends State<LoginPage> {
alignment: Alignment.centerRight,
child: TextButton(
onPressed: () {
// Naviguer vers la page de récupération de mot de passe
_showForgotPasswordDialog(context);
},
child: Text(
'Mot de passe oublié ?',
@@ -863,10 +941,10 @@ class _LoginPageState extends State<LoginPage> {
onPressed: () {
context.go('/register');
},
child: Text(
child: const Text(
'Inscription Administrateur',
style: TextStyle(
color: theme.colorScheme.tertiary,
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
@@ -897,8 +975,255 @@ class _LoginPageState extends State<LoginPage> {
),
),
),
// Badge de version en bas à droite
if (_appVersion.isNotEmpty)
Positioned(
bottom: 16,
right: 16,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: theme.colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: theme.colorScheme.primary.withOpacity(0.3),
width: 1,
),
),
child: Text(
'v$_appVersion',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.primary.withOpacity(0.8),
fontSize: 10,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
);
}
// Affiche la boîte de dialogue pour la récupération de mot de passe
void _showForgotPasswordDialog(BuildContext context) {
final emailController = TextEditingController();
final formKey = GlobalKey<FormState>();
bool isLoading = false;
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return StatefulBuilder(builder: (context, setState) {
return AlertDialog(
title: const Row(
children: [
Icon(Icons.lock_reset, color: Colors.blue),
SizedBox(width: 10),
Text('Récupération de mot de passe'),
],
),
content: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Veuillez entrer votre adresse email pour recevoir un nouveau mot de passe.',
style: TextStyle(fontSize: 14),
),
const SizedBox(height: 16),
CustomTextField(
controller: emailController,
label: 'Email',
hintText: 'Entrez votre email',
prefixIcon: Icons.email_outlined,
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer votre email';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$')
.hasMatch(value)) {
return 'Veuillez entrer un email valide';
}
return null;
},
),
],
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: isLoading
? null
: () async {
if (formKey.currentState!.validate()) {
setState(() {
isLoading = true;
});
try {
// Vérifier la connexion Internet
await connectivityService.checkConnectivity();
if (!connectivityService.isConnected) {
throw Exception('Aucune connexion Internet');
}
// Construire l'URL de l'API
final baseUrl = Uri.base.origin;
final apiUrl = '$baseUrl/api/lostpassword';
print('Envoi de la requête à: $apiUrl');
print('Email: ${emailController.text.trim()}');
http.Response? response;
try {
// Envoyer la requête à l'API
response = await http.post(
Uri.parse(apiUrl),
headers: {'Content-Type': 'application/json'},
body: json.encode({
'email': emailController.text.trim(),
}),
);
print('Réponse reçue: ${response.statusCode}');
print('Corps de la réponse: ${response.body}');
// Si la réponse est 404, c'est peut-être un problème de route
if (response.statusCode == 404) {
// Essayer avec une URL alternative
final alternativeUrl =
'$baseUrl/api/index.php/lostpassword';
print(
'Tentative avec URL alternative: $alternativeUrl');
final alternativeResponse = await http.post(
Uri.parse(alternativeUrl),
headers: {'Content-Type': 'application/json'},
body: json.encode({
'email': emailController.text.trim(),
}),
);
print(
'Réponse alternative reçue: ${alternativeResponse.statusCode}');
print(
'Corps de la réponse alternative: ${alternativeResponse.body}');
// Si la réponse alternative est un succès, utiliser cette réponse
if (alternativeResponse.statusCode == 200) {
response = alternativeResponse;
}
}
} catch (e) {
print(
'Erreur lors de l\'envoi de la requête: $e');
throw Exception('Erreur de connexion: $e');
}
// Traiter la réponse
if (response != null &&
response.statusCode == 200) {
// Modifier le contenu de la boîte de dialogue pour afficher le message de succès
setState(() {
isLoading = false;
});
// Remplacer le contenu de la boîte de dialogue par un message de succès
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
// Fermer automatiquement la boîte de dialogue après 2 secondes
Future.delayed(Duration(seconds: 2), () {
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
});
return const AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.check_circle,
color: Colors.green,
size: 48,
),
SizedBox(height: 16),
Text(
'Vous recevrez un nouveau mot de passe par email',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16),
),
],
),
);
},
);
} else {
// Fermer la boîte de dialogue actuelle
Navigator.of(context).pop();
// Afficher un message d'erreur
final responseData = json.decode(response.body);
throw Exception(responseData['message'] ??
'Erreur lors de la récupération du mot de passe');
}
} catch (e) {
// Afficher un message d'erreur
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e
.toString()
.contains('Exception:')
? e.toString().split('Exception: ')[1]
: 'Erreur lors de la récupération du mot de passe'),
backgroundColor: Colors.red,
),
);
} finally {
if (mounted) {
setState(() {
isLoading = false;
});
}
}
}
},
child: isLoading
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text('Recevoir un nouveau mot de passe'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
),
],
);
});
},
);
}
}

View File

@@ -1,11 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:go_router/go_router.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'dart:math' as math;
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/presentation/widgets/custom_button.dart';
import 'package:geosector_app/presentation/widgets/custom_text_field.dart';
import 'package:geosector_app/core/services/connectivity_service.dart';
import 'package:geosector_app/presentation/widgets/connectivity_indicator.dart';
import 'package:geosector_app/core/services/app_info_service.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
class RegisterPage extends StatefulWidget {
@@ -15,39 +22,211 @@ class RegisterPage extends StatefulWidget {
State<RegisterPage> createState() => _RegisterPageState();
}
// Class pour dessiner les petits points blancs sur le fond
class DotsPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white.withOpacity(0.5)
..style = PaintingStyle.fill;
final random = math.Random(42); // Seed fixe pour consistance
final numberOfDots = (size.width * size.height) ~/ 1500;
for (int i = 0; i < numberOfDots; i++) {
final x = random.nextDouble() * size.width;
final y = random.nextDouble() * size.height;
final radius = 1.0 + random.nextDouble() * 2.0;
canvas.drawCircle(Offset(x, y), radius, paint);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
// Modèle pour les villes
class City {
final String name;
final String postalCode;
City({required this.name, required this.postalCode});
factory City.fromJson(Map<String, dynamic> json) {
return City(
name: json['nom'] ?? '',
postalCode: json['codePostal'] ?? '',
);
}
}
class _RegisterPageState extends State<RegisterPage> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _amicaleNameController = TextEditingController();
final _postalCodeController = TextEditingController();
final _cityNameController = TextEditingController();
final _emailController = TextEditingController();
final _captchaController = TextEditingController();
String _appVersion = '';
// État de la connexion Internet
// Valeur cachée pour le test anti-robot
final String _hiddenToken = DateTime.now().millisecondsSinceEpoch.toString();
// Valeurs pour le captcha simple
final int _captchaNum1 =
2 + (DateTime.now().second % 5); // Nombre entre 2 et 6
final int _captchaNum2 =
3 + (DateTime.now().minute % 4); // Nombre entre 3 et 6
// État de la connexion Internet et de la plateforme
bool _isConnected = false;
bool _isMobile = false;
String _connectionType = '';
bool _isLoading = false; // État de chargement local
// Liste des villes correspondant au code postal
List<City> _cities = [];
City? _selectedCity;
bool _isLoadingCities = false;
Future<void> _getAppVersion() async {
try {
final packageInfo = await PackageInfo.fromPlatform();
if (mounted) {
setState(() {
_appVersion = packageInfo.version;
});
}
} catch (e) {
debugPrint('Erreur lors de la récupération de la version: $e');
// Fallback sur la version du AppInfoService si elle existe
if (mounted) {
setState(() {
_appVersion = AppInfoService.fullVersion
.split(' ')
.last; // Extraire juste le numéro
});
}
}
}
@override
void initState() {
super.initState();
// Déterminer si l'application s'exécute sur mobile
_isMobile = !kIsWeb;
// Initialiser l'état de la connexion
WidgetsBinding.instance.addPostFrameCallback((_) {
_checkConnectivity();
// Récupérer la version de l'application
_getAppVersion();
// Écouter les changements du code postal
_postalCodeController.addListener(_onPostalCodeChanged);
}
// Méthode pour vérifier la connectivité
Future<void> _checkConnectivity() async {
await connectivityService.checkConnectivity();
if (mounted) {
// Utiliser l'instance globale de connectivityService définie dans app.dart
setState(() {
_isConnected = connectivityService.isConnected;
_connectionType = connectivityService.connectionType;
});
}
}
// Méthode appelée lorsque le code postal change
void _onPostalCodeChanged() {
final postalCode = _postalCodeController.text;
// Réinitialiser la ville sélectionnée si le code postal change
setState(() {
_selectedCity = null;
});
// Si le code postal a au moins 3 chiffres, rechercher les villes correspondantes
if (postalCode.length >= 3) {
_fetchCities(postalCode);
} else {
setState(() {
_cities = [];
});
}
}
// Méthode pour récupérer les villes correspondant au code postal
Future<void> _fetchCities(String postalCode) async {
if (!_isConnected) return;
setState(() {
_isLoadingCities = true;
});
try {
// Utiliser l'API interne de geosector pour récupérer les villes par code postal
final baseUrl = Uri
.base.origin; // Récupère l'URL de base (ex: https://app.geosector.fr)
final apiUrl = '$baseUrl/api/villes?code_postal=$postalCode';
final response = await http.get(
Uri.parse(apiUrl),
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode == 200) {
final Map<String, dynamic> responseData = json.decode(response.body);
// Vérifier si la réponse contient des données
if (responseData['success'] == true && responseData['data'] != null) {
final List<dynamic> data = responseData['data'];
setState(() {
_cities = data
.map((city) => City(
name: city['nom'] ?? '',
postalCode: city['code_postal'] ?? postalCode,
))
.toList();
_isLoadingCities = false;
// Ne pas présélectionner automatiquement la première ville
// Laisser l'utilisateur saisir son code postal complet et choisir une ville
_selectedCity = null;
});
} else {
setState(() {
_cities = [];
_isLoadingCities = false;
});
}
} else {
setState(() {
_cities = [];
_isLoadingCities = false;
});
}
} catch (e) {
print('Erreur lors de la récupération des villes: $e');
setState(() {
_cities = [];
_isLoadingCities = false;
});
}
}
@override
void dispose() {
_nameController.dispose();
_amicaleNameController.dispose();
_postalCodeController.removeListener(_onPostalCodeChanged);
_postalCodeController.dispose();
_cityNameController.dispose();
_emailController.dispose();
_captchaController.dispose();
super.dispose();
}
@@ -58,7 +237,24 @@ class _RegisterPageState extends State<RegisterPage> {
final size = MediaQuery.of(context).size;
return Scaffold(
body: SafeArea(
body: Stack(
children: [
// Fond dégradé avec petits points blancs
AnimatedContainer(
duration: const Duration(milliseconds: 500),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.white, Colors.blue.shade300],
),
),
child: CustomPaint(
painter: DotsPainter(),
child: Container(width: double.infinity, height: double.infinity),
),
),
SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
@@ -69,10 +265,9 @@ class _RegisterPageState extends State<RegisterPage> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Logo et titre
SvgPicture.asset(
'assets/images/icon-geosector.svg',
Image.asset(
'assets/images/logo-geosector-1024.png',
height: 140,
fit: BoxFit.contain,
),
const SizedBox(height: 16),
Text(
@@ -87,7 +282,8 @@ class _RegisterPageState extends State<RegisterPage> {
Text(
'Enregistrez votre amicale sur GeoSector',
style: theme.textTheme.bodyLarge?.copyWith(
color: theme.colorScheme.onBackground.withOpacity(0.7),
color:
theme.colorScheme.onBackground.withOpacity(0.7),
),
textAlign: TextAlign.center,
),
@@ -99,10 +295,71 @@ class _RegisterPageState extends State<RegisterPage> {
if (mounted && _isConnected != isConnected) {
setState(() {
_isConnected = isConnected;
_connectionType =
connectivityService.connectionType;
});
}
},
),
// Message d'avertissement pour les utilisateurs mobiles sans connexion
if (_isMobile && !_isConnected)
Container(
margin: const EdgeInsets.only(top: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: theme.colorScheme.error.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: theme.colorScheme.error.withOpacity(0.3),
),
),
child: Column(
children: [
Icon(
Icons.signal_wifi_off,
color: theme.colorScheme.error,
size: 32,
),
const SizedBox(height: 8),
Text(
'Connexion Internet requise',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.error,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'Veuillez vous connecter à Internet (WiFi ou données mobiles) pour pouvoir vous inscrire.',
style: theme.textTheme.bodyMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: () async {
await _checkConnectivity();
if (_isConnected && mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Connexion Internet $_connectionType détectée.'),
backgroundColor: Colors.green,
),
);
}
},
icon: const Icon(Icons.refresh),
label: const Text('Vérifier à nouveau'),
style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary,
foregroundColor: theme.colorScheme.onPrimary,
),
),
],
),
),
const SizedBox(height: 16),
// Formulaire d'inscription
@@ -116,10 +373,14 @@ class _RegisterPageState extends State<RegisterPage> {
label: 'Nom complet',
hintText: 'Entrez votre nom complet',
prefixIcon: Icons.person_outline,
isRequired: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer votre nom complet';
}
if (value.length < 5) {
return 'Le nom doit contenir au moins 5 caractères';
}
return null;
},
),
@@ -130,6 +391,7 @@ class _RegisterPageState extends State<RegisterPage> {
hintText: 'Entrez votre email',
prefixIcon: Icons.email_outlined,
keyboardType: TextInputType.emailAddress,
isRequired: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer votre email';
@@ -147,10 +409,14 @@ class _RegisterPageState extends State<RegisterPage> {
label: 'Nom de l\'amicale',
hintText: 'Entrez le nom de votre amicale',
prefixIcon: Icons.local_fire_department,
isRequired: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer le nom de votre amicale';
}
if (value.length < 5) {
return 'Le nom de l\'amicale doit contenir au moins 5 caractères';
}
return null;
},
),
@@ -158,9 +424,15 @@ class _RegisterPageState extends State<RegisterPage> {
CustomTextField(
controller: _postalCodeController,
label: 'Code postal de l\'amicale',
hintText: 'Entrez le code postal de votre amicale',
hintText:
'Entrez le code postal de votre amicale',
prefixIcon: Icons.location_on_outlined,
keyboardType: TextInputType.number,
isRequired: true,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(5),
],
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer votre code postal';
@@ -172,26 +444,176 @@ class _RegisterPageState extends State<RegisterPage> {
},
),
const SizedBox(height: 16),
// Sélection de la commune
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'Commune de l\'amicale',
style:
theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w500,
color: theme.colorScheme.onBackground,
),
),
Text(
'',
style: TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
],
),
const SizedBox(height: 8),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: const Color(0xFFECEFF1),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: _isLoadingCities
? const Padding(
padding: EdgeInsets.symmetric(
vertical: 16),
child: Center(
child: CircularProgressIndicator(),
),
)
: DropdownButtonFormField<City>(
value: _selectedCity,
decoration: InputDecoration(
prefixIcon: Icon(
Icons.location_city_outlined,
color: theme.colorScheme.primary,
),
hintText: _postalCodeController
.text.length <
3
? 'Entrez d\'abord au moins 3 chiffres du code postal'
: _cities.isEmpty
? 'Aucune commune trouvée pour ce code postal'
: 'Sélectionnez une commune',
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(12),
borderSide: BorderSide.none,
),
contentPadding:
const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
),
items: _cities.map((City city) {
return DropdownMenuItem<City>(
value: city,
child: Text(city.name),
);
}).toList(),
onChanged: (City? newValue) {
setState(() {
_selectedCity = newValue;
// Mettre à jour le code postal avec celui de la ville sélectionnée
if (newValue != null) {
// Désactiver temporairement le listener pour éviter une boucle infinie
_postalCodeController
.removeListener(
_onPostalCodeChanged);
// Mettre à jour le code postal
_postalCodeController.text =
newValue.postalCode;
// Réactiver le listener
_postalCodeController
.addListener(
_onPostalCodeChanged);
}
});
},
validator: (value) {
if (value == null) {
return 'Veuillez sélectionner une commune';
}
return null;
},
isExpanded: true,
icon: Icon(
Icons.arrow_drop_down,
color: theme.colorScheme.primary,
),
dropdownColor: Colors.white,
),
),
],
),
const SizedBox(height: 16),
// Test anti-robot (captcha simple)
const SizedBox(height: 24),
Text(
'Vérification de sécurité',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w500,
color: theme.colorScheme.primary,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
CustomTextField(
controller: _cityNameController,
label: 'Commune de l\'amicale',
hintText:
'Entrez le nom de la commune de votre amicale',
prefixIcon: Icons.location_city_outlined,
controller: _captchaController,
label:
'Combien font $_captchaNum1 + $_captchaNum2 ?',
hintText: 'Entrez le résultat',
prefixIcon: Icons.security,
keyboardType: TextInputType.number,
isRequired: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer le nom de la commune de votre amicale';
return 'Veuillez répondre à cette question';
}
final int? answer = int.tryParse(value);
if (answer == null) {
return 'Veuillez entrer un nombre';
}
if (answer != _captchaNum1 + _captchaNum2) {
return 'La réponse est incorrecte';
}
return null;
},
),
const SizedBox(height: 16),
// Champ caché pour le token anti-robot
// Ce champ ne sera pas visible mais sera envoyé avec le formulaire
Opacity(
opacity: 0,
child: SizedBox(
height: 0,
child: TextFormField(
initialValue: _hiddenToken,
enabled: false,
),
),
),
const SizedBox(height: 32),
// Bouton d'inscription
CustomButton(
onPressed: (userRepository.isLoading || !_isConnected)
onPressed: (_isLoading ||
(_isMobile && !_isConnected))
? null
: () async {
if (_formKey.currentState!.validate()) {
@@ -219,7 +641,8 @@ class _RegisterPageState extends State<RegisterPage> {
if (connectivityService
.isConnected &&
mounted) {
ScaffoldMessenger.of(context)
ScaffoldMessenger.of(
context)
.showSnackBar(
SnackBar(
content: Text(
@@ -236,34 +659,285 @@ class _RegisterPageState extends State<RegisterPage> {
}
return;
}
final success =
await userRepository.register(
_emailController.text.trim(),
'', // Mot de passe vide, sera généré par le serveur
_nameController.text.trim(),
_amicaleNameController.text.trim(),
_postalCodeController.text,
_cityNameController.text.trim(),
);
if (success && mounted) {
context.go('/user');
} else if (mounted) {
// Vérifier que le captcha est correct
final int? captchaAnswer = int.tryParse(
_captchaController.text);
if (captchaAnswer !=
_captchaNum1 + _captchaNum2) {
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
content: Text(
'Échec de l\'inscription. Veuillez réessayer.'),
'La vérification de sécurité a échoué. Veuillez réessayer.'),
backgroundColor: Colors.red,
),
);
return;
}
// Préparer les données du formulaire
final Map<String, dynamic> formData = {
'email': _emailController.text.trim(),
'name': _nameController.text.trim(),
'amicale_name': _amicaleNameController
.text
.trim(),
'postal_code':
_postalCodeController.text,
'city_name':
_selectedCity?.name ?? '',
'captcha_answer': captchaAnswer,
'captcha_expected':
_captchaNum1 + _captchaNum2,
'token': _hiddenToken,
};
// Afficher un indicateur de chargement
setState(() {
_isLoading = true;
});
try {
// Envoyer les données à l'API
final baseUrl = Uri.base.origin;
final apiUrl =
'$baseUrl/api/register';
final response = await http.post(
Uri.parse(apiUrl),
headers: {
'Content-Type':
'application/json',
},
body: json.encode(formData),
);
// Masquer l'indicateur de chargement
setState(() {
_isLoading = false;
});
// Traiter la réponse
if (response.statusCode == 200 ||
response.statusCode == 201) {
final responseData =
json.decode(response.body);
// Vérifier si la réponse indique un succès
final bool isSuccess =
responseData['success'] ==
true ||
responseData['status'] ==
'success';
// Récupérer le message de la réponse
final String message = responseData[
'message'] ??
(isSuccess
? 'Inscription réussie !'
: 'Échec de l\'inscription. Veuillez réessayer.');
if (isSuccess) {
if (mounted) {
// Afficher une boîte de dialogue de succès
showDialog(
context: context,
barrierDismissible:
false, // L'utilisateur doit cliquer sur OK
builder:
(BuildContext context) {
return AlertDialog(
title: const Row(
children: [
Icon(
Icons.check_circle,
color: Colors.green,
),
SizedBox(width: 10),
Text(
'Inscription réussie'),
],
),
content: Column(
mainAxisSize:
MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
Text(
'Votre demande d\'inscription a été enregistrée avec succès.',
style: theme
.textTheme
.bodyLarge,
),
SizedBox(height: 16),
Text(
'Vous allez recevoir un email contenant :',
style: theme
.textTheme
.bodyMedium,
),
SizedBox(height: 8),
Row(
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
Icon(
Icons
.arrow_right,
size: 20,
color: theme
.colorScheme
.primary),
const SizedBox(
width: 4),
Expanded(
child: Text(
'Votre identifiant de connexion'),
),
],
),
SizedBox(height: 4),
Row(
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
Icon(
Icons
.arrow_right,
size: 20,
color: theme
.colorScheme
.primary),
SizedBox(
width: 4),
Expanded(
child: Text(
'Un lien pour définir votre mot de passe'),
),
],
),
SizedBox(height: 16),
Text(
'Vérifiez votre boîte de réception et vos spams.',
style: TextStyle(
fontStyle:
FontStyle
.italic,
color: theme
.colorScheme
.onSurface
.withOpacity(
0.7),
),
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.of(
context)
.pop();
// Rediriger vers la page de connexion
context
.go('/login');
},
child: Text('OK'),
style: TextButton
.styleFrom(
foregroundColor:
theme
.colorScheme
.primary,
textStyle: TextStyle(
fontWeight:
FontWeight
.bold),
),
),
],
);
},
);
}
} else {
// Afficher le message d'erreur retourné par l'API
if (mounted) {
// Afficher un message d'erreur plus visible
showDialog(
context: context,
builder:
(BuildContext context) {
return AlertDialog(
title: const Text(
'Erreur d\'inscription'),
content: Text(message),
actions: [
TextButton(
onPressed: () {
Navigator.of(
context)
.pop();
},
child:
const Text('OK'),
),
],
);
},
);
// Afficher également un SnackBar
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.red,
),
);
}
}
} else {
// Gérer les erreurs HTTP
if (mounted) {
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
content: Text(
'Erreur ${response.statusCode}: ${response.reasonPhrase ?? "Échec de l\'inscription"}'),
backgroundColor: Colors.red,
),
);
}
}
} catch (e) {
// Masquer l'indicateur de chargement
setState(() {
_isLoading = false;
});
// Gérer les exceptions
if (mounted) {
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
content: Text(
'Erreur: ${e.toString()}'),
backgroundColor: Colors.red,
),
);
}
}
}
},
text: _isConnected
? 'Enregistrer mon amicale'
: 'Connexion Internet requise',
isLoading: userRepository.isLoading,
text: (_isMobile && !_isConnected)
? 'Connexion Internet requise'
: 'Enregistrer mon amicale',
isLoading: _isLoading,
),
const SizedBox(height: 24),
@@ -290,17 +964,48 @@ class _RegisterPageState extends State<RegisterPage> {
],
),
// Lien vers la page d'accueil
// Lien vers le site web
TextButton(
onPressed: () {
context.go('/');
// Déterminer l'URL du site web en fonction de l'environnement
String webUrl = 'https://geosector.fr';
if (kIsWeb) {
final host = Uri.base.host;
if (host.startsWith('dapp.')) {
webUrl = 'https://dev.geosector.fr';
} else if (host.startsWith('rapp.')) {
webUrl = 'https://rec.geosector.fr';
} else if (host.startsWith('app.')) {
webUrl = 'https://geosector.fr';
}
}
// Ouvrir l'URL dans une nouvelle fenêtre/onglet
launchUrl(
Uri.parse(webUrl),
mode: LaunchMode.externalApplication,
);
},
child: Text(
'Revenir à l\'accueil',
child: const Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.language,
size: 16,
color: Colors.blue,
),
SizedBox(width: 8),
Text(
'Revenir sur le site web',
style: TextStyle(
color: theme.colorScheme.secondary,
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
@@ -311,6 +1016,36 @@ class _RegisterPageState extends State<RegisterPage> {
),
),
),
// Badge de version en bas à droite
if (_appVersion.isNotEmpty)
Positioned(
bottom: 16,
right: 16,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: theme.colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: theme.colorScheme.primary.withOpacity(0.3),
width: 1,
),
),
child: Text(
'v$_appVersion',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.primary.withOpacity(0.8),
fontSize: 10,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
);
}
}

Some files were not shown because too many files have changed in this diff Show More