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 'dart:async';
import 'package:flutter/material.dart';
import 'app_theme.dart';
import 'l10n/app_localizations.dart';
import 'l10n/localization_helpers.dart';
import 'models/chat.dart';
@@ -52,10 +53,7 @@ class _ChatsViewState extends State<ChatsView> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).chats),
backgroundColor: Colors.deepPurple[100],
),
appBar: AppBar(title: Text(AppLocalizations.of(context).chats)),
body: Column(
children: [const OfflineBanner(), Expanded(child: _buildBody())],
),
@@ -72,11 +70,15 @@ class _ChatsViewState extends State<ChatsView> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.chat_outlined, size: 64, color: Colors.grey),
const Icon(
Icons.chat_outlined,
size: 64,
color: AppColors.textMuted,
),
const SizedBox(height: 16),
Text(
AppLocalizations.of(context).noChatsAvailable,
style: const TextStyle(fontSize: 16, color: Colors.grey),
style: const TextStyle(fontSize: 16, color: AppColors.textMuted),
),
],
),
@@ -108,10 +110,11 @@ class _ChatsViewState extends State<ChatsView> {
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: ListTile(
leading: CircleAvatar(
backgroundColor: isJobChat ? Colors.blue[100] : Colors.green[100],
backgroundColor:
isJobChat ? AppColors.primarySoft : AppColors.secondarySoft,
child: Icon(
isJobChat ? Icons.work : Icons.support_agent,
color: isJobChat ? Colors.blue[700] : Colors.green[700],
color: isJobChat ? AppColors.primaryStrong : AppColors.secondary,
),
),
title: Text(() {
@@ -129,7 +132,7 @@ class _ChatsViewState extends State<ChatsView> {
previewText,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14, color: Colors.grey[700]),
style: const TextStyle(fontSize: 14, color: AppColors.textMuted),
),
trailing: Column(
crossAxisAlignment: CrossAxisAlignment.end,
@@ -137,16 +140,17 @@ class _ChatsViewState extends State<ChatsView> {
children: [
Text(
timeLabel,
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
style: const TextStyle(fontSize: 12, color: AppColors.textMuted),
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: isJobChat ? Colors.blue[50] : Colors.green[50],
color:
isJobChat ? AppColors.primarySoft : AppColors.secondarySoft,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isJobChat ? Colors.blue[200]! : Colors.green[200]!,
color: isJobChat ? AppColors.primary : AppColors.secondary,
),
),
child: Text(
@@ -154,7 +158,8 @@ class _ChatsViewState extends State<ChatsView> {
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: isJobChat ? Colors.blue[700] : Colors.green[700],
color:
isJobChat ? AppColors.primaryStrong : AppColors.secondary,
),
),
),