Files
packet/lib/presentation/pages/pendientes_page.dart

224 lines
10 KiB
Dart
Raw Normal View History

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import '../bloc/pendientes/pendientes_cubit.dart';
import '../bloc/app/app_cubit.dart';
import '../bloc/app/app_state.dart';
import '../../core/utils/hash_utils.dart';
import '../../core/utils/retry_utils.dart';
class PendientesPage extends StatelessWidget {
const PendientesPage({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<PendientesCubit, PendientesState>(
builder: (context, state) {
if (state.isLoading) {
return const Center(child: CircularProgressIndicator());
}
return Scaffold(
body: Column(
children: [
if (state.queueFull)
Container(
width: double.infinity,
color: Theme.of(context).colorScheme.errorContainer,
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.warning,
color: Theme.of(context).colorScheme.error),
const SizedBox(width: 12),
Expanded(
child: Text(
'Cola llena (20/20). Libera espacio para continuar.',
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
),
),
],
),
),
Expanded(
child: state.pendientes.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.check_circle_outline,
size: 64, color: Colors.green.shade400),
const SizedBox(height: 16),
Text(
'Sin pendientes',
style: TextStyle(color: Colors.grey.shade600),
),
],
),
)
: RefreshIndicator(
onRefresh: () => context.read<PendientesCubit>().load(),
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: state.pendientes.length,
itemBuilder: (context, index) {
final pendiente = state.pendientes[index];
final dateFormat = DateFormat('HH:mm dd/MM');
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
pendiente.puedeReintentar
? Icons.schedule
: Icons.error,
color: pendiente.puedeReintentar
? Colors.orange
: Colors.red,
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
pendiente.titulo ?? 'Sin título',
style: const TextStyle(
fontWeight: FontWeight.bold),
),
Text(
HashUtils.truncateHash(
pendiente.hash),
style: TextStyle(
fontFamily: 'monospace',
fontSize: 12,
color: Colors.grey.shade600,
),
),
],
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
_InfoChip(
icon: Icons.repeat,
label: '${pendiente.intentos}/20',
),
const SizedBox(width: 8),
if (pendiente.ultimoIntento != null)
_InfoChip(
icon: Icons.access_time,
label: dateFormat
.format(pendiente.ultimoIntento!),
),
const SizedBox(width: 8),
if (pendiente.proximoIntento != null)
_InfoChip(
icon: Icons.timer,
label: RetryUtils.formatTimeRemaining(
pendiente.proximoIntento!),
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton.icon(
onPressed: () => context
.read<PendientesCubit>()
.eliminar(pendiente.hash),
icon: const Icon(Icons.download),
label: const Text('Recuperar'),
),
const SizedBox(width: 8),
BlocBuilder<AppCubit, AppState>(
builder: (context, appState) {
return FilledButton.icon(
onPressed: pendiente.puedeReintentar &&
appState.destinoActivo !=
null
? () => context
.read<PendientesCubit>()
.reintentar(
pendiente,
appState.destinoActivo!,
)
: null,
icon: const Icon(Icons.replay),
label: const Text('Reintentar'),
);
},
),
],
),
],
),
),
);
},
),
),
),
],
),
floatingActionButton: state.pendientes.isNotEmpty
? BlocBuilder<AppCubit, AppState>(
builder: (context, appState) {
return FloatingActionButton.extended(
onPressed: appState.destinoActivo != null
? () => context
.read<PendientesCubit>()
.reintentarTodos(appState.destinoActivo!)
: null,
icon: const Icon(Icons.replay),
label: const Text('Reintentar todos'),
);
},
)
: null,
);
},
);
}
}
class _InfoChip extends StatelessWidget {
final IconData icon;
final String label;
const _InfoChip({required this.icon, required this.label});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 14, color: Colors.grey.shade600),
const SizedBox(width: 4),
Text(
label,
style: TextStyle(fontSize: 12, color: Colors.grey.shade600),
),
],
),
);
}
}