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

@@ -1189,6 +1189,36 @@ class WebSocketService {
}
}
/// Send station completion event to server.
/// Messages are buffered if offline and sent automatically when reconnected.
Future<void> sendStationCompleted({
required String jobId,
required String jobNumber,
required int stationOrder,
bool hasIncompleteOptionalTasks = false,
}) async {
const String destination = '/server/station_completed';
final payload = <String, dynamic>{
'jobId': jobId,
'jobNumber': jobNumber,
'stationOrder': stationOrder,
'completedAt': DateTime.now().toUtc().toIso8601String(),
'hasIncompleteOptionalTasks': hasIncompleteOptionalTasks,
};
try {
final jsonPayload = jsonEncode(payload);
sendMessage(destination, jsonPayload);
} catch (e, st) {
developer.log(
'Error sending station completion: $e',
name: 'WebSocketService',
);
developer.log('Stack: $st', name: 'WebSocketService');
}
}
/// Dispose resources
void dispose() {
_stopReconnectTimer();