Restructuration majeure du projet: migration de flutt vers app, ajout de l'API et mise à jour du site web

This commit is contained in:
d6soft
2025-05-16 09:19:03 +02:00
parent b5aafc424b
commit 5c2620de30
391 changed files with 19780 additions and 7233 deletions

View File

@@ -0,0 +1,316 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_svg/flutter_svg.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/app.dart'; // Pour accéder aux instances globales
class RegisterPage extends StatefulWidget {
const RegisterPage({super.key});
@override
State<RegisterPage> createState() => _RegisterPageState();
}
class _RegisterPageState extends State<RegisterPage> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _amicaleNameController = TextEditingController();
final _postalCodeController = TextEditingController();
final _cityNameController = TextEditingController();
final _emailController = TextEditingController();
// État de la connexion Internet
bool _isConnected = false;
@override
void initState() {
super.initState();
// Initialiser l'état de la connexion
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
// Utiliser l'instance globale de connectivityService définie dans app.dart
setState(() {
_isConnected = connectivityService.isConnected;
});
}
});
}
@override
void dispose() {
_nameController.dispose();
_amicaleNameController.dispose();
_postalCodeController.dispose();
_cityNameController.dispose();
_emailController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// Utiliser l'instance globale de userRepository définie dans app.dart
final theme = Theme.of(context);
final size = MediaQuery.of(context).size;
return Scaffold(
body: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 500),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Logo et titre
SvgPicture.asset(
'assets/images/icon-geosector.svg',
height: 140,
fit: BoxFit.contain,
),
const SizedBox(height: 16),
Text(
'Inscription Administrateur',
style: theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'Enregistrez votre amicale sur GeoSector',
style: theme.textTheme.bodyLarge?.copyWith(
color: theme.colorScheme.onBackground.withOpacity(0.7),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
// Indicateur de connectivité
ConnectivityIndicator(
onConnectivityChanged: (isConnected) {
if (mounted && _isConnected != isConnected) {
setState(() {
_isConnected = isConnected;
});
}
},
),
const SizedBox(height: 16),
// Formulaire d'inscription
Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
CustomTextField(
controller: _nameController,
label: 'Nom complet',
hintText: 'Entrez votre nom complet',
prefixIcon: Icons.person_outline,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer votre nom complet';
}
return null;
},
),
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;
},
),
const SizedBox(height: 16),
CustomTextField(
controller: _amicaleNameController,
label: 'Nom de l\'amicale',
hintText: 'Entrez le nom de votre amicale',
prefixIcon: Icons.local_fire_department,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer le nom de votre amicale';
}
return null;
},
),
const SizedBox(height: 16),
CustomTextField(
controller: _postalCodeController,
label: 'Code postal de l\'amicale',
hintText: 'Entrez le code postal de votre amicale',
prefixIcon: Icons.location_on_outlined,
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer votre code postal';
}
if (!RegExp(r'^[0-9]{5}$').hasMatch(value)) {
return 'Le code postal doit contenir 5 chiffres';
}
return null;
},
),
const SizedBox(height: 16),
CustomTextField(
controller: _cityNameController,
label: 'Commune de l\'amicale',
hintText:
'Entrez le nom de la commune de votre amicale',
prefixIcon: Icons.location_city_outlined,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer le nom de la commune de votre amicale';
}
return null;
},
),
const SizedBox(height: 16),
const SizedBox(height: 32),
// Bouton d'inscription
CustomButton(
onPressed: (userRepository.isLoading || !_isConnected)
? null
: () async {
if (_formKey.currentState!.validate()) {
// Vérifier la connexion Internet avant de soumettre
// Utiliser l'instance globale de connectivityService définie dans app.dart
await connectivityService
.checkConnectivity();
if (!connectivityService.isConnected) {
if (mounted) {
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
content: const Text(
'Aucune connexion Internet. L\'inscription nécessite une connexion active.'),
backgroundColor:
theme.colorScheme.error,
duration:
const Duration(seconds: 3),
action: SnackBarAction(
label: 'Réessayer',
onPressed: () async {
await connectivityService
.checkConnectivity();
if (connectivityService
.isConnected &&
mounted) {
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
content: Text(
'Connexion Internet ${connectivityService.connectionType} détectée.'),
backgroundColor:
Colors.green,
),
);
}
},
),
),
);
}
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) {
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
content: Text(
'Échec de l\'inscription. Veuillez réessayer.'),
backgroundColor: Colors.red,
),
);
}
}
},
text: _isConnected
? 'Enregistrer mon amicale'
: 'Connexion Internet requise',
isLoading: userRepository.isLoading,
),
const SizedBox(height: 24),
// Déjà un compte
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Déjà un compte ?',
style: theme.textTheme.bodyMedium,
),
TextButton(
onPressed: () {
context.go('/login');
},
child: Text(
'Se connecter',
style: TextStyle(
color: theme.colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
),
],
),
// Lien vers la page d'accueil
TextButton(
onPressed: () {
context.go('/');
},
child: Text(
'Revenir à l\'accueil',
style: TextStyle(
color: theme.colorScheme.secondary,
),
),
),
],
),
),
],
),
),
),
),
),
);
}
}