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

@@ -4,6 +4,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as img;
import 'app_theme.dart';
import 'l10n/app_localizations.dart';
import 'l10n/localization_helpers.dart';
import 'app_state.dart';
@@ -257,13 +258,12 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
'Job-Nr: ${_activeChat.jobNumber}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
color: AppColors.textMuted,
fontWeight: FontWeight.normal,
),
),
],
),
backgroundColor: Colors.deepPurple[100],
actions: [
IconButton(
icon: Icon(isJobChat ? Icons.work : Icons.support_agent),
@@ -281,7 +281,7 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
// Messages list
Expanded(
child: Container(
decoration: BoxDecoration(color: Colors.grey[50]),
decoration: const BoxDecoration(color: AppColors.surfaceMuted),
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.fromLTRB(8, 8, 8, 96),
@@ -325,7 +325,7 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
vertical: isImage ? 6 : 8,
),
decoration: BoxDecoration(
color: isOwn ? Colors.deepPurple[100] : Colors.white,
color: isOwn ? AppColors.primarySoft : AppColors.surface,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(12),
topRight: const Radius.circular(12),
@@ -351,7 +351,10 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
children: [
Text(
_formatMessageTime(message.createdAt),
style: TextStyle(fontSize: 11, color: Colors.grey[600]),
style: const TextStyle(
fontSize: 11,
color: AppColors.textMuted,
),
),
if (isOwn) ...[
const SizedBox(width: 4),
@@ -362,10 +365,10 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
size: 14,
color:
message.pendingSync
? Colors.orange[700]
? AppColors.warning
: (message.read
? Colors.deepPurple[400]
: Colors.grey[600]),
? AppColors.primary
: AppColors.textMuted),
),
],
],
@@ -384,7 +387,7 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
if (!isImage) {
return Text(
message.content,
style: TextStyle(fontSize: 15, color: Colors.grey[800]),
style: const TextStyle(fontSize: 15, color: AppColors.textStrong),
);
}
@@ -455,8 +458,8 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
border: Border(top: BorderSide(color: Colors.grey[300]!)),
color: AppColors.surface,
border: const Border(top: BorderSide(color: AppColors.border)),
),
child: SafeArea(
child: Row(
@@ -466,12 +469,12 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey[200],
color: AppColors.surfaceMuted,
borderRadius: BorderRadius.circular(20),
),
child: const Icon(
Icons.attach_file,
color: Colors.black87,
color: AppColors.text,
size: 20,
),
),
@@ -480,7 +483,7 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[100],
color: AppColors.surfaceMuted,
borderRadius: BorderRadius.circular(20),
),
child: TextField(
@@ -508,7 +511,7 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.deepPurple,
color: AppColors.primary,
borderRadius: BorderRadius.circular(20),
),
child: const Icon(Icons.send, color: Colors.white, size: 20),