From 3415cfa2b9255c4f9fd7b143abf434c11ab9d147 Mon Sep 17 00:00:00 2001 From: Sven laptop Date: Fri, 8 May 2026 19:04:09 +0200 Subject: [PATCH] Enhance Docker build workflow by adding path filters for specific APIs and workflow file --- .github/workflows/docker-build.yml | 7 +- admin_ui/src/components/ScheduleMaker.jsx | 21 +- bus_terminal/lib/theme/app_theme.dart | 2 + .../lib/widgets/custom_input_field.dart | 9 +- bus_terminal/lib/widgets/route_card.dart | 292 +++++++++--------- 5 files changed, 179 insertions(+), 152 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 8c1296c..b903d29 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -5,11 +5,16 @@ on: branches: - main - develop + paths: + - 'auth_api/**' + - 'employee_api/**' + - 'transit_api/**' + - 'admin_ui/**' + - '.github/workflows/docker-build.yml' pull_request: branches: - main - develop - workflow_dispatch: env: # GitHub Container Registry diff --git a/admin_ui/src/components/ScheduleMaker.jsx b/admin_ui/src/components/ScheduleMaker.jsx index 06c0a81..0afdc47 100644 --- a/admin_ui/src/components/ScheduleMaker.jsx +++ b/admin_ui/src/components/ScheduleMaker.jsx @@ -21,6 +21,7 @@ export default function ScheduleMaker({ token, user }) { const [selectedLine, setSelectedLine] = useState('') const [selectedBus, setSelectedBus] = useState('') const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]) + const [overviewDate, setOverviewDate] = useState(new Date().toISOString().split('T')[0]) const [startTime, setStartTime] = useState('08:00') const [driverId, setDriverId] = useState('') const [selectedWeekdays, setSelectedWeekdays] = useState([]) @@ -226,7 +227,15 @@ export default function ScheduleMaker({ token, user }) { const filteredSchedules = selectedDriverFilter ? schedules.filter(s => String(s.driverId) === String(selectedDriverFilter)) : schedules - const schedulesBySlot = groupSchedulesByTimeSlot(filteredSchedules, timeSlots) + const overviewSchedules = filteredSchedules.filter(schedule => { + if (!overviewDate) return true + const scheduleDate = new Date(schedule.startTime) + const selectedOverview = new Date(`${overviewDate}T00:00:00`) + return scheduleDate.getFullYear() === selectedOverview.getFullYear() + && scheduleDate.getMonth() === selectedOverview.getMonth() + && scheduleDate.getDate() === selectedOverview.getDate() + }) + const schedulesBySlot = groupSchedulesByTimeSlot(overviewSchedules, timeSlots) return (
@@ -326,6 +335,16 @@ export default function ScheduleMaker({ token, user }) { )}

Timetable

+
+ setOverviewDate(e.target.value)} + /> + +
diff --git a/bus_terminal/lib/theme/app_theme.dart b/bus_terminal/lib/theme/app_theme.dart index b69b45f..45b3e88 100644 --- a/bus_terminal/lib/theme/app_theme.dart +++ b/bus_terminal/lib/theme/app_theme.dart @@ -67,6 +67,7 @@ class AppTheme { brightness: Brightness.light, colorScheme: colorScheme, scaffoldBackgroundColor: const Color(0xFFF1F5F9), + materialTapTargetSize: MaterialTapTargetSize.padded, textTheme: _textTheme(colorScheme), appBarTheme: AppBarTheme( backgroundColor: colorScheme.surface, @@ -138,6 +139,7 @@ class AppTheme { brightness: Brightness.dark, colorScheme: colorScheme, scaffoldBackgroundColor: const Color(0xFF0B1220), + materialTapTargetSize: MaterialTapTargetSize.padded, textTheme: _textTheme(colorScheme).apply(bodyColor: Colors.white, displayColor: Colors.white), appBarTheme: AppBarTheme( backgroundColor: const Color(0xFF0B1220), diff --git a/bus_terminal/lib/widgets/custom_input_field.dart b/bus_terminal/lib/widgets/custom_input_field.dart index 8ba4532..d1ec8af 100644 --- a/bus_terminal/lib/widgets/custom_input_field.dart +++ b/bus_terminal/lib/widgets/custom_input_field.dart @@ -82,15 +82,18 @@ class _CustomInputFieldState extends State { borderSide: BorderSide(color: colorScheme.primary, width: 2), ), suffixIcon: widget.obscureText - ? GestureDetector( - onTap: () { + ? IconButton( + tooltip: _obscureText ? 'Toon wachtwoord' : 'Verberg wachtwoord', + onPressed: () { setState(() => _obscureText = !_obscureText); }, - child: Icon( + icon: Icon( _obscureText ? Icons.visibility_off : Icons.visibility, size: 28, // Larger icon for accessibility color: colorScheme.primary, ), + splashRadius: 24, + constraints: const BoxConstraints(minWidth: 48, minHeight: 48), ) : null, ), diff --git a/bus_terminal/lib/widgets/route_card.dart b/bus_terminal/lib/widgets/route_card.dart index d82f9da..24232aa 100644 --- a/bus_terminal/lib/widgets/route_card.dart +++ b/bus_terminal/lib/widgets/route_card.dart @@ -53,175 +53,173 @@ class RouteCard extends StatelessWidget { final statusColor = _getStatusColor(route.status); final startTimeStr = _formatTime(route.startTime); - return GestureDetector( - onTap: () { - // Navigate to navigation screen - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => NavigationScreen(route: route), - ), - ); - }, - child: Card( - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - elevation: 0, - color: colorScheme.surface, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Header: Line number + Status - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // Line number - Large and prominent - Container( - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [colorScheme.primary, colorScheme.tertiary], - begin: Alignment.topLeft, - end: Alignment.bottomRight, + return Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => NavigationScreen(route: route), + ), + ); + }, + borderRadius: BorderRadius.circular(20), + splashFactory: InkSparkle.splashFactory, + child: Card( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + elevation: 0, + color: colorScheme.surface, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header: Line number + Status + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Line number - Large and prominent + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [colorScheme.primary, colorScheme.tertiary], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(8), ), - borderRadius: BorderRadius.circular(8), - ), - child: Text( - 'Line ${route.busLine.lineNumber}', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - ), - // Status badge - Container( - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - color: statusColor.withOpacity(0.14), - border: Border.all(color: statusColor), - borderRadius: BorderRadius.circular(20), - ), - child: Text( - _getStatusLabel(route.status), - style: TextStyle( - color: statusColor, - fontWeight: FontWeight.w600, - fontSize: 12, + child: Text( + 'Line ${route.busLine.lineNumber}', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 18, + ), ), ), - ), - ], - ), - const SizedBox(height: 16), + // Status badge + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: statusColor.withOpacity(0.14), + border: Border.all(color: statusColor), + borderRadius: BorderRadius.circular(20), + ), + child: Text( + _getStatusLabel(route.status), + style: TextStyle( + color: statusColor, + fontWeight: FontWeight.w600, + fontSize: 12, + ), + ), + ), + ], + ), + const SizedBox(height: 16), - // Route info: Start -> End - Row( - children: [ - Expanded( - child: Column( + // Route info: Start -> End + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'From', + style: Theme.of(context).textTheme.labelSmall, + ), + Text( + route.busLine.startStop, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + Icon( + Icons.arrow_forward, + color: colorScheme.outline, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + 'To', + style: Theme.of(context).textTheme.labelSmall, + ), + Text( + route.busLine.endStop, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 16), + + // Bus info and time + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Bus type + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'From', + 'Bus', style: Theme.of(context).textTheme.labelSmall, ), Text( - route.busLine.startStop, - style: - Theme.of(context).textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + '${route.busType.name} (${route.busType.seatCapacity} seats)', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 4), + Text( + route.busType.licensePlate, + style: Theme.of(context).textTheme.labelMedium?.copyWith( + color: colorScheme.onSurfaceVariant, + ), ), ], ), - ), - Icon( - Icons.arrow_forward, - color: colorScheme.outline, - ), - Expanded( - child: Column( + // Start time + Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - 'To', + 'Start Time', style: Theme.of(context).textTheme.labelSmall, ), Text( - route.busLine.endStop, - style: - Theme.of(context).textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + startTimeStr, + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + color: colorScheme.primary, + ), + ), + const SizedBox(height: 4), + Text( + '${route.busLine.estimatedDuration} min duration', + style: Theme.of(context).textTheme.labelMedium?.copyWith( + color: colorScheme.onSurfaceVariant, + ), ), ], ), - ), - ], - ), - const SizedBox(height: 16), - - // Bus info and time - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // Bus type - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Bus', - style: Theme.of(context).textTheme.labelSmall, - ), - Text( - '${route.busType.name} (${route.busType.seatCapacity} seats)', - style: - Theme.of(context).textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - const SizedBox(height: 4), - Text( - route.busType.licensePlate, - style: Theme.of(context).textTheme.labelMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ], - ), - // Start time - Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - 'Start Time', - style: Theme.of(context).textTheme.labelSmall, - ), - Text( - startTimeStr, - style: - Theme.of(context).textTheme.headlineSmall?.copyWith( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), - ), - const SizedBox(height: 4), - Text( - '${route.busLine.estimatedDuration} min duration', - style: Theme.of(context).textTheme.labelMedium?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ], - ), - ], + ], + ), + ], + ), ), ), ),