Files
mobile/lib/features/establishments/presentation/establishment_detail_screen.dart

208 lines
6.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../core/router/app_router.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/widgets/empty_state.dart';
import '../../../core/widgets/machine_grid_layout.dart';
import '../../../core/widgets/machine_widgets.dart';
import '../../establishments/data/establishment_repository.dart';
import '../../establishments/domain/establishment.dart';
/// Écran laverie — grille machines.
class EstablishmentDetailScreen extends ConsumerStatefulWidget {
const EstablishmentDetailScreen({
super.key,
required this.establishmentUuid,
});
final String establishmentUuid;
@override
ConsumerState<EstablishmentDetailScreen> createState() => _EstablishmentDetailScreenState();
}
class _EstablishmentDetailScreenState extends ConsumerState<EstablishmentDetailScreen> {
String _filter = 'all';
List<Machine> _filterMachines(List<Machine> machines) => switch (_filter) {
'washer' => machines.where((m) => m.type.startsWith('washer')).toList(),
'dryer' => machines.where((m) => m.type.startsWith('dryer')).toList(),
_ => machines,
};
@override
Widget build(BuildContext context) {
final establishmentAsync =
ref.watch(establishmentDetailProvider(widget.establishmentUuid));
return Scaffold(
backgroundColor: AppColors.background,
appBar: AppBar(
title: establishmentAsync.maybeWhen(
data: (e) => Text(e.name, overflow: TextOverflow.ellipsis),
orElse: () => const Text('Ma laverie'),
),
),
body: establishmentAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => EmptyState(
icon: Icons.error_outline,
title: 'Erreur',
subtitle: '$error',
actionLabel: 'Réessayer',
onAction: () => ref.invalidate(establishmentDetailProvider(widget.establishmentUuid)),
),
data: (establishment) => _EstablishmentBody(
establishment: establishment,
filter: _filter,
onFilterChanged: (value) => setState(() => _filter = value),
onRefresh: () async {
ref.invalidate(establishmentDetailProvider(widget.establishmentUuid));
},
machines: _filterMachines(establishment.machines),
onMachineTap: (machine) => context.push(AppRoutes.machineAction(machine.uuid)),
),
),
);
}
}
class _EstablishmentBody extends StatelessWidget {
const _EstablishmentBody({
required this.establishment,
required this.filter,
required this.onFilterChanged,
required this.onRefresh,
required this.machines,
required this.onMachineTap,
});
final Establishment establishment;
final String filter;
final ValueChanged<String> onFilterChanged;
final Future<void> Function() onRefresh;
final List<Machine> machines;
final ValueChanged<Machine> onMachineTap;
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: onRefresh,
child: ListView(
padding: const EdgeInsets.all(16),
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: AppColors.gradientAccent,
borderRadius: BorderRadius.circular(14),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
establishment.name,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),
Text(
establishment.fullAddress,
style: TextStyle(color: Colors.white.withValues(alpha: 0.9), fontSize: 13),
),
],
),
),
const SizedBox(height: 16),
if (establishment.machines.isNotEmpty) ...[
MachineStatsRow(machines: establishment.machines),
const SizedBox(height: 12),
const MachineStatusLegend(),
const SizedBox(height: 16),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
_FilterChip(
label: 'Toutes',
selected: filter == 'all',
onTap: () => onFilterChanged('all'),
),
const SizedBox(width: 8),
_FilterChip(
label: 'Lave-linge',
selected: filter == 'washer',
onTap: () => onFilterChanged('washer'),
),
const SizedBox(width: 8),
_FilterChip(
label: 'Sèche-linge',
selected: filter == 'dryer',
onTap: () => onFilterChanged('dryer'),
),
],
),
),
const SizedBox(height: 16),
],
if (establishment.machines.isEmpty)
const EmptyState(
icon: Icons.local_laundry_service_outlined,
title: 'Aucune machine',
subtitle: 'Cette laverie n\'a pas encore de machines.',
)
else if (machines.isEmpty)
const EmptyState(
icon: Icons.filter_alt_outlined,
title: 'Aucun résultat',
subtitle: 'Aucune machine dans cette catégorie.',
)
else
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: MachineGridLayout.delegate(context),
itemCount: machines.length,
itemBuilder: (context, index) => MachineGridCard(
machine: machines[index],
onTap: () => onMachineTap(machines[index]),
),
),
],
),
);
}
}
class _FilterChip extends StatelessWidget {
const _FilterChip({
required this.label,
required this.selected,
required this.onTap,
});
final String label;
final bool selected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return FilterChip(
label: Text(label),
selected: selected,
onSelected: (_) => onTap(),
selectedColor: AppColors.primary.withValues(alpha: 0.15),
checkmarkColor: AppColors.primary,
labelStyle: TextStyle(
color: selected ? AppColors.primary : AppColors.textSecondary,
fontWeight: selected ? FontWeight.w600 : FontWeight.normal,
),
);
}
}