Enhance FixedScheduleEditor; implement driver assignment dialog and conflict checking for bulk schedules
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../models/schedule.dart';
|
||||
import '../models/schedule.dart' as schedule;
|
||||
import '../services/schedule_service.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../services/exceptions.dart';
|
||||
@@ -15,7 +15,7 @@ class DailyScheduleScreen extends StatefulWidget {
|
||||
class _DailyScheduleScreenState extends State<DailyScheduleScreen> {
|
||||
final _scheduleService = ScheduleService();
|
||||
final _authService = AuthService();
|
||||
late Future<DailySchedule> _scheduleFuture;
|
||||
late Future<schedule.DailySchedule> _scheduleFuture;
|
||||
DateTime _selectedDate = DateTime.now();
|
||||
|
||||
@override
|
||||
@@ -62,6 +62,16 @@ class _DailyScheduleScreenState extends State<DailyScheduleScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
// Group routes by hour for better visual organization
|
||||
Map<int, List<schedule.Route>> _groupByHour(List<schedule.Route> routes) {
|
||||
final grouped = <int, List<schedule.Route>>{};
|
||||
for (final route in routes) {
|
||||
final hour = route.startTime.hour;
|
||||
grouped.putIfAbsent(hour, () => []).add(route);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
void _moveDay(int deltaDays) {
|
||||
setState(() {
|
||||
_selectedDate = _selectedDate.add(Duration(days: deltaDays));
|
||||
@@ -198,7 +208,7 @@ class _DailyScheduleScreenState extends State<DailyScheduleScreen> {
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: FutureBuilder<DailySchedule>(
|
||||
child: FutureBuilder<schedule.DailySchedule>(
|
||||
future: _scheduleFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
@@ -267,12 +277,12 @@ class _DailyScheduleScreenState extends State<DailyScheduleScreen> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'No routes scheduled',
|
||||
'Geen actieve ritten',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Check back later for your schedule',
|
||||
'Je hebt geen geplande ritten voor deze dag',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -288,25 +298,64 @@ class _DailyScheduleScreenState extends State<DailyScheduleScreen> {
|
||||
..sort((a, b) => a.startTime.compareTo(b.startTime));
|
||||
final scheduledRoutes = routes.where((r) => r.status == 'scheduled').toList();
|
||||
final inProgressRoutes = routes.where((r) => r.status == 'in_progress').toList();
|
||||
final completedRoutes = routes.where((r) => r.status == 'completed').toList();
|
||||
|
||||
// Merge and group by hour
|
||||
final allActiveRoutes = [...inProgressRoutes, ...scheduledRoutes]
|
||||
..sort((a, b) => a.startTime.compareTo(b.startTime));
|
||||
final hourGroups = _groupByHour(allActiveRoutes);
|
||||
final sortedHours = hourGroups.keys.toList()..sort();
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: _refreshSchedule,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
children: [
|
||||
if (inProgressRoutes.isNotEmpty) ...[
|
||||
_sectionHeader('In Progress', colorScheme.tertiary),
|
||||
...inProgressRoutes.map((route) => RouteCard(route: route, onTap: () {})),
|
||||
],
|
||||
if (scheduledRoutes.isNotEmpty) ...[
|
||||
_sectionHeader('Scheduled', colorScheme.primary),
|
||||
...scheduledRoutes.map((route) => RouteCard(route: route, onTap: () {})),
|
||||
],
|
||||
if (completedRoutes.isNotEmpty) ...[
|
||||
_sectionHeader('Completed', colorScheme.secondary),
|
||||
...completedRoutes.map((route) => RouteCard(route: route, onTap: () {})),
|
||||
],
|
||||
if (allActiveRoutes.isEmpty)
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 32),
|
||||
child: Text(
|
||||
'Geen actieve ritten voor deze dag',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
...sortedHours.map((hour) {
|
||||
final routesForHour = hourGroups[hour] ?? [];
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16, top: 16, bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'${hour.toString().padLeft(2, '0')}:00 uur',
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: colorScheme.outlineVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
...routesForHour.map((route) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: RouteCard(route: route, onTap: () {}),
|
||||
)),
|
||||
const SizedBox(height: 4),
|
||||
],
|
||||
);
|
||||
}),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
@@ -319,17 +368,4 @@ class _DailyScheduleScreenState extends State<DailyScheduleScreen> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _sectionHeader(String title, Color color) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16, top: 24, bottom: 8),
|
||||
child: Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,9 @@ class _RecurringSchedulesScreenState extends State<RecurringSchedulesScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
final schedules = snapshot.data ?? [];
|
||||
final allSchedules = snapshot.data ?? [];
|
||||
// Filter to show only truly recurring schedules (with weekdays > 0)
|
||||
final schedules = allSchedules.where((s) => s.weekdays.isNotEmpty).toList();
|
||||
|
||||
if (schedules.isEmpty) {
|
||||
return Center(
|
||||
@@ -165,9 +167,16 @@ class _RecurringSchedulesScreenState extends State<RecurringSchedulesScreen> {
|
||||
Icon(Icons.event_note_outlined, size: 48, color: colorScheme.outline),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Geen vaste diensten gevonden',
|
||||
'Geen herhaalde diensten gevonden',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Herhaalde diensten worden hier weergegeven',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user