Agrego frontend app
This commit is contained in:
+140
@@ -0,0 +1,140 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:gimnasio_soma/features/huerfanas/data/repositories/huerfanas_repository_impl.dart';
|
||||
import 'package:gimnasio_soma/features/huerfanas/domain/entities/reserva_huerfana.dart';
|
||||
import 'package:gimnasio_soma/features/huerfanas/domain/repositories/huerfanas_repository.dart';
|
||||
import 'package:gimnasio_soma/features/turnos/presentation/providers/turnos_provider.dart';
|
||||
|
||||
final huerfanasRepositoryProvider = Provider<HuerfanasRepository>((ref) {
|
||||
return HuerfanasRepositoryImpl();
|
||||
});
|
||||
|
||||
String _errorMessage(Object e) {
|
||||
if (e is PostgrestException) return e.message;
|
||||
return e.toString().replaceFirst('Exception: ', '');
|
||||
}
|
||||
|
||||
final huerfanasProvider =
|
||||
StateNotifierProvider<HuerfanasNotifier, AsyncValue<List<ReservaHuerfana>>>(
|
||||
(ref) {
|
||||
return HuerfanasNotifier(ref, ref.read(huerfanasRepositoryProvider));
|
||||
});
|
||||
|
||||
class HuerfanasNotifier
|
||||
extends StateNotifier<AsyncValue<List<ReservaHuerfana>>> {
|
||||
final Ref _ref;
|
||||
final HuerfanasRepository _repository;
|
||||
String? _currentEstado = 'pendiente';
|
||||
|
||||
HuerfanasNotifier(this._ref, this._repository)
|
||||
: super(const AsyncValue.loading()) {
|
||||
load();
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
final data = await _repository.obtenerHuerfanas(estado: _currentEstado);
|
||||
state = AsyncValue.data(data);
|
||||
} catch (e, st) {
|
||||
state = AsyncValue.error(e, st);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> filtrar(String? estado) async {
|
||||
_currentEstado = estado;
|
||||
await load();
|
||||
}
|
||||
|
||||
String? get currentEstado => _currentEstado;
|
||||
|
||||
/// Retorna null si tuvo éxito, o un mensaje de error.
|
||||
Future<String?> resolver(String huerfanaId, String nuevoEstado) async {
|
||||
try {
|
||||
await _repository.resolverHuerfana(huerfanaId, nuevoEstado);
|
||||
await load();
|
||||
return null;
|
||||
} catch (e) {
|
||||
return _errorMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reserva el turno y marca la huérfana como 'reubicado' atómicamente.
|
||||
/// Retorna null si tuvo éxito, o un mensaje de error.
|
||||
///
|
||||
/// La reubicación ocupa un cupo en [turnoId]. Invalidamos turnosProvider
|
||||
/// para que la pantalla de Turnos no muestre un cupo desactualizado si ya
|
||||
/// tenía esa semana cacheada de antes.
|
||||
Future<String?> mover(String huerfanaId, String turnoId) async {
|
||||
try {
|
||||
await _repository.moverHuerfana(huerfanaId, turnoId);
|
||||
await load();
|
||||
_ref.invalidate(turnosProvider);
|
||||
return null;
|
||||
} catch (e) {
|
||||
return _errorMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Marca todas las huérfanas del conjunto como 'resuelta' (best-effort).
|
||||
Future<void> notificarLote(Iterable<String> ids) async {
|
||||
for (final id in ids) {
|
||||
try {
|
||||
await _repository.resolverHuerfana(id, 'resuelta');
|
||||
} catch (_) {
|
||||
// best-effort: continúa con las demás aunque alguna falle
|
||||
}
|
||||
}
|
||||
await load();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Selección múltiple ────────────────────────────────────────────────────────
|
||||
|
||||
final huerfanasModoSeleccionProvider = StateProvider<bool>((ref) => false);
|
||||
|
||||
class _SeleccionNotifier extends StateNotifier<Set<String>> {
|
||||
_SeleccionNotifier() : super({});
|
||||
|
||||
void toggle(String id) {
|
||||
final next = {...state};
|
||||
if (next.contains(id)) {
|
||||
next.remove(id);
|
||||
} else {
|
||||
next.add(id);
|
||||
}
|
||||
state = next;
|
||||
}
|
||||
|
||||
void limpiar() => state = {};
|
||||
}
|
||||
|
||||
final huerfanasSeleccionProvider =
|
||||
StateNotifierProvider<_SeleccionNotifier, Set<String>>(
|
||||
(ref) => _SeleccionNotifier());
|
||||
|
||||
// ── Badge sidebar ─────────────────────────────────────────────────────────────
|
||||
|
||||
/// Cantidad de reservas huérfanas pendientes — usado para el badge en sidebar.
|
||||
///
|
||||
/// Observa [huerfanasProvider] para recomputarse tras cualquier mutación.
|
||||
/// Si el filtro activo es 'pendiente' o null derivamos el conteo en memoria
|
||||
/// (sin RPC extra). Si el filtro es otro, hacemos una consulta independiente.
|
||||
final huerfanasPendienteCountProvider =
|
||||
FutureProvider.autoDispose<int>((ref) async {
|
||||
final state = ref.watch(huerfanasProvider);
|
||||
final notifier = ref.read(huerfanasProvider.notifier);
|
||||
|
||||
final lista = state.valueOrNull;
|
||||
if (lista != null) {
|
||||
if (notifier.currentEstado == 'pendiente') return lista.length;
|
||||
if (notifier.currentEstado == null) {
|
||||
return lista.where((r) => r.estado == EstadoHuerfana.pendiente).length;
|
||||
}
|
||||
}
|
||||
|
||||
// Filtro activo no es pendiente/todas: hacemos la consulta directa.
|
||||
final repo = ref.read(huerfanasRepositoryProvider);
|
||||
final pendientes = await repo.obtenerHuerfanas(estado: 'pendiente');
|
||||
return pendientes.length;
|
||||
});
|
||||
Reference in New Issue
Block a user