initial commit
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
/// Modèle réservation de créneau machine.
|
||||
class Booking {
|
||||
const Booking({
|
||||
required this.uuid,
|
||||
required this.machineUuid,
|
||||
required this.machineName,
|
||||
required this.slotStart,
|
||||
required this.slotEnd,
|
||||
required this.status,
|
||||
this.bookingFee = 0,
|
||||
});
|
||||
|
||||
final String uuid;
|
||||
final String machineUuid;
|
||||
final String machineName;
|
||||
final DateTime? slotStart;
|
||||
final DateTime? slotEnd;
|
||||
final String status;
|
||||
final double bookingFee;
|
||||
|
||||
factory Booking.fromJson(Map<String, dynamic> json) {
|
||||
final machine = json['machine'] as Map<String, dynamic>?;
|
||||
|
||||
return Booking(
|
||||
uuid: json['uuid'] as String,
|
||||
machineUuid: machine?['uuid'] as String? ?? json['machine_uuid'] as String? ?? '',
|
||||
machineName: machine?['name'] as String? ?? 'Machine',
|
||||
slotStart: json['slot_start'] != null
|
||||
? DateTime.tryParse(json['slot_start'] as String)
|
||||
: null,
|
||||
slotEnd: json['slot_end'] != null
|
||||
? DateTime.tryParse(json['slot_end'] as String)
|
||||
: null,
|
||||
status: json['status'] as String? ?? 'pending',
|
||||
bookingFee: (json['booking_fee'] as num?)?.toDouble() ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../core/api/api_client.dart';
|
||||
import '../../../core/api/api_endpoints.dart';
|
||||
import '../domain/booking.dart';
|
||||
|
||||
/// Fournisseur des réservations de l'utilisateur connecté.
|
||||
final bookingsProvider = FutureProvider<List<Booking>>((ref) async {
|
||||
final response = await ref.watch(apiClientProvider).get(ApiEndpoints.bookings);
|
||||
final data = response.data;
|
||||
|
||||
List<dynamic> list;
|
||||
if (data is List<dynamic>) {
|
||||
list = data;
|
||||
} else if (data is Map<String, dynamic> && data['data'] is List<dynamic>) {
|
||||
list = data['data'] as List<dynamic>;
|
||||
} else {
|
||||
list = [];
|
||||
}
|
||||
|
||||
return list
|
||||
.map((json) => Booking.fromJson(json as Map<String, dynamic>))
|
||||
.toList();
|
||||
});
|
||||
|
||||
/// Écran listant les réservations de l'utilisateur.
|
||||
class BookingsScreen extends ConsumerWidget {
|
||||
const BookingsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final bookingsAsync = ref.watch(bookingsProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Mes réservations')),
|
||||
body: bookingsAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (error, _) => Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('Erreur : $error'),
|
||||
const SizedBox(height: 12),
|
||||
ElevatedButton(
|
||||
onPressed: () => ref.invalidate(bookingsProvider),
|
||||
child: const Text('Réessayer'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
data: (bookings) {
|
||||
if (bookings.isEmpty) {
|
||||
return const Center(
|
||||
child: Text('Aucune réservation.\nRéservez un créneau depuis une laverie.'),
|
||||
);
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async => ref.invalidate(bookingsProvider),
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: bookings.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 8),
|
||||
itemBuilder: (context, index) {
|
||||
final booking = bookings[index];
|
||||
return Card(
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.event),
|
||||
title: Text(booking.machineName),
|
||||
subtitle: Text(
|
||||
booking.slotStart != null
|
||||
? DateFormat('dd/MM/yyyy HH:mm').format(booking.slotStart!)
|
||||
: 'Créneau à confirmer',
|
||||
),
|
||||
trailing: Chip(label: Text(booking.status)),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user