947 lines
30 KiB
Dart
947 lines
30 KiB
Dart
// ===== lib/pages/login.dart AVEC SUPPORT ENTREPRISE =====
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:wtpe/widgets/wortis_logo.dart';
|
|
import '../main.dart';
|
|
import '../services/wortis_api_service.dart';
|
|
import 'role_navigator.dart';
|
|
|
|
class LoginPage extends StatefulWidget {
|
|
const LoginPage({super.key});
|
|
|
|
@override
|
|
_LoginPageState createState() => _LoginPageState();
|
|
}
|
|
|
|
class _LoginPageState extends State<LoginPage>
|
|
with SingleTickerProviderStateMixin {
|
|
final _formKey = GlobalKey<FormState>();
|
|
final _agentIdController = TextEditingController();
|
|
final _pinController = TextEditingController();
|
|
bool _obscurePin = true;
|
|
bool _apiConnected = false;
|
|
bool _testingConnection = false;
|
|
|
|
late AnimationController _animationController;
|
|
late Animation<double> _fadeAnimation;
|
|
late Animation<Offset> _slideAnimation;
|
|
|
|
// Couleurs Wortis
|
|
static const Color primaryColor = Color(0xFF006699);
|
|
static const Color secondaryColor = Color(0xFF0088CC);
|
|
static const Color accentColor = Color(0xFFFF6B35);
|
|
static const Color backgroundColor = Color(0xFFF8FAFC);
|
|
static const Color surfaceColor = Colors.white;
|
|
static const Color errorColor = Color(0xFFE53E3E);
|
|
static const Color textPrimaryColor = Color(0xFF1A202C);
|
|
static const Color textSecondaryColor = Color(0xFF718096);
|
|
static const Color borderColor = Color(0xFFE2E8F0);
|
|
static const Color enterpriseColor = Color(0xFF8B5CF6);
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_setupAnimations();
|
|
_testApiConnection();
|
|
}
|
|
|
|
void _setupAnimations() {
|
|
_animationController = AnimationController(
|
|
duration: Duration(milliseconds: 1500),
|
|
vsync: this,
|
|
);
|
|
|
|
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
|
CurvedAnimation(
|
|
parent: _animationController,
|
|
curve: Interval(0.0, 0.8, curve: Curves.easeOut),
|
|
),
|
|
);
|
|
|
|
_slideAnimation = Tween<Offset>(
|
|
begin: Offset(0, 0.3),
|
|
end: Offset.zero,
|
|
).animate(
|
|
CurvedAnimation(
|
|
parent: _animationController,
|
|
curve: Interval(0.3, 1.0, curve: Curves.easeOut),
|
|
),
|
|
);
|
|
|
|
_animationController.forward();
|
|
}
|
|
|
|
Future<void> _testApiConnection() async {
|
|
setState(() {
|
|
_testingConnection = true;
|
|
});
|
|
|
|
final isConnected = await AuthApiService.testConnection();
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_apiConnected = isConnected;
|
|
_testingConnection = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> _createTestUser() async {
|
|
String? nom = await _showCreateUserDialog();
|
|
if (nom == null || nom.isEmpty) return;
|
|
|
|
final result = await AuthApiService.createUser(nom, '1234');
|
|
|
|
if (result['success'] == true) {
|
|
final agentId = result['generated_agent_id'];
|
|
setState(() {
|
|
_agentIdController.text = agentId;
|
|
_pinController.text = '1234';
|
|
});
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Row(
|
|
children: [
|
|
Icon(Icons.check_circle, color: Colors.white),
|
|
SizedBox(width: 8),
|
|
Expanded(child: Text('Utilisateur créé: $agentId / PIN: 1234')),
|
|
],
|
|
),
|
|
backgroundColor: Colors.green,
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
);
|
|
} else {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('Erreur: ${result['message']}'),
|
|
backgroundColor: Colors.orange,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<String?> _showCreateUserDialog() async {
|
|
final nameController = TextEditingController();
|
|
|
|
return showDialog<String>(
|
|
context: context,
|
|
builder:
|
|
(context) => AlertDialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
title: Row(
|
|
children: [
|
|
Icon(Icons.person_add, color: primaryColor),
|
|
SizedBox(width: 12),
|
|
Text('Créer un utilisateur'),
|
|
],
|
|
),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
padding: EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: primaryColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(color: primaryColor.withOpacity(0.3)),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.info_outline, color: primaryColor, size: 20),
|
|
SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
'L\'ID agent sera généré automatiquement (WRT####)',
|
|
style: TextStyle(fontSize: 12, color: primaryColor),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(height: 16),
|
|
TextField(
|
|
controller: nameController,
|
|
decoration: InputDecoration(
|
|
labelText: 'Nom complet',
|
|
hintText: 'Ex: Jean Dupont',
|
|
prefixIcon: Icon(Icons.person_outline),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
autofocus: true,
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text('Annuler'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed:
|
|
() => Navigator.pop(context, nameController.text.trim()),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: primaryColor,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child: Text('Créer', style: TextStyle(color: Colors.white)),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_agentIdController.dispose();
|
|
_pinController.dispose();
|
|
_animationController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _login() async {
|
|
if (_formKey.currentState!.validate()) {
|
|
final authController = Provider.of<AuthController>(
|
|
context,
|
|
listen: false,
|
|
);
|
|
|
|
authController.clearError();
|
|
|
|
bool success = await authController.login(
|
|
_agentIdController.text.trim(),
|
|
_pinController.text.trim(),
|
|
false,
|
|
);
|
|
|
|
if (success && mounted) {
|
|
// Afficher un message de bienvenue personnalisé
|
|
final isEnterprise = authController.isEnterpriseMember;
|
|
final accountType = isEnterprise ? 'Entreprise' : 'Agent';
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Row(
|
|
children: [
|
|
Container(
|
|
padding: EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
isEnterprise ? Icons.business : Icons.person,
|
|
color: Colors.white,
|
|
size: 20,
|
|
),
|
|
),
|
|
SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
'Bienvenue ${authController.agentName}',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
if (isEnterprise &&
|
|
authController.enterprise?.nomEntreprise != null) ...[
|
|
SizedBox(height: 2),
|
|
Text(
|
|
authController.enterprise!.nomEntreprise,
|
|
style: TextStyle(
|
|
color: Colors.white.withOpacity(0.9),
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Text(
|
|
accountType,
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 10,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
backgroundColor: isEnterprise ? enterpriseColor : Colors.green,
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
duration: Duration(seconds: 2),
|
|
margin: EdgeInsets.all(16),
|
|
),
|
|
);
|
|
|
|
// Navigation basée sur le rôle
|
|
final userRole = authController.role ?? 'agent';
|
|
RoleNavigator.navigateByRole(context, userRole);
|
|
} else if (mounted) {
|
|
final errorMessage =
|
|
authController.errorMessage ?? 'Erreur de connexion';
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Row(
|
|
children: [
|
|
Icon(Icons.error_outline, color: Colors.white, size: 24),
|
|
SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
'Connexion échouée',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
SizedBox(height: 2),
|
|
Text(errorMessage, style: TextStyle(fontSize: 12)),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
backgroundColor: errorColor,
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
duration: Duration(seconds: 4),
|
|
margin: EdgeInsets.all(16),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _openWifiSettings() async {
|
|
try {
|
|
const platform = MethodChannel('com.wortis.agent/settings');
|
|
await platform.invokeMethod('exitKioskMode');
|
|
await platform.invokeMethod('openWifiSettings');
|
|
} catch (e) {
|
|
print('Erreur ouverture WiFi: $e');
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('Impossible d\'ouvrir les paramètres WiFi'),
|
|
backgroundColor: Colors.orange,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: backgroundColor,
|
|
body: SafeArea(
|
|
child: SingleChildScrollView(
|
|
physics: BouncingScrollPhysics(),
|
|
padding: EdgeInsets.all(24),
|
|
child: FadeTransition(
|
|
opacity: _fadeAnimation,
|
|
child: SlideTransition(
|
|
position: _slideAnimation,
|
|
child: Column(
|
|
children: [
|
|
SizedBox(height: 20),
|
|
_buildApiStatusIndicator(),
|
|
SizedBox(height: 20),
|
|
_buildHeader(),
|
|
SizedBox(height: 50),
|
|
_buildLoginForm(),
|
|
SizedBox(height: 30),
|
|
_buildHelpSection(),
|
|
SizedBox(height: 20),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildApiStatusIndicator() {
|
|
return Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
decoration: BoxDecoration(
|
|
color:
|
|
_testingConnection
|
|
? Colors.orange.withOpacity(0.1)
|
|
: _apiConnected
|
|
? Colors.green.withOpacity(0.1)
|
|
: Colors.red.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(25),
|
|
border: Border.all(
|
|
color:
|
|
_testingConnection
|
|
? Colors.orange.withOpacity(0.3)
|
|
: _apiConnected
|
|
? Colors.green.withOpacity(0.3)
|
|
: Colors.red.withOpacity(0.3),
|
|
width: 1.5,
|
|
),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
if (_testingConnection) ...[
|
|
SizedBox(
|
|
width: 16,
|
|
height: 16,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 2,
|
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.orange),
|
|
),
|
|
),
|
|
SizedBox(width: 8),
|
|
Text(
|
|
'Test connexion...',
|
|
style: TextStyle(
|
|
color: Colors.orange.shade700,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
] else ...[
|
|
Container(
|
|
padding: EdgeInsets.all(4),
|
|
decoration: BoxDecoration(
|
|
color:
|
|
_apiConnected
|
|
? Colors.green.withOpacity(0.2)
|
|
: Colors.red.withOpacity(0.2),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
_apiConnected
|
|
? Icons.cloud_done_rounded
|
|
: Icons.cloud_off_rounded,
|
|
size: 16,
|
|
color:
|
|
_apiConnected ? Colors.green.shade700 : Colors.red.shade700,
|
|
),
|
|
),
|
|
SizedBox(width: 8),
|
|
Text(
|
|
_apiConnected ? 'API connectée' : 'API déconnectée',
|
|
style: TextStyle(
|
|
color:
|
|
_apiConnected ? Colors.green.shade700 : Colors.red.shade700,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
SizedBox(width: 8),
|
|
GestureDetector(
|
|
onTap: _testApiConnection,
|
|
child: Container(
|
|
padding: EdgeInsets.all(4),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.5),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
Icons.refresh_rounded,
|
|
size: 16,
|
|
color:
|
|
_apiConnected
|
|
? Colors.green.shade700
|
|
: Colors.red.shade700,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildHeader() {
|
|
return Column(
|
|
children: [
|
|
WortisLogoWidget(size: 100, isWhite: false, withShadow: true),
|
|
SizedBox(height: 24),
|
|
ShaderMask(
|
|
shaderCallback:
|
|
(bounds) => LinearGradient(
|
|
colors: [primaryColor, secondaryColor],
|
|
).createShader(bounds),
|
|
child: Text(
|
|
'WORTIS AGENT',
|
|
style: TextStyle(
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
letterSpacing: 2,
|
|
),
|
|
),
|
|
),
|
|
SizedBox(height: 8),
|
|
Text(
|
|
'Connexion à votre espace',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
color: textSecondaryColor,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
SizedBox(height: 12),
|
|
// Badges des types de comptes supportés
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_buildAccountTypeBadge(
|
|
icon: Icons.person,
|
|
label: 'Agent',
|
|
color: primaryColor,
|
|
),
|
|
SizedBox(width: 12),
|
|
_buildAccountTypeBadge(
|
|
icon: Icons.business,
|
|
label: 'Entreprise',
|
|
color: enterpriseColor,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildAccountTypeBadge({
|
|
required IconData icon,
|
|
required String label,
|
|
required Color color,
|
|
}) {
|
|
return Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(20),
|
|
border: Border.all(color: color.withOpacity(0.3), width: 1),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(icon, color: color, size: 14),
|
|
SizedBox(width: 6),
|
|
Text(
|
|
label,
|
|
style: TextStyle(
|
|
color: color,
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildLoginForm() {
|
|
return Container(
|
|
padding: EdgeInsets.all(28),
|
|
decoration: BoxDecoration(
|
|
color: surfaceColor,
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.08),
|
|
blurRadius: 20,
|
|
offset: Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: primaryColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Icon(
|
|
Icons.login_rounded,
|
|
color: primaryColor,
|
|
size: 24,
|
|
),
|
|
),
|
|
SizedBox(width: 12),
|
|
Text(
|
|
'Identifiants',
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.w600,
|
|
color: textPrimaryColor,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
SizedBox(height: 24),
|
|
|
|
_buildTextField(
|
|
controller: _agentIdController,
|
|
label: 'ID Agent',
|
|
hint: 'Entrez votre identifiant',
|
|
prefixIcon: Icons.badge_outlined,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'L\'ID agent est requis';
|
|
}
|
|
if (value.length < 3) {
|
|
return 'ID agent trop court (min. 3 caractères)';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
|
|
SizedBox(height: 20),
|
|
|
|
_buildTextField(
|
|
controller: _pinController,
|
|
label: 'Code PIN',
|
|
hint: 'Entrez votre code PIN',
|
|
prefixIcon: Icons.lock_outline,
|
|
obscureText: _obscurePin,
|
|
suffixIcon: IconButton(
|
|
icon: Icon(
|
|
_obscurePin
|
|
? Icons.visibility_outlined
|
|
: Icons.visibility_off_outlined,
|
|
color: textSecondaryColor,
|
|
),
|
|
onPressed: () {
|
|
setState(() {
|
|
_obscurePin = !_obscurePin;
|
|
});
|
|
},
|
|
),
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Le code PIN est requis';
|
|
}
|
|
if (value.length < 4) {
|
|
return 'Le PIN doit contenir au moins 4 chiffres';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
|
|
SizedBox(height: 32),
|
|
|
|
Consumer<AuthController>(
|
|
builder: (context, authController, child) {
|
|
return _buildLoginButton(authController);
|
|
},
|
|
),
|
|
|
|
SizedBox(height: 16),
|
|
|
|
Container(
|
|
width: double.infinity,
|
|
child: OutlinedButton.icon(
|
|
onPressed: _openWifiSettings,
|
|
icon: Icon(Icons.wifi_find_rounded, size: 20),
|
|
label: Text('Paramètres WiFi'),
|
|
style: OutlinedButton.styleFrom(
|
|
padding: EdgeInsets.symmetric(vertical: 14),
|
|
side: BorderSide(color: primaryColor, width: 1.5),
|
|
foregroundColor: primaryColor,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Bouton de test (commenté par défaut)
|
|
// if (_apiConnected) ...[
|
|
// SizedBox(height: 16),
|
|
// TextButton.icon(
|
|
// onPressed: _createTestUser,
|
|
// icon: Icon(Icons.add_circle_outline, size: 18),
|
|
// label: Text('Créer utilisateur test'),
|
|
// style: TextButton.styleFrom(foregroundColor: primaryColor),
|
|
// ),
|
|
// ],
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTextField({
|
|
required TextEditingController controller,
|
|
required String label,
|
|
required String hint,
|
|
required IconData prefixIcon,
|
|
Widget? suffixIcon,
|
|
bool obscureText = false,
|
|
String? Function(String?)? validator,
|
|
}) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
label,
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
color: textPrimaryColor,
|
|
),
|
|
),
|
|
SizedBox(height: 8),
|
|
TextFormField(
|
|
controller: controller,
|
|
validator: validator,
|
|
obscureText: obscureText,
|
|
decoration: InputDecoration(
|
|
hintText: hint,
|
|
hintStyle: TextStyle(color: textSecondaryColor),
|
|
prefixIcon: Icon(prefixIcon, color: primaryColor),
|
|
suffixIcon: suffixIcon,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: borderColor),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: borderColor),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: primaryColor, width: 2),
|
|
),
|
|
errorBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: errorColor),
|
|
),
|
|
focusedErrorBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: errorColor, width: 2),
|
|
),
|
|
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
|
filled: true,
|
|
fillColor: backgroundColor.withOpacity(0.3),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildLoginButton(AuthController authController) {
|
|
return Container(
|
|
height: 54,
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [primaryColor, secondaryColor],
|
|
begin: Alignment.centerLeft,
|
|
end: Alignment.centerRight,
|
|
),
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: primaryColor.withOpacity(0.4),
|
|
blurRadius: 10,
|
|
offset: Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: ElevatedButton(
|
|
onPressed: authController.isLoading ? null : _login,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.transparent,
|
|
shadowColor: Colors.transparent,
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
child:
|
|
authController.isLoading
|
|
? SizedBox(
|
|
width: 24,
|
|
height: 24,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 2.5,
|
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
|
),
|
|
)
|
|
: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.login_rounded, size: 22),
|
|
SizedBox(width: 10),
|
|
Text(
|
|
'SE CONNECTER',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
letterSpacing: 1.2,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildHelpSection() {
|
|
return Container(
|
|
padding: EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
secondaryColor.withOpacity(0.1),
|
|
primaryColor.withOpacity(0.05),
|
|
],
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(color: secondaryColor.withOpacity(0.3), width: 1.5),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
padding: EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
shape: BoxShape.circle,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: primaryColor.withOpacity(0.2),
|
|
blurRadius: 10,
|
|
offset: Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Icon(
|
|
Icons.help_outline_rounded,
|
|
color: primaryColor,
|
|
size: 32,
|
|
),
|
|
),
|
|
SizedBox(height: 16),
|
|
Text(
|
|
'Besoin d\'aide ?',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: textPrimaryColor,
|
|
),
|
|
),
|
|
SizedBox(height: 12),
|
|
Container(
|
|
padding: EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.7),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
|
|
// child: Column(
|
|
// children: [
|
|
// Row(
|
|
// children: [
|
|
// Icon(Icons.person, color: primaryColor, size: 16),
|
|
// SizedBox(width: 8),
|
|
// Expanded(
|
|
// child: Text(
|
|
// 'Compte Agent - Accès individuel',
|
|
// style: TextStyle(fontSize: 13, color: textPrimaryColor),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// SizedBox(height: 8),
|
|
// Row(
|
|
// children: [
|
|
// Icon(Icons.business, color: enterpriseColor, size: 16),
|
|
// SizedBox(width: 8),
|
|
// Expanded(
|
|
// child: Text(
|
|
// 'Compte Entreprise - Solde partagé',
|
|
// style: TextStyle(fontSize: 13, color: textPrimaryColor),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ],
|
|
// ),
|
|
),
|
|
SizedBox(height: 16),
|
|
Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
|
decoration: BoxDecoration(
|
|
color: primaryColor,
|
|
borderRadius: BorderRadius.circular(25),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: primaryColor.withOpacity(0.3),
|
|
blurRadius: 8,
|
|
offset: Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.phone, color: Colors.white, size: 18),
|
|
SizedBox(width: 8),
|
|
Text(
|
|
'Support: 50 05',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|