Files
2026-07-09 22:57:07 +02:00

174 lines
6.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../core/auth/auth_provider.dart';
import '../../../core/router/app_router.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/widgets/empty_state.dart';
import '../../../core/widgets/machine_widgets.dart';
import '../../establishments/data/establishment_repository.dart';
import '../../establishments/domain/establishment.dart';
import '../../wash/data/wash_repository.dart';
/// Tableau de bord — vue d'ensemble inspirée WashOnline, plus moderne.
class HomeScreen extends ConsumerWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final establishmentsAsync = ref.watch(establishmentsProvider);
final washesAsync = ref.watch(washesProvider);
final user = ref.watch(authProvider).user;
return establishmentsAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => EmptyState(
icon: Icons.cloud_off_outlined,
title: 'Connexion impossible',
subtitle: 'Vérifiez que l\'API est démarrée\net que vous êtes sur le même réseau.',
actionLabel: 'Réessayer',
onAction: () => ref.invalidate(establishmentsProvider),
),
data: (establishments) {
if (establishments.isEmpty) {
return const EmptyState(
icon: Icons.storefront_outlined,
title: 'Aucune laverie',
subtitle: 'Aucun établissement disponible pour le moment.',
);
}
final activeWash = washesAsync.maybeWhen(
data: (washes) {
for (final wash in washes) {
if (wash.status == 'running' || wash.status == 'pending_start' || wash.status == 'active') {
return wash;
}
}
return null;
},
orElse: () => null,
);
return RefreshIndicator(
onRefresh: () async {
ref.invalidate(establishmentsProvider);
ref.invalidate(washesProvider);
},
child: ListView(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
children: [
Text(
user?.firstName != null ? 'Bonjour ${user!.firstName}' : 'Bienvenue',
style: Theme.of(context).textTheme.headlineMedium?.copyWith(fontSize: 20),
),
const SizedBox(height: 16),
const QuickActionsRow(),
if (activeWash != null) ...[
const SizedBox(height: 16),
ActiveWashBanner(
machineName: activeWash.machineName,
onTap: () => context.go(AppRoutes.washes),
),
],
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Mes laveries', style: Theme.of(context).textTheme.titleMedium),
Text(
'${establishments.length}',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: AppColors.primary,
fontWeight: FontWeight.w600,
),
),
],
),
const SizedBox(height: 10),
...establishments.map(
(establishment) => Padding(
padding: const EdgeInsets.only(bottom: 10),
child: _EstablishmentCard(
establishment: establishment,
onTap: () => context.push('/establishments/${establishment.uuid}'),
),
),
),
],
),
);
},
);
}
}
class _EstablishmentCard extends StatelessWidget {
const _EstablishmentCard({
required this.establishment,
required this.onTap,
});
final Establishment establishment;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return Card(
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: AppColors.gradientPrimary,
borderRadius: BorderRadius.circular(12),
),
child: const Icon(Icons.storefront_rounded, color: Colors.white),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(establishment.name, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 2),
Text(
establishment.fullAddress,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 8),
Row(
children: [
Icon(Icons.grid_view_rounded, size: 14, color: AppColors.primary.withValues(alpha: 0.8)),
const SizedBox(width: 4),
const Text(
'Voir les machines',
style: TextStyle(
color: AppColors.primary,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
),
const Icon(Icons.chevron_right, color: AppColors.textSecondary),
],
),
),
),
);
}
}