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 6a609fb467
commit 599b9fcda0
662 changed files with 213221 additions and 174243 deletions

View File

@@ -0,0 +1,242 @@
import 'package:flutter/material.dart';
import 'package:geosector_app/presentation/widgets/custom_text_field.dart';
import 'package:geosector_app/presentation/widgets/form_section.dart';
/// Exemple d'utilisation modernisée du formulaire de passage
/// utilisant CustomTextField et FormSection adaptés
class PassageFormModernizedExample extends StatefulWidget {
final bool readOnly;
const PassageFormModernizedExample({
super.key,
this.readOnly = false,
});
@override
State<PassageFormModernizedExample> createState() => _PassageFormModernizedExampleState();
}
class _PassageFormModernizedExampleState extends State<PassageFormModernizedExample> {
// Controllers pour l'exemple
final _numeroController = TextEditingController();
final _rueBisController = TextEditingController();
final _rueController = TextEditingController();
final _villeController = TextEditingController();
final _dateController = TextEditingController();
final _timeController = TextEditingController();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _phoneController = TextEditingController();
final _montantController = TextEditingController();
final _remarqueController = TextEditingController();
@override
void dispose() {
_numeroController.dispose();
_rueBisController.dispose();
_rueController.dispose();
_villeController.dispose();
_dateController.dispose();
_timeController.dispose();
_nameController.dispose();
_emailController.dispose();
_phoneController.dispose();
_montantController.dispose();
_remarqueController.dispose();
super.dispose();
}
void _selectDate() {
// Logique de sélection de date
}
void _selectTime() {
// Logique de sélection d'heure
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Formulaire Modernisé')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
// Section Date et Heure avec FormSection
FormSection(
title: 'Date et Heure de passage',
icon: Icons.schedule,
children: [
Row(
children: [
Expanded(
child: CustomTextField(
controller: _dateController,
label: "Date",
isRequired: true,
readOnly: true,
showLabel: false,
hintText: "DD/MM/YYYY",
suffixIcon: const Icon(Icons.calendar_today),
onTap: widget.readOnly ? null : _selectDate,
),
),
const SizedBox(width: 12),
Expanded(
child: CustomTextField(
controller: _timeController,
label: "Heure",
isRequired: true,
readOnly: true,
showLabel: false,
hintText: "HH:MM",
suffixIcon: const Icon(Icons.access_time),
onTap: widget.readOnly ? null : _selectTime,
),
),
],
),
],
),
const SizedBox(height: 24),
// Section Adresse
FormSection(
title: 'Adresse',
icon: Icons.location_on,
children: [
Row(
children: [
Expanded(
flex: 1,
child: CustomTextField(
controller: _numeroController,
label: "Numéro",
isRequired: true,
showLabel: false,
textAlign: TextAlign.right,
keyboardType: TextInputType.number,
readOnly: widget.readOnly,
),
),
const SizedBox(width: 12),
Expanded(
flex: 1,
child: CustomTextField(
controller: _rueBisController,
label: "Bis/Ter",
showLabel: false,
readOnly: widget.readOnly,
),
),
],
),
const SizedBox(height: 16),
CustomTextField(
controller: _rueController,
label: "Rue",
isRequired: true,
showLabel: false,
readOnly: widget.readOnly,
),
const SizedBox(height: 16),
CustomTextField(
controller: _villeController,
label: "Ville",
isRequired: true,
showLabel: false,
readOnly: widget.readOnly,
),
],
),
const SizedBox(height: 24),
// Section Occupant
FormSection(
title: 'Occupant',
icon: Icons.person,
children: [
CustomTextField(
controller: _nameController,
label: "Nom de l'occupant",
isRequired: true,
showLabel: false,
readOnly: widget.readOnly,
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: CustomTextField(
controller: _emailController,
label: "Email",
showLabel: false,
keyboardType: TextInputType.emailAddress,
readOnly: widget.readOnly,
),
),
const SizedBox(width: 12),
Expanded(
child: CustomTextField(
controller: _phoneController,
label: "Téléphone",
showLabel: false,
keyboardType: TextInputType.phone,
readOnly: widget.readOnly,
),
),
],
),
],
),
const SizedBox(height: 24),
// Section Règlement (sans bordure)
FormSection(
title: '',
showBorder: true,
children: [
Row(
children: [
Expanded(
child: CustomTextField(
controller: _montantController,
label: "Montant (€)",
isRequired: true,
showLabel: false,
hintText: "0.00",
textAlign: TextAlign.right,
keyboardType: const TextInputType.numberWithOptions(decimal: true),
readOnly: widget.readOnly,
),
),
const SizedBox(width: 12),
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).colorScheme.outline),
borderRadius: BorderRadius.circular(8),
),
child: const Text("Dropdown de type de règlement"),
),
),
],
),
const SizedBox(height: 16),
CustomTextField(
controller: _remarqueController,
label: "Remarque",
showLabel: false,
hintText: "Commentaire sur le passage...",
maxLines: 2,
readOnly: widget.readOnly,
),
],
),
],
),
),
);
}
}