Rejection retry flow with configurable wait days

- Usuario model: proRejectedAt parsed from professionals.updated_at
- _StatusCard: shows countdown or retry button based on rejection_wait_days setting
- Retry clears the rejected record via DELETE /professionals/me

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-06-28 18:03:20 -05:00
co-authored by Claude Sonnet 4.6
parent 9634aa13ff
commit 391c32f765
2 changed files with 110 additions and 52 deletions
+95 -52
View File
@@ -611,7 +611,7 @@ class _UploadTile extends StatelessWidget {
}
}
class _StatusCard extends StatelessWidget {
class _StatusCard extends StatefulWidget {
final Color cardBg;
final Color border;
final Color textPrimary;
@@ -626,26 +626,62 @@ class _StatusCard extends StatelessWidget {
required this.isPending,
});
@override
State<_StatusCard> createState() => _StatusCardState();
}
class _StatusCardState extends State<_StatusCard> {
int _waitDays = 7;
bool _loadingSettings = true;
@override
void initState() {
super.initState();
if (!widget.isPending) _loadWaitDays();
}
Future<void> _loadWaitDays() async {
try {
final data = await ApiService.instance.get('/settings') as Map<String, dynamic>;
final v = data['rejection_wait_days'];
if (v != null) setState(() => _waitDays = (v as num).toInt());
} catch (_) {}
if (mounted) setState(() => _loadingSettings = false);
}
Future<void> _retry() async {
try {
await ApiService.instance.delete('/professionals/me');
} catch (_) {}
if (mounted) await context.read<AuthProvider>().isAuthenticated();
}
@override
Widget build(BuildContext context) {
final accent = isPending
final accent = widget.isPending
? const Color(0xFFF59E0B)
: const Color(0xFFEF4444);
final user = context.watch<AuthProvider>().user;
final rejectedAt = user?.proRejectedAt;
final daysSince = rejectedAt != null
? DateTime.now().difference(rejectedAt).inDays
: _waitDays;
final daysLeft = (_waitDays - daysSince).clamp(0, _waitDays);
final canRetry = !widget.isPending && daysLeft == 0;
return Container(
margin: const EdgeInsets.only(top: 24),
padding: const EdgeInsets.all(32),
decoration: BoxDecoration(
color: cardBg,
color: widget.cardBg,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: border),
border: Border.all(color: widget.border),
),
child: Column(
children: [
if (isPending)
const Image(
image: AssetImage('assets/checklist.gif'),
width: 160)
if (widget.isPending)
const Image(image: AssetImage('assets/checklist.gif'), width: 160)
else
Container(
padding: const EdgeInsets.all(20),
@@ -653,63 +689,70 @@ class _StatusCard extends StatelessWidget {
color: accent.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
isPending
? Icons.hourglass_empty_rounded
: Icons.sentiment_dissatisfied_outlined,
size: 52,
color: accent),
child: Icon(Icons.sentiment_dissatisfied_outlined, size: 52, color: accent),
),
const SizedBox(height: 18),
Text(
isPending
? 'Solicitud en revisión'
: 'Solicitud no aprobada',
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.bold,
color: textPrimary),
widget.isPending ? 'Solicitud en revisión' : 'Solicitud no aprobada',
style: TextStyle(fontSize: 19, fontWeight: FontWeight.bold, color: widget.textPrimary),
),
const SizedBox(height: 10),
Text(
isPending
widget.isPending
? 'Estamos revisando tus datos. Te notificaremos cuando tu cuenta esté aprobada.'
: 'Tu solicitud no fue aprobada. Revisa tus documentos y vuelve a intentarlo.',
: canRetry
? 'Puedes volver a enviar tu solicitud con los documentos corregidos.'
: 'Tu solicitud no fue aprobada. Podrás reintentar en $daysLeft día${daysLeft == 1 ? '' : 's'}.',
textAlign: TextAlign.center,
style:
TextStyle(color: textSec, fontSize: 13, height: 1.5),
style: TextStyle(color: widget.textSec, fontSize: 13, height: 1.5),
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: accent.withOpacity(0.12),
borderRadius: BorderRadius.circular(10),
border:
Border.all(color: accent.withOpacity(0.4)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isPending
? Icons.hourglass_empty_rounded
: Icons.cancel_outlined,
color: accent,
size: 16,
if (!widget.isPending && !_loadingSettings) ...[
if (canRetry)
FilledButton.icon(
onPressed: _retry,
icon: const Icon(Icons.refresh, size: 18),
label: const Text('Volver a solicitar'),
style: FilledButton.styleFrom(backgroundColor: const Color(0xFF42A4EF)),
)
else
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: accent.withOpacity(0.12),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: accent.withOpacity(0.4)),
),
const SizedBox(width: 6),
Text(
isPending ? 'Revisión en proceso' : 'No aprobado',
style: TextStyle(
color: accent,
fontWeight: FontWeight.w600,
fontSize: 13),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.timer_outlined, color: accent, size: 16),
const SizedBox(width: 6),
Text(
'Disponible en $daysLeft día${daysLeft == 1 ? '' : 's'}',
style: TextStyle(color: accent, fontWeight: FontWeight.w600, fontSize: 13),
),
],
),
],
),
] else if (widget.isPending)
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: accent.withOpacity(0.12),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: accent.withOpacity(0.4)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.hourglass_empty_rounded, color: accent, size: 16),
const SizedBox(width: 6),
Text('Revisión en proceso',
style: TextStyle(color: accent, fontWeight: FontWeight.w600, fontSize: 13)),
],
),
),
),
],
),
);