Skip to content

Commit 8e08abf

Browse files
committed
✨ Se añadio la funcion de verificacion por CURP
1 parent 36277ce commit 8e08abf

9 files changed

Lines changed: 207 additions & 42 deletions

File tree

lib/core/widgets/text_input_field.dart

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@ class TextInputField extends StatelessWidget {
1010
final FocusNode? focusNode;
1111
final ValueChanged<String>? onFieldSubmitted;
1212
final bool readOnly;
13-
14-
// NUEVO: Propiedad para el icono del final (ojito)
1513
final Widget? suffixIcon;
16-
1714
final String? Function(String?)? validator;
15+
final TextCapitalization textCapitalization;
1816

1917
const TextInputField({
2018
super.key,
@@ -29,6 +27,7 @@ class TextInputField extends StatelessWidget {
2927
this.readOnly = false,
3028
this.suffixIcon, // Lo recibimos en el constructor
3129
this.validator, // Lo recibimos aquí
30+
this.textCapitalization = TextCapitalization.none,
3231
});
3332

3433
@override
@@ -60,17 +59,15 @@ class TextInputField extends StatelessWidget {
6059
onFieldSubmitted: onFieldSubmitted,
6160
readOnly: readOnly,
6261
validator: validator,
62+
textCapitalization: textCapitalization,
6363
decoration: InputDecoration(
6464
labelText: label,
6565
labelStyle: TextStyle(color: Colors.grey[600]),
66-
prefixIcon: Icon(icon, color: const Color(0xFF2F5A93)), // Color azul de tu tema
67-
68-
// AQUI SE AGREGA EL ICONO DEL OJITO
66+
prefixIcon: Icon(icon, color: const Color(0xFF2F5A93)),
6967
suffixIcon: suffixIcon,
70-
7168
border: OutlineInputBorder(
7269
borderRadius: BorderRadius.circular(14),
73-
borderSide: BorderSide.none, // Sin borde porque usamos el Container para la sombra
70+
borderSide: BorderSide.none,
7471
),
7572
filled: true,
7673
fillColor: Colors.transparent, // Transparente para ver el color del Container

lib/data/models/user_model.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ class UserModel {
33
final String boleta;
44
final String name;
55
final String email;
6-
final String status; // Se mantiene como "estatus general/resumen"
6+
final String status;
77
final String role;
88
final List<String> academies;
99
final String? dictamenUrl;
1010
final double? finalGrade;
1111
final List<String> subjectsToTake;
12-
final Map<String, String> academyStatus; // <-- NUEVO: Estatus por academia
12+
final Map<String, String> academyStatus;
1313

1414
UserModel({
1515
required this.id,

lib/data/repositories/auth_repository.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ class AuthRepository {
4545
}
4646
}
4747

48+
Future<bool> checkCurpExists(String curp) async {
49+
try {
50+
// Buscamos si hay algún usuario con ese CURP
51+
final snapshot = await _db
52+
.collection('users')
53+
.where('curp', isEqualTo: curp)
54+
.limit(1)
55+
.get();
56+
57+
return snapshot.docs.isNotEmpty;
58+
} catch (e) {
59+
throw Exception("Error al verificar CURP: $e");
60+
}
61+
}
62+
4863
Future<void> activateAccount({
4964
required String docId,
5065
required String email,
@@ -54,6 +69,7 @@ class AuthRepository {
5469
required String dictamenFileName,
5570
File? dictamenFileMobile,
5671
Uint8List? dictamenFileWeb,
72+
required String curp,
5773
}) async {
5874
try {
5975
UserCredential cred = await _auth.createUserWithEmailAndPassword(email: email, password: password);
@@ -78,6 +94,7 @@ class AuthRepository {
7894
'email_personal': personalEmail,
7995
'phone': phone,
8096
'dictamen_url': downloadUrl,
97+
'curp': curp,
8198
'status': 'PENDIENTE_ASIGNACION',
8299
'updated_at': FieldValue.serverTimestamp(),
83100
});

lib/features/login/viewmodels/register_viewmodel.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,21 @@ class RegisterViewModel extends ChangeNotifier {
2929
File? _dictamenFileMobile;
3030
Uint8List? _dictamenFileWeb;
3131

32+
String? _curp;
33+
3234
// --- GETTERS ---
3335
int get currentStep => _currentStep;
3436
bool get isLoading => _isLoading;
3537
String? get errorMessage => _errorMessage;
3638
UserModel? get foundStudent => _foundStudent;
3739
String? get dictamenFileName => _dictamenFileName;
3840

41+
// 2. NUEVO: Setter para asignar el CURP desde la vista si fuera necesario
42+
void setCurp(String value) {
43+
_curp = value;
44+
notifyListeners(); // Opcional
45+
}
46+
3947
// --- METHODS ---
4048

4149
// STEP 0: Search for student by boleta
@@ -92,6 +100,13 @@ class RegisterViewModel extends ChangeNotifier {
92100
return false;
93101
}
94102

103+
// Validación extra de seguridad
104+
if (_curp == null || _curp!.isEmpty) {
105+
_errorMessage = "Falta el CURP para completar el registro.";
106+
notifyListeners();
107+
return false;
108+
}
109+
95110
_isLoading = true;
96111
_errorMessage = null;
97112
notifyListeners();
@@ -106,6 +121,7 @@ class RegisterViewModel extends ChangeNotifier {
106121
dictamenFileName: _dictamenFileName!,
107122
dictamenFileMobile: _dictamenFileMobile,
108123
dictamenFileWeb: _dictamenFileWeb,
124+
curp: _curp!,
109125
);
110126
return true;
111127
} catch (e) {
@@ -122,6 +138,7 @@ class RegisterViewModel extends ChangeNotifier {
122138
_foundStudent = null;
123139
_errorMessage = null;
124140
boletaController.clear();
141+
_curp = null;
125142
notifyListeners();
126143
}
127144

lib/features/login/viewmodels/student_lookup_viewmodel.dart

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,23 @@ class StudentLookupViewModel extends ChangeNotifier {
1313

1414
// 1. VARIABLE PARA GUARDAR EL USUARIO ENCONTRADO
1515
UserModel? _foundUser;
16+
bool _isBoletaVerified = false;
17+
1618

17-
// Getters para que la vista consuma los datos
1819
bool get isLoading => _isLoading;
1920
String? get errorMessage => _errorMessage;
20-
21-
// 2. GETTER PÚBLICO
2221
UserModel? get foundUser => _foundUser;
22+
bool get isBoletaVerified => _isBoletaVerified;
23+
24+
final RegExp _curpRegex = RegExp(
25+
r'^[A-Z][AEIOUX][A-Z]{2}\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])[HM](?:AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS|NE)[B-DF-HJ-NP-TV-Z]{3}[0-9A-Z]\d$',
26+
);
27+
28+
bool isCurpValid(String curp) {
29+
return _curpRegex.hasMatch(curp.toUpperCase());
30+
}
2331

32+
// PASO 1: Buscar Boleta
2433
Future<bool> searchStudent(String boleta) async {
2534
if (boleta.isEmpty) {
2635
_errorMessage = "Escribe una boleta";
@@ -31,6 +40,7 @@ class StudentLookupViewModel extends ChangeNotifier {
3140
_isLoading = true;
3241
_errorMessage = null;
3342
_foundUser = null; // Reseteamos búsqueda anterior
43+
_isBoletaVerified = false;
3444
notifyListeners();
3545

3646
try {
@@ -48,6 +58,7 @@ class StudentLookupViewModel extends ChangeNotifier {
4858
} else {
4959
// 3. ¡ÉXITO! GUARDAMOS EL USUARIO
5060
_foundUser = user;
61+
_isBoletaVerified = true;
5162
notifyListeners();
5263
return true;
5364
}
@@ -58,4 +69,45 @@ class StudentLookupViewModel extends ChangeNotifier {
5869
return false;
5970
}
6071
}
72+
73+
Future<bool> validateCurp(String curp) async {
74+
_errorMessage = null;
75+
76+
// 1. Validar Regex
77+
if (curp.isEmpty || !_curpRegex.hasMatch(curp.toUpperCase())) {
78+
_errorMessage = "El formato del CURP es inválido.";
79+
notifyListeners();
80+
return false;
81+
}
82+
83+
_isLoading = true;
84+
notifyListeners();
85+
86+
try {
87+
// 2. Validar duplicado en BD
88+
final bool exists = await _authRepo.checkCurpExists(curp.toUpperCase());
89+
_isLoading = false;
90+
91+
if (exists) {
92+
_errorMessage = "Este CURP ya está registrado en otra cuenta.";
93+
notifyListeners();
94+
return false;
95+
}
96+
97+
return true;
98+
} catch (e) {
99+
_isLoading = false;
100+
_errorMessage = "Error al validar CURP: $e";
101+
notifyListeners();
102+
return false;
103+
}
104+
}
105+
106+
// Método para "resetear" si el usuario se equivocó de boleta
107+
void resetSearch() {
108+
_foundUser = null;
109+
_isBoletaVerified = false;
110+
_errorMessage = null;
111+
notifyListeners();
112+
}
61113
}

lib/features/login/views/register_view.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ class RegisterView extends StatefulWidget {
1414
final String foundName;
1515
final String docId;
1616
final String email;
17+
final String curp;
1718

1819
const RegisterView({
1920
super.key,
2021
required this.boleta,
2122
required this.foundName,
2223
required this.docId,
2324
required this.email,
25+
required this.curp,
2426
});
2527

2628
@override
@@ -120,6 +122,7 @@ class _RegisterViewState extends State<RegisterView> {
120122
dictamenFileName: _fileName!,
121123
dictamenFileMobile: _selectedFileMobile,
122124
dictamenFileWeb: _selectedFileWeb,
125+
curp: widget.curp,
123126
);
124127

125128
if (!mounted) return;

0 commit comments

Comments
 (0)