Resolve merge conflicts: README + ignore IDE files
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import '../app_config.dart';
|
||||
import '../models/demo_profile.dart';
|
||||
import '../models/auth_session.dart';
|
||||
import '../services/address_repository.dart';
|
||||
import '../services/auth_repository.dart';
|
||||
import '../services/local_seed_repository.dart';
|
||||
import 'address_screen.dart';
|
||||
|
||||
final RegExp _lettersOnly = RegExp(r"[a-zA-ZáéíóúÁÉÍÓÚñÑüÜ\s]");
|
||||
final RegExp _addressText = RegExp(r"[a-zA-Z0-9áéíóúÁÉÍÓÚñÑüÜ#\-\s]");
|
||||
final RegExp _emailChars = RegExp(r"[a-zA-Z0-9@._+\-]");
|
||||
|
||||
class AuthScreen extends StatefulWidget {
|
||||
const AuthScreen({
|
||||
super.key,
|
||||
@@ -34,6 +40,14 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
|
||||
bool _isLoading = false;
|
||||
String? _errorMessage;
|
||||
LocalSeedData? _seedData;
|
||||
bool _loadingSeedData = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadSeedData();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -46,8 +60,42 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _loadSeedData() async {
|
||||
final seedData = await LocalSeedRepository.instance.load();
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_seedData = seedData;
|
||||
_loadingSeedData = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _fillDemoProfile(DemoProfile profile) {
|
||||
_loginEmailController.text = profile.email;
|
||||
_loginPasswordController.text = profile.password;
|
||||
_registerNameController.text = profile.name;
|
||||
_registerEmailController.text = profile.email;
|
||||
_registerPasswordController.text = profile.password;
|
||||
_registerConfirmPasswordController.text = profile.password;
|
||||
}
|
||||
|
||||
Future<void> _useDemoProfile(DemoProfile profile) async {
|
||||
_fillDemoProfile(profile);
|
||||
await _submit(() {
|
||||
return widget.authRepository.signIn(
|
||||
email: profile.email,
|
||||
password: profile.password,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _signIn() async {
|
||||
if (!(_loginFormKey.currentState?.validate() ?? false)) {
|
||||
setState(() {
|
||||
_errorMessage = 'Respete los campos';
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -61,6 +109,9 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
|
||||
Future<void> _signUp() async {
|
||||
if (!(_registerFormKey.currentState?.validate() ?? false)) {
|
||||
setState(() {
|
||||
_errorMessage = 'Respete los campos';
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,7 +157,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_errorMessage = 'No se pudo completar la operación. Verifica el backend y vuelve a intentar.';
|
||||
_errorMessage = 'No se pudo completar la operación. Revisa los datos locales y vuelve a intentar.';
|
||||
});
|
||||
} finally {
|
||||
if (mounted) {
|
||||
@@ -167,6 +218,13 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
style: TextStyle(color: Colors.grey.shade700, height: 1.4),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
if (!_loadingSeedData && _seedData != null && _seedData!.demoProfiles.isNotEmpty) ...[
|
||||
_DemoProfilesSection(
|
||||
profiles: _seedData!.demoProfiles,
|
||||
onProfileSelected: _useDemoProfile,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F5F9),
|
||||
@@ -221,11 +279,6 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Base URL configurada: ${AppConfig.apiBaseUrl}',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey.shade600),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -264,6 +317,7 @@ class _LoginForm extends StatelessWidget {
|
||||
TextFormField(
|
||||
controller: emailController,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
inputFormatters: [FilteringTextInputFormatter.allow(_emailChars)],
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Correo electrónico',
|
||||
prefixIcon: Icon(Icons.email_outlined),
|
||||
@@ -273,7 +327,7 @@ class _LoginForm extends StatelessWidget {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return 'Ingresa tu correo';
|
||||
}
|
||||
if (!value.contains('@')) {
|
||||
if (!value.contains('@') || value.startsWith('@') || value.endsWith('@')) {
|
||||
return 'Ingresa un correo válido';
|
||||
}
|
||||
return null;
|
||||
@@ -347,6 +401,8 @@ class _RegisterForm extends StatelessWidget {
|
||||
TextFormField(
|
||||
controller: nameController,
|
||||
textCapitalization: TextCapitalization.words,
|
||||
keyboardType: TextInputType.name,
|
||||
inputFormatters: [FilteringTextInputFormatter.allow(_lettersOnly)],
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Nombre',
|
||||
prefixIcon: Icon(Icons.person_outline),
|
||||
@@ -363,6 +419,7 @@ class _RegisterForm extends StatelessWidget {
|
||||
TextFormField(
|
||||
controller: emailController,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
inputFormatters: [FilteringTextInputFormatter.allow(_emailChars)],
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Correo electrónico',
|
||||
prefixIcon: Icon(Icons.email_outlined),
|
||||
@@ -372,7 +429,7 @@ class _RegisterForm extends StatelessWidget {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return 'Ingresa tu correo';
|
||||
}
|
||||
if (!value.contains('@')) {
|
||||
if (!value.contains('@') || value.startsWith('@') || value.endsWith('@')) {
|
||||
return 'Ingresa un correo válido';
|
||||
}
|
||||
return null;
|
||||
@@ -382,6 +439,7 @@ class _RegisterForm extends StatelessWidget {
|
||||
TextFormField(
|
||||
controller: passwordController,
|
||||
obscureText: true,
|
||||
inputFormatters: [FilteringTextInputFormatter.deny(RegExp(r'\s'))],
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Contraseña',
|
||||
prefixIcon: Icon(Icons.lock_outline),
|
||||
@@ -401,6 +459,7 @@ class _RegisterForm extends StatelessWidget {
|
||||
TextFormField(
|
||||
controller: confirmPasswordController,
|
||||
obscureText: true,
|
||||
inputFormatters: [FilteringTextInputFormatter.deny(RegExp(r'\s'))],
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Confirmar contraseña',
|
||||
prefixIcon: Icon(Icons.lock_reset_outlined),
|
||||
@@ -458,4 +517,47 @@ class _AuthStatusBanner extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DemoProfilesSection extends StatelessWidget {
|
||||
const _DemoProfilesSection({
|
||||
required this.profiles,
|
||||
required this.onProfileSelected,
|
||||
});
|
||||
|
||||
final List<DemoProfile> profiles;
|
||||
final Future<void> Function(DemoProfile profile) onProfileSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
color: const Color(0xFFF8FAFC),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Perfiles demo', style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w800)),
|
||||
const SizedBox(height: 8),
|
||||
Text('Toca un perfil para llenar el formulario de acceso.', style: TextStyle(color: Colors.grey.shade700)),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: profiles
|
||||
.map(
|
||||
(profile) => ActionChip(
|
||||
label: Text('${profile.name} • ${profile.routeId}'),
|
||||
onPressed: () => onProfileSelected(profile),
|
||||
),
|
||||
)
|
||||
.toList(growable: false),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user