Lieferstationen-Dialog (Backend/Vaadin): - Aufgaben per Drag & Drop neu anordnen, inkl. Drag-Handle, komprimierter Kachelansicht während des Drags und horizontaler Einfügelinie als Drop-Target - Drop-Indikator wird unterdrückt, wenn der Drop keine Positionsänderung bewirken würde, und nach dem Abschluss clientseitig zuverlässig aufgeräumt - Drag-Handle, Aufgabentyp-Label und Close-Button auf einheitlicher Position ausgerichtet; Abstände in der Kachel komprimiert Station-Abschluss-Flow (Flutter-App + Backend): - Neuer Button "Station abschließen" unter den Aufgaben; deaktiviert, solange Pflichtaufgaben offen sind, ansonsten aktiv (auch wenn nur optionale Aufgaben existieren) - Hinweisdialog nach Erledigung der letzten Pflichtaufgabe sowie Warnung bei offenen optionalen Aufgaben vor dem Senden - Neue station_completed-Nachricht (jobId, jobNumber, stationOrder, completedAt, hasIncompleteOptionalTasks) wird an den Server gesendet - Backend: Auftrag wird nicht mehr automatisch beim Erledigen der letzten Pflichtaufgabe abgeschlossen, sondern erst beim Empfang der station_completed-Nachricht (neuer Handler in MessageController und MessagingConfig) Aufgabenliste in der App: - Farbcodierung optionaler Aufgaben entfernt; stattdessen vertikal zentrierter "Optional"-Chip am rechten Kartenrand Weitere UI-Überarbeitungen über Login, Jobs, Chats, Settings, Aufgaben-Capture- Screens, Offline-Banner und zugehörige Widgets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
176 lines
6.3 KiB
Dart
176 lines
6.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
class AppColors {
|
|
static const Color primary = Color(0xFF2563EB);
|
|
static const Color primaryStrong = Color(0xFF1D4ED8);
|
|
static const Color primarySoft = Color(0xFFE8F0FF);
|
|
static const Color secondary = Color(0xFF0F4C5C);
|
|
static const Color secondarySoft = Color(0xFFDDEEF2);
|
|
static const Color success = Color(0xFF059669);
|
|
static const Color successSoft = Color(0xFFE7F6F1);
|
|
static const Color warning = Color(0xFFD97706);
|
|
static const Color warningSoft = Color(0xFFFFF4E5);
|
|
static const Color danger = Color(0xFFDC2626);
|
|
static const Color dangerSoft = Color(0xFFFDECEC);
|
|
static const Color surface = Color(0xFFFFFFFF);
|
|
static const Color surfaceMuted = Color(0xFFF7FAFF);
|
|
static const Color scaffold = Color(0xFFF5F7FB);
|
|
static const Color scaffoldAccent = Color(0xFFEEF4FF);
|
|
static const Color border = Color(0xFFD6DDE7);
|
|
static const Color borderStrong = Color(0xFFC6D0DD);
|
|
static const Color text = Color(0xFF1E293B);
|
|
static const Color textStrong = Color(0xFF0F172A);
|
|
static const Color textMuted = Color(0xFF64748B);
|
|
}
|
|
|
|
class AppGradients {
|
|
static const LinearGradient shellBackground = LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
AppColors.scaffoldAccent,
|
|
AppColors.surfaceMuted,
|
|
AppColors.scaffold,
|
|
],
|
|
stops: [0, 0.45, 1],
|
|
);
|
|
}
|
|
|
|
ThemeData buildAppTheme() {
|
|
final colorScheme = ColorScheme.fromSeed(
|
|
seedColor: AppColors.primary,
|
|
brightness: Brightness.light,
|
|
).copyWith(
|
|
primary: AppColors.primary,
|
|
onPrimary: Colors.white,
|
|
primaryContainer: AppColors.primarySoft,
|
|
onPrimaryContainer: AppColors.primaryStrong,
|
|
secondary: AppColors.secondary,
|
|
onSecondary: Colors.white,
|
|
secondaryContainer: AppColors.secondarySoft,
|
|
onSecondaryContainer: AppColors.secondary,
|
|
tertiary: AppColors.success,
|
|
onTertiary: Colors.white,
|
|
tertiaryContainer: AppColors.successSoft,
|
|
onTertiaryContainer: AppColors.success,
|
|
surface: AppColors.surface,
|
|
onSurface: AppColors.textStrong,
|
|
onSurfaceVariant: AppColors.textMuted,
|
|
outline: AppColors.border,
|
|
surfaceTint: Colors.transparent,
|
|
error: AppColors.danger,
|
|
onError: Colors.white,
|
|
);
|
|
final baseTheme = ThemeData(useMaterial3: true, colorScheme: colorScheme);
|
|
const radius = Radius.circular(14);
|
|
final border = OutlineInputBorder(
|
|
borderRadius: const BorderRadius.all(radius),
|
|
borderSide: const BorderSide(color: AppColors.border),
|
|
);
|
|
|
|
return baseTheme.copyWith(
|
|
scaffoldBackgroundColor: AppColors.scaffold,
|
|
canvasColor: AppColors.scaffold,
|
|
textTheme: baseTheme.textTheme
|
|
.apply(bodyColor: AppColors.text, displayColor: AppColors.textStrong)
|
|
.copyWith(
|
|
headlineMedium: baseTheme.textTheme.headlineMedium?.copyWith(
|
|
fontWeight: FontWeight.w700,
|
|
color: AppColors.textStrong,
|
|
),
|
|
titleLarge: baseTheme.textTheme.titleLarge?.copyWith(
|
|
fontWeight: FontWeight.w700,
|
|
color: AppColors.textStrong,
|
|
),
|
|
bodyLarge: baseTheme.textTheme.bodyLarge?.copyWith(
|
|
color: AppColors.text,
|
|
),
|
|
bodyMedium: baseTheme.textTheme.bodyMedium?.copyWith(
|
|
color: AppColors.text,
|
|
),
|
|
),
|
|
appBarTheme: const AppBarTheme(
|
|
backgroundColor: AppColors.primarySoft,
|
|
foregroundColor: AppColors.primaryStrong,
|
|
surfaceTintColor: Colors.transparent,
|
|
elevation: 0,
|
|
),
|
|
cardTheme: const CardThemeData(
|
|
color: AppColors.surface,
|
|
surfaceTintColor: Colors.transparent,
|
|
elevation: 0,
|
|
margin: EdgeInsets.zero,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(18)),
|
|
side: BorderSide(color: AppColors.border),
|
|
),
|
|
),
|
|
dividerTheme: const DividerThemeData(
|
|
color: AppColors.border,
|
|
space: 1,
|
|
thickness: 1,
|
|
),
|
|
inputDecorationTheme: InputDecorationTheme(
|
|
filled: true,
|
|
fillColor: AppColors.surface,
|
|
labelStyle: const TextStyle(color: AppColors.textMuted),
|
|
hintStyle: const TextStyle(color: AppColors.textMuted),
|
|
prefixIconColor: AppColors.textMuted,
|
|
suffixIconColor: AppColors.textMuted,
|
|
border: border,
|
|
enabledBorder: border,
|
|
focusedBorder: border.copyWith(
|
|
borderSide: const BorderSide(color: AppColors.primary, width: 1.5),
|
|
),
|
|
errorBorder: border.copyWith(
|
|
borderSide: const BorderSide(color: AppColors.danger),
|
|
),
|
|
focusedErrorBorder: border.copyWith(
|
|
borderSide: const BorderSide(color: AppColors.danger, width: 1.5),
|
|
),
|
|
),
|
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppColors.primary,
|
|
foregroundColor: Colors.white,
|
|
disabledBackgroundColor: AppColors.border,
|
|
disabledForegroundColor: AppColors.textMuted,
|
|
elevation: 0,
|
|
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
|
textStyle: const TextStyle(fontWeight: FontWeight.w600),
|
|
),
|
|
),
|
|
outlinedButtonTheme: OutlinedButtonThemeData(
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: AppColors.primaryStrong,
|
|
side: const BorderSide(color: AppColors.border),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
|
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
|
|
),
|
|
),
|
|
textButtonTheme: TextButtonThemeData(
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: AppColors.primaryStrong,
|
|
textStyle: const TextStyle(fontWeight: FontWeight.w600),
|
|
),
|
|
),
|
|
snackBarTheme: SnackBarThemeData(
|
|
behavior: SnackBarBehavior.floating,
|
|
backgroundColor: AppColors.textStrong,
|
|
contentTextStyle: baseTheme.textTheme.bodyMedium?.copyWith(
|
|
color: Colors.white,
|
|
),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
|
),
|
|
badgeTheme: const BadgeThemeData(
|
|
backgroundColor: AppColors.primary,
|
|
textColor: Colors.white,
|
|
),
|
|
progressIndicatorTheme: const ProgressIndicatorThemeData(
|
|
color: AppColors.primary,
|
|
),
|
|
listTileTheme: const ListTileThemeData(iconColor: AppColors.textMuted),
|
|
);
|
|
}
|