feat: Gestion des secteurs et migration v3.0.4+304

- Ajout système complet de gestion des secteurs avec contours géographiques
- Import des contours départementaux depuis GeoJSON
- API REST pour la gestion des secteurs (/api/sectors)
- Service de géolocalisation pour déterminer les secteurs
- Migration base de données avec tables x_departements_contours et sectors_adresses
- Interface Flutter pour visualisation et gestion des secteurs
- Ajout thème sombre dans l'application
- Corrections diverses et optimisations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
pierre
2025-08-07 11:01:45 +02:00
parent 3bbc599ab4
commit 1018b86537
620 changed files with 120502 additions and 91396 deletions

View File

@@ -1,2 +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
file:///home/pierre/.pub-cache/hosted/pub.dev/build_daemon-4.0.4/lib/fake.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/build_runner-2.5.4/lib/fake.dart

View File

@@ -1,11 +1,13 @@
// @dart=3.6
// ignore_for_file: directives_ordering
// build_runner >=2.4.16
// 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 'dart:isolate' as _i4;
import 'package:build_runner/src/build_script_generate/build_process_state.dart'
as _i5;
import 'package:build_runner/build_runner.dart' as _i6;
import 'dart:io' as _i7;
@@ -24,18 +26,6 @@ final _builders = <_i1.BuilderApplication>[
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,
@@ -43,12 +33,13 @@ final _builders = <_i1.BuilderApplication>[
];
void main(
List<String> args, [
_i5.SendPort? sendPort,
_i4.SendPort? sendPort,
]) async {
var result = await _i6.run(
await _i5.buildProcessState.receive(sendPort);
_i5.buildProcessState.isolateExitCode = await _i6.run(
args,
_builders,
);
sendPort?.send(result);
_i7.exitCode = result;
_i7.exitCode = _i5.buildProcessState.isolateExitCode!;
await _i5.buildProcessState.send(sendPort);
}

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,11 +15,11 @@ class PassageModelAdapter extends TypeAdapter<PassageModel> {
return PassageModel(
id: fields[0] as int,
fkOperation: fields[1] as int,
fkSector: fields[2] 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,
passedAt: fields[6] as DateTime?,
numero: fields[7] as String,
rue: fields[8] as String,
rueBis: fields[9] as String,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1 +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"}
{"sdk":"3.8.1 (stable) (Wed May 28 00:47:25 2025 -0700) on \"linux_x64\"","analyzer":"/home/pierre/.pub-cache/hosted/pub.dev/analyzer-6.11.0","build_resolvers":"/home/pierre/.pub-cache/hosted/pub.dev/build_resolvers-2.5.4"}

View File

@@ -9,6 +9,7 @@
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:shared_preferences_web/shared_preferences_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
@@ -17,6 +18,7 @@ void registerPlugins([final Registrar? pluginRegistrar]) {
ConnectivityPlusWebPlugin.registerWith(registrar);
GeolocatorPlugin.registerWith(registrar);
PackageInfoPlusWebPlugin.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);
registrar.registerMessageHandler();
}

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

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
["/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

@@ -1 +0,0 @@
/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

@@ -1 +0,0 @@
{"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/01af3ba6904766cfc820f0897fc71456/main.dart"]}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
/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

@@ -1 +0,0 @@
{"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

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

View File

@@ -1 +0,0 @@
{"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\"}]"}

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -9,6 +9,7 @@
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:shared_preferences_web/shared_preferences_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
@@ -17,6 +18,7 @@ void registerPlugins([final Registrar? pluginRegistrar]) {
ConnectivityPlusWebPlugin.registerWith(registrar);
GeolocatorPlugin.registerWith(registrar);
PackageInfoPlusWebPlugin.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);
registrar.registerMessageHandler();
}

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
3.32.4
3.32.8

0
app/.env-backup Normal file → Executable file
View File

8
app/.env-deploy-dev Normal file → Executable file
View File

@@ -2,7 +2,13 @@
HOST_SSH_USER=pierre
HOST_SSH_HOST=195.154.80.116
HOST_SSH_PORT=22
HOST_SSH_KEY=/Users/pierre/.ssh/id_rsa_mbpi
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
HOST_SSH_KEY=/Users/pierre/.ssh/id_rsa_mbpi
else
# Linux/Ubuntu
HOST_SSH_KEY=/home/pierre/.ssh/id_rsa_mbpi
fi
# Paramètres du container Incus
INCUS_PROJECT=default

File diff suppressed because one or more lines are too long

115
app/.vscode/settings.json vendored Normal file → Executable file
View File

@@ -1,4 +1,116 @@
{
"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": 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.tokenColorCustomizations": {
"textMateRules": [
{
"scope": ["storage.type.function", "storage.type.class"],
"settings": {
"fontStyle": "bold",
"foreground": "#4B9CD3"
}
}
]
},
"editor.minimap.renderCharacters": true,
"editor.minimap.maxColumn": 120,
"breadcrumbs.enabled": false,
// -- 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",
// -- Code
"editor.occurrencesHighlight": "singleFile", // On veut voir les occurences d'une variable
"editor.renderWhitespace": "trailing", // On ne veut pas laisser d'espace en fin de ligne
"editor.renderControlCharacters": true, // On veut voir les caractères de contrôle
// Thème
"editor.fontFamily": "'JetBrains Mono', 'Fira Code', 'Operator Mono Lig', monospace",
"editor.fontLigatures": false,
"editor.fontSize": 13,
"editor.lineHeight": 22,
"editor.guides.bracketPairs": "active",
// Ergonomie
"editor.wordWrap": "off",
"editor.rulers": [],
"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,
"editor.formatOnPaste": true,
"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,
"editor.formatOnPaste": true
},
"intelephense.format.braces": "k&r",
"intelephense.format.enable": true,
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnPaste": true
},
"prettier.printWidth": 360,
"prettier.semi": true,
"prettier.singleQuote": true,
"prettier.tabWidth": 2,
"prettier.trailingComma": "es5",
"explorer.autoReveal": false,
"explorer.confirmDragAndDrop": false,
"emmet.triggerExpansionOnTab": true,
"emmet.includeLanguages": {
"javascript": "javascriptreact"
},
"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,
"files.exclude": {
"**/.idea": true
},
// Languages
"javascript.preferences.importModuleSpecifierEnding": "js",
"typescript.preferences.importModuleSpecifierEnding": "js",
// Extensions
"tailwindCSS.experimental.configFile": "web/tailwind.config.js",
"editor.quickSuggestions": {
"strings": true
},
"[svelte]": {
"editor.defaultFormatter": "svelte.svelte-vscode",
"editor.formatOnSave": true
},
"prettier.documentSelectors": ["**/*.svelte"],
"svelte.plugin.svelte.diagnostics.enable": false,
"js/ts.implicitProjectConfig.checkJs": false,
"svelte.enable-ts-plugin": false,
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#2f7c47",
"activityBar.background": "#2f7c47",
@@ -18,5 +130,6 @@
"titleBar.inactiveBackground": "#21573299",
"titleBar.inactiveForeground": "#e7e7e799"
},
"peacock.color": "#215732"
"peacock.color": "#215732",
}

0
app/.windsurfrules Normal file → Executable file
View File

391
app/CLAUDE.md Executable file
View File

@@ -0,0 +1,391 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Instructions générales pour Claude
### Langue de communication
- **TOUJOURS répondre en français** sauf si explicitement demandé autrement
- Utiliser un langage technique approprié en français
### Méthodologie de travail
- **Travailler par étapes** : Ne jamais se lancer directement dans la modification du code
- **Présenter et proposer** : Toujours expliquer ce que je compte faire AVANT de le faire
- **Demander validation** : Attendre l'accord de l'utilisateur avant de procéder aux modifications
- **Structure de réponse** :
1. Analyser la demande
2. Proposer une solution détaillée
3. Lister les fichiers qui seront modifiés
4. Attendre la validation
5. Implémenter les changements
## Build Commands
- **Run app**: `flutter run` - start the Flutter app in debug mode
- **Build for release**: `flutter build apk` (Android), `flutter build ios` (iOS), `flutter build web` (Web)
- **Run tests**: `flutter test` - run all unit tests
- **Run specific test**: `flutter test test/widget_test.dart` - run a specific test file
- **Analyze code**: `flutter analyze` - check for errors, warnings, and lints
- **Format code**: `flutter format lib/` - format all Dart files
- **Generate code**: `flutter packages pub run build_runner build` - generate Hive adapters and JSON serialization
- **Clean build**: `flutter clean && flutter pub get` - clean and reinstall dependencies
- **Update dependencies**: `flutter pub upgrade` - update all packages
- **Launcher icons**: `flutter pub run flutter_launcher_icons:main` - generate app icons
## Code Architecture
### Core Architecture Pattern
The app follows a **Repository Pattern** with singleton services and reactive UI updates:
```
UI Layer (ValueListenableBuilder)
Repository Layer (Business Logic)
Service Layer (API + Hive Storage)
```
### Key Architectural Components
#### **Singleton Services** (lib/core/services/)
- `ApiService` - HTTP client with environment auto-detection (DEV/REC/PROD)
- `HiveService` - Local database management with typed Box handling
- `CurrentUserService` - User session and authentication state
- `CurrentAmicaleService` - Current organization context
- `DataLoadingService` - Orchestrates API data synchronization to Hive
#### **Repository Pattern** (lib/core/repositories/)
- `UserRepository` - User management and authentication
- `OperationRepository` - Operations and campaigns
- `MembreRepository` - Team member management
- `PassageRepository` - Distribution tracking
- `SectorRepository` - Geographic sectors
- `AmicaleRepository` - Organization management
#### **Reactive UI Pattern**
Uses `ValueListenableBuilder` with Hive boxes for automatic UI updates:
```dart
ValueListenableBuilder<Box<UserModel>>(
valueListenable: userRepository.getUsersBox().listenable(),
builder: (context, box, child) {
// UI automatically updates when data changes
},
)
```
### Data Flow Architecture
#### **"API First" Principle**
1. UI action triggers repository method
2. Repository attempts API call first
3. On success → Save to Hive → UI auto-updates via ValueListenableBuilder
4. On error → Show error message, no local changes
#### **Hive Storage Strategy**
- **TypeId Management**: Models use specific typeIds (UserModel: 0, OperationModel: 1, etc.)
- **Typed Boxes**: Each model has its own strongly-typed Hive box
- **Auto-sync**: Data automatically syncs between API and local storage
- **Offline-ready**: App functions with cached data when offline
- **Box Caching Optimization**: Repositories use cached Box references to prevent repeated access checks
### Error Handling Architecture
#### **Centralized Error Management**
- `ApiException` class extracts meaningful error messages from API responses
- `showError()` and `showSuccess()` methods provide consistent user feedback
- Repository methods propagate exceptions to UI layer for handling
#### **Error Flow Pattern**
```dart
try {
final result = await repository.updateUser(user);
ApiException.showSuccess(context, "User updated successfully");
} catch (e) {
ApiException.showError(context, e); // Handles all error types
}
```
#### **Smart Dialog Detection**
ApiException automatically detects when called from within a Dialog and adjusts message positioning:
- **Normal context**: Uses standard SnackBar
- **Dialog context**: Uses overlay SnackBar positioned above the Dialog
- **Mobile/Web aware**: Automatically adapts presentation for each platform
- **Consistent styling**: Unified colors, icons, and behavior across all contexts
```dart
// Same API works everywhere - smart context detection
if (validationFails) {
ApiException.showError(context, Exception("Validation failed"));
return; // Dialog stays open for corrections
}
// Success handling with auto-close
if (success) {
Navigator.pop(context); // Close dialog first
ApiException.showSuccess(context, "Operation completed");
}
```
### State Management Strategy
#### **No Provider/Bloc** - Direct Reactive Pattern
- Uses `ValueListenableBuilder` directly with Hive boxes
- Singleton services maintain global state
- `ChangeNotifier` only for specific repository loading states
#### **Dialog Auto-Management Pattern**
Dialogs handle their own submission and closing:
```dart
// Dialog manages its own lifecycle
void _handleSubmit() async {
try {
await repository.saveData(data);
Navigator.pop(context); // Auto-close on success
widget.onSuccess?.call(); // Simple callback
} catch (e) {
ApiException.showError(context, e); // Error without closing
}
}
```
### Multi-Role Permission System
#### **Role Hierarchy**
- **Role 1** (Membre): Field operations, distribution tracking
- **Role 2** (Admin Amicale): Organization management, member administration
- **Role 3+** (Super Admin): Global system administration
#### **Permission Checks**
```dart
final currentUser = CurrentUserService.instance;
if (currentUser.canAccessAdmin) {
// Show admin features
}
if (currentUser.isSuperAdmin) {
// Show super admin features
}
```
### Code Generation Requirements
#### **Hive Adapters**
Models require code generation for Hive serialization:
```bash
flutter packages pub run build_runner build
```
Run this after modifying any `@HiveType` models in `lib/core/data/models/`.
#### **Model Structure**
```dart
@HiveType(typeId: X)
class MyModel extends HiveObject {
@HiveField(0)
final int id;
@HiveField(1)
final String name;
}
```
### Development Workflow
#### **Working with Repositories**
1. Always inject repositories via constructor or global instances from `app.dart`
2. Use `try/catch` blocks for all repository calls
3. Show loading states during async operations
4. Handle errors with `ApiException.showError()`
5. Implement Hive box caching to avoid repeated access checks during high-frequency operations
#### **Adding New Features**
1. Create/modify models in `lib/core/data/models/`
2. Run `build_runner` to generate Hive adapters
3. Add repository methods in `lib/core/repositories/`
4. Create UI widgets in `lib/presentation/widgets/`
5. Wire up with `ValueListenableBuilder` for reactivity
#### **Testing Strategy**
- Unit tests for repositories and services
- Widget tests for UI components
- Integration tests for complete user flows
- Use `flutter test` for running tests
### Environment Configuration
#### **Auto-Detection**
The app automatically detects environment based on URL:
- `dapp.geosector.fr` → DEV environment
- `rapp.geosector.fr` → REC environment
- Production → PROD environment
- Non-web platforms → DEV by default
#### **API Configuration**
Environment-specific API endpoints are configured in `ApiService` automatically.
### Common Patterns to Follow
#### **Repository Method Pattern**
```dart
Future<bool> updateUser(UserModel user) async {
try {
final response = await ApiService.instance.put('/users/${user.id}', user.toJson());
final updatedUser = UserModel.fromJson(response.data);
await _saveUserToHive(updatedUser);
return true;
} catch (e) {
throw ApiException.fromDioException(e);
}
}
```
#### **Hive Box Caching Pattern**
```dart
class MyRepository {
// Cache de la box pour éviter les vérifications répétées
Box<MyModel>? _cachedBox;
// Getter lazy pour n'accéder à la boîte que lorsque nécessaire
Box<MyModel> get _myBox {
if (_cachedBox == null) {
if (!Hive.isBoxOpen(AppKeys.myBoxName)) {
throw Exception('La boîte n\'est pas ouverte');
}
_cachedBox = Hive.box<MyModel>(AppKeys.myBoxName);
}
return _cachedBox!;
}
// Méthode pour réinitialiser le cache après modification
void _resetCache() {
_cachedBox = null;
}
// Sauvegarder avec réinitialisation du cache
Future<void> saveItem(MyModel item) async {
await _myBox.put(item.id, item);
_resetCache(); // Crucial pour ValueListenableBuilder
notifyListeners();
}
}
```
#### **Widget Error Handling**
```dart
Future<void> _handleSave() async {
try {
setState(() => _isLoading = true);
await repository.saveData(data);
ApiException.showSuccess(context, "Saved successfully");
} catch (e) {
ApiException.showError(context, e);
} finally {
setState(() => _isLoading = false);
}
}
```
#### **ValueListenableBuilder Usage**
```dart
ValueListenableBuilder<Box<ModelType>>(
valueListenable: repository.getBox().listenable(),
builder: (context, box, child) {
final items = box.values.where((item) => filterCondition).toList();
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ItemWidget(item: items[index]),
);
},
)
```
### Hive Box Performance Management
#### **Box Caching Strategy**
Repositories implement a caching pattern to optimize Hive box access and prevent performance issues during high-frequency operations:
**Problems Solved:**
- Eliminates repeated `Hive.isBoxOpen()` checks (up to 848 checks per page load)
- Improves performance during list rendering and filtering operations
- Maintains thread-safe access to Hive boxes
**Implementation Pattern:**
```dart
class ExampleRepository {
// Private cached box reference
Box<ExampleModel>? _cachedBox;
// Lazy getter with caching
Box<ExampleModel> get _exampleBox {
if (_cachedBox == null) {
if (!Hive.isBoxOpen(AppKeys.exampleBoxName)) {
throw Exception('Box not open: ${AppKeys.exampleBoxName}');
}
_cachedBox = Hive.box<ExampleModel>(AppKeys.exampleBoxName);
debugPrint('Repository: Box ${AppKeys.exampleBoxName} cached');
}
return _cachedBox!;
}
// Cache reset for ValueListenableBuilder notifications
void _resetCache() {
_cachedBox = null;
}
}
```
#### **When to Reset Cache**
Cache reset is **essential** after any database modification to ensure ValueListenableBuilder and other Hive listeners detect changes:
```dart
// REQUIRED: After individual saves
Future<void> saveItem(ExampleModel item) async {
await _exampleBox.put(item.id, item);
_resetCache(); // ← Essential for UI reactivity
notifyListeners();
}
// REQUIRED: After deletions
Future<void> deleteItem(int id) async {
await _exampleBox.delete(id);
_resetCache(); // ← Essential for UI reactivity
notifyListeners();
}
// REQUIRED: After bulk operations
Future<void> clearAll() async {
await _exampleBox.clear();
_resetCache(); // ← Essential for UI reactivity
notifyListeners();
}
// REQUIRED: After API data processing
Future<void> processApiData(List<dynamic> data) async {
await _exampleBox.clear();
for (final item in data) {
await _exampleBox.put(item.id, ExampleModel.fromJson(item));
}
_resetCache(); // ← Essential for UI reactivity
notifyListeners();
}
```
#### **Performance Impact**
| Scenario | Without Caching | With Caching |
|----------|-----------------|--------------|
| Loading 848 passages | 848 box checks | 1 box access |
| List filtering | Check per item | Cached access |
| ValueListenableBuilder | Repeated verification | Optimized access |
| Memory usage | Minimal overhead | Negligible cache |
#### **Best Practices**
1. **Always cache** in repositories that handle frequently accessed data
2. **Always reset cache** after any modification operation
3. **Use lazy getters** to avoid unnecessary box access
4. **Include debug prints** to monitor caching behavior during development
5. **Apply pattern consistently** across all repositories for maintainability
This architecture ensures type safety, reactivity, offline capability, and maintainable code structure across the entire application.

0
app/PLAN2-APP.md Normal file → Executable file
View File

160
app/README-APP.md Normal file → Executable file
View File

@@ -70,7 +70,7 @@ GEOSECTOR est une solution complète développée en Flutter qui révolutionne l
- **🗺️ Cartographie avancée** : Flutter Map avec tuiles Mapbox
- **📍 Géolocalisation précise** : Suivi GPS des équipes
- **💾 Stockage hybride** : Cache local Hive + synchronisation cloud
- **💾 Stockage hybride** : Cache local Hive + synchronisation cloud avec optimisation des performances
- **💬 Communication** : Chat MQTT en temps réel
- **🔐 Sécurité** : Authentification JWT + gestion fine des permissions
- **📱 Multi-plateforme** : iOS, Android, Web
@@ -233,10 +233,32 @@ AudienceTargetModelAdapter() // typeId: 24
NotificationSettingsAdapter() // typeId: 25
```
Compatibilité entre modèles
UserModel ↔ MembreModel : Conversion bidirectionnelle via toUserModel() et fromUserModel()
Synchronisation : Maintien de la cohérence entre les deux représentations
Champs spécialisés : Préservation des données spécifiques à chaque modèle
### Clarification importante : UserModel vs MembreModel vs UserSectorModel
⚠️ **ATTENTION** : Il existe une distinction cruciale entre ces trois modèles :
#### **UserModel** (Box: `users`)
- Représente **uniquement l'utilisateur courant connecté** (current user)
- Stocké dans la box Hive `users` qui ne contient qu'un seul enregistrement
- Utilisé pour l'authentification et la session de l'utilisateur actuel
- **Ne pas confondre avec les membres de l'amicale**
#### **MembreModel** (Box: `membres`)
- Représente **tous les membres d'une amicale**
- Stocké dans la box Hive `membres` qui contient plusieurs enregistrements
- Utilisé pour la gestion des équipes et l'attribution aux secteurs
- Chaque membre a son propre ID unique
#### **UserSectorModel** (Box: `user_sector`)
- Représente **l'association entre un membre et un secteur**
- ⚠️ **IMPORTANT** : Le champ `id` dans `UserSectorModel` correspond à l'ID du **membre** (MembreModel.id), **PAS** à l'ID de l'utilisateur (UserModel.id)
- Permet de savoir quels membres sont affectés à quels secteurs
- Nom trompeur : devrait s'appeler "MemberSectorModel" pour éviter la confusion
### Compatibilité entre modèles
- **UserModel ↔ MembreModel** : Conversion bidirectionnelle via `toUserModel()` et `fromUserModel()`
- **Synchronisation** : Maintien de la cohérence entre les deux représentations
- **Champs spécialisés** : Préservation des données spécifiques à chaque modèle
🎨 Interface utilisateur
Architecture des composants
UserFormDialog - Modale unifiée
@@ -286,7 +308,44 @@ UX claire : Feedback immédiat sur les erreurs de validation
## ⚠️ Gestion des erreurs
Architecture centralisée
### 🎯 Système ApiException intelligent
GEOSECTOR v2.0 utilise un **système centralisé de gestion des messages** qui s'adapte automatiquement au contexte d'affichage pour garantir une visibilité optimale des notifications utilisateur.
#### **🧠 Détection automatique de contexte**
L'`ApiException` détecte intelligemment si elle est appelée depuis une Dialog et adapte l'affichage :
- **📱 Contexte normal** : SnackBar standard en bas d'écran
- **💬 Contexte Dialog** : Overlay SnackBar positionné au-dessus de la Dialog estompée
- **🌐 Mobile/Web** : Adaptation automatique selon la plateforme
- **🎨 Cohérence visuelle** : Couleurs, icônes et comportements unifiés
```dart
// Même API partout - détection intelligente du contexte
void _handleValidation() {
if (formInvalid) {
// Dans une Dialog : overlay au-dessus, Dialog reste ouverte
ApiException.showError(context, Exception("Champs requis manquants"));
return;
}
// Succès : fermer Dialog puis afficher confirmation
Navigator.pop(context);
ApiException.showSuccess(context, "Données sauvegardées");
}
```
#### **✨ Avantages de l'approche unifiée**
| Aspect | Avant | Avec ApiException |
|--------|-------|-------------------|
| **Visibilité** | SnackBar masqué par Dialog | Overlay visible au-dessus |
| **Consistance** | Messages dispersés | API unifiée dans toute l'app |
| **Maintenance** | Code répétitif | Système centralisé |
| **UX** | Frustrant (messages cachés) | Fluide et prévisible |
### Architecture centralisée
```mermaid
sequenceDiagram
@@ -642,6 +701,95 @@ CurrentAmicaleService : Amicale de l'utilisateur actuel
ApiService : Communication centralisée avec l'API
DataLoadingService : Orchestration du chargement des données
## 🚀 Optimisation des performances Hive
### 📈 Gestion des Box Hive avec cache
GEOSECTOR v2.0 implémente une **stratégie de cache avancée** pour les Box Hive afin d'éliminer les goulots d'étranglement de performance lors d'opérations haute fréquence.
#### **🎯 Problème résolu**
Avant l'optimisation, l'application effectuait jusqu'à **848 vérifications** `Hive.isBoxOpen()` par chargement de page, causant des ralentissements significatifs lors du rendu des listes et du filtrage des données.
#### **💡 Solution : Cache lazy des Box**
```dart
// Pattern implémenté dans tous les repositories
class OptimizedRepository {
Box<ModelType>? _cachedBox;
Box<ModelType> get _modelBox {
if (_cachedBox == null) {
if (!Hive.isBoxOpen(AppKeys.boxName)) {
throw Exception('Box non ouverte');
}
_cachedBox = Hive.box<ModelType>(AppKeys.boxName);
debugPrint('Repository: Box mise en cache');
}
return _cachedBox!;
}
void _resetCache() {
_cachedBox = null;
}
}
```
#### **🔄 Gestion du cache et réactivité**
**Point critique** : Le cache doit être réinitialisé après **toute modification** pour garantir le bon fonctionnement de `ValueListenableBuilder` :
```dart
// ✅ OBLIGATOIRE après modification
Future<void> saveData(ModelType data) async {
await _modelBox.put(data.id, data);
_resetCache(); // ← Crucial pour la réactivité UI
notifyListeners();
}
// ✅ OBLIGATOIRE après suppression
Future<void> deleteData(int id) async {
await _modelBox.delete(id);
_resetCache(); // ← Crucial pour la réactivité UI
notifyListeners();
}
// ✅ OBLIGATOIRE après vidage ou traitement API
Future<void> processApiData(List<dynamic> data) async {
await _modelBox.clear();
// ... traitement des données
_resetCache(); // ← Crucial pour la réactivité UI
notifyListeners();
}
```
#### **📊 Impact performance**
| Métrique | Avant optimisation | Après optimisation |
|----------|-------------------|-------------------|
| **Vérifications box** | 848 par page | 1 par session |
| **Temps de rendu** | 200-500ms | <50ms |
| **Filtrage liste** | Lent (check répétés) | Instantané |
| **Mémoire** | Overhead minimal | Impact négligeable |
#### **🏗️ Repositories optimisés**
L'optimisation est implémentée dans tous les repositories critiques :
- **SectorRepository** : Gestion des secteurs géographiques
- **PassageRepository** : Suivi des distributions
- **MembreRepository** : Gestion des équipes
- **OperationRepository** : Campagnes et opérations
- **AmicaleRepository** : Organisations
#### **🎯 Règles d'implémentation**
1. **Cache systématique** : Tous les repositories fréquemment utilisés
2. **Reset obligatoire** : Après toute opération de modification
3. **Getter lazy** : Accès différé à la box uniquement si nécessaire
4. **Debug logging** : Traçabilité du cache en développement
5. **Cohérence** : Pattern appliqué uniformément
Cette architecture garantit une application performante, maintenable et évolutive avec une excellente expérience utilisateur. 🚀
## 🎨 Architecture des Dialogs Auto-Gérées

0
app/README-icons.md Normal file → Executable file
View File

16
app/README.md Normal file
View File

@@ -0,0 +1,16 @@
# 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.

0
app/add_framework_paths.rb Normal file → Executable file
View File

0
app/analysis_options.yaml Normal file → Executable file
View File

42
app/android/app/build.gradle.kts Normal file → Executable file
View File

@@ -1,3 +1,6 @@
import java.util.Properties
import java.io.FileInputStream
plugins {
id("com.android.application")
id("kotlin-android")
@@ -5,12 +8,20 @@ plugins {
id("dev.flutter.flutter-gradle-plugin")
}
// Charger les propriétés de signature
val keystorePropertiesFile = rootProject.file("key.properties")
val keystoreProperties = Properties()
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
namespace = "fr.geosector.app.geosector_app"
namespace = "fr.geosector.app2025"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
ndkVersion = "27.0.12077973"
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
@@ -20,8 +31,8 @@ android {
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "fr.geosector.app.geosector_app"
// Application ID for Google Play Store
applicationId = "fr.geosector.app2025"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
@@ -30,11 +41,24 @@ android {
versionName = flutter.versionName
}
signingConfigs {
if (keystorePropertiesFile.exists()) {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = file(keystoreProperties["storeFile"] as String)
storePassword = keystoreProperties["storePassword"] as String
}
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
if (keystorePropertiesFile.exists()) {
signingConfig = signingConfigs.getByName("release")
} else {
signingConfig = signingConfigs.getByName("debug")
}
}
}
}
@@ -42,3 +66,7 @@ android {
flutter {
source = "../.."
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}

0
app/android/app/src/debug/AndroidManifest.xml Normal file → Executable file
View File

0
app/android/app/src/main/AndroidManifest.xml Normal file → Executable file
View File

View File

@@ -0,0 +1,5 @@
package fr.geosector.app2025
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

View File

View File

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 303 B

After

Width:  |  Height:  |  Size: 303 B

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 300 B

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 306 B

After

Width:  |  Height:  |  Size: 306 B

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 318 B

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