feat: Drag-and-Drop-Reihenfolge, Station-Abschluss-Flow und UI-Verbesserungen

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>
This commit is contained in:
2026-04-13 11:26:30 +02:00
parent 1ac755bcbd
commit 6e8bedd9b4
19 changed files with 1458 additions and 548 deletions

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'l10n/app_localizations.dart';
import 'app_state.dart';
import 'app_theme.dart';
/// Supported languages with their display names and flag emojis
class LanguageOption {
@@ -36,17 +37,17 @@ class _SettingsViewState extends State<SettingsView> {
setState(() {
_selectedLanguageCode = languageCode;
});
// Save language preference
await _appState.setLanguage(languageCode);
// Show confirmation snackbar
_showLanguageChangedSnackBar(languageCode);
}
void _showLanguageChangedSnackBar(String languageCode) {
final l10n = AppLocalizations.of(context);
// Get the language name from the corresponding localization
String languageName;
String flagEmoji;
@@ -98,11 +99,9 @@ class _SettingsViewState extends State<SettingsView> {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'${l10n.languageChanged}: $flagEmoji $languageName',
),
content: Text('${l10n.languageChanged}: $flagEmoji $languageName'),
duration: const Duration(seconds: 2),
backgroundColor: Colors.green,
backgroundColor: AppColors.success,
),
);
}
@@ -129,10 +128,7 @@ class _SettingsViewState extends State<SettingsView> {
final languageOptions = _getLanguageOptions();
return Scaffold(
appBar: AppBar(
title: Text(l10n.settings),
backgroundColor: Colors.deepPurple[100],
),
appBar: AppBar(title: Text(l10n.settings)),
body: ListView(
children: [
// Language Selection Section
@@ -143,7 +139,7 @@ class _SettingsViewState extends State<SettingsView> {
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.grey,
color: AppColors.textMuted,
letterSpacing: 1.2,
),
),
@@ -160,7 +156,7 @@ class _SettingsViewState extends State<SettingsView> {
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.grey[100],
color: AppColors.surfaceMuted,
borderRadius: BorderRadius.circular(20),
),
child: Center(
@@ -173,22 +169,27 @@ class _SettingsViewState extends State<SettingsView> {
title: Text(
language.name,
style: TextStyle(
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
color: isSelected ? Colors.deepPurple : Colors.black87,
fontWeight:
isSelected ? FontWeight.w600 : FontWeight.normal,
color:
isSelected
? AppColors.primaryStrong
: AppColors.textStrong,
),
),
trailing: isSelected
? const Icon(
Icons.check_circle,
color: Colors.deepPurple,
)
: const Icon(
Icons.circle_outlined,
color: Colors.grey,
),
trailing:
isSelected
? const Icon(
Icons.check_circle,
color: AppColors.primary,
)
: const Icon(
Icons.circle_outlined,
color: AppColors.textMuted,
),
onTap: () => _onLanguageSelected(language.code),
selected: isSelected,
selectedTileColor: Colors.deepPurple.withValues(alpha: 0.05),
selectedTileColor: AppColors.primarySoft,
),
const Divider(height: 1, indent: 72),
],
@@ -203,17 +204,14 @@ class _SettingsViewState extends State<SettingsView> {
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.grey,
color: AppColors.textMuted,
letterSpacing: 1.2,
),
),
),
const Divider(height: 1),
ListTile(
leading: Icon(
Icons.info_outline,
color: Colors.grey[600],
),
leading: Icon(Icons.info_outline, color: AppColors.textMuted),
title: Text(l10n.version),
subtitle: const Text('0.9.2'),
),