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:
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'app_state.dart';
|
||||
import 'app_theme.dart';
|
||||
import 'l10n/app_localizations.dart';
|
||||
import 'l10n/localization_helpers.dart';
|
||||
import 'services/websocket_service.dart';
|
||||
@@ -98,7 +99,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
if (isConnected && !_wasConnected) {
|
||||
_showSnack(
|
||||
AppLocalizations.of(context).connectionRestored,
|
||||
backgroundColor: Colors.green,
|
||||
backgroundColor: AppColors.success,
|
||||
);
|
||||
if (_appState.isLoggedIn) {
|
||||
_loadJobs();
|
||||
@@ -115,7 +116,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
if (_appState.isLoggedIn && !_isLoggingOut) {
|
||||
_showSnack(
|
||||
AppLocalizations.of(context).connectionLost,
|
||||
backgroundColor: Colors.red,
|
||||
backgroundColor: AppColors.danger,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -146,7 +147,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
jobNumber != null
|
||||
? 'Job $jobNumber ${AppLocalizations.of(context).jobRemoved}'
|
||||
: AppLocalizations.of(context).jobRemoved;
|
||||
_showSnack(message, backgroundColor: Colors.orange);
|
||||
_showSnack(message, backgroundColor: AppColors.warning);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -177,7 +178,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
jobNumber.isNotEmpty
|
||||
? '${AppLocalizations.of(context).newJobReceived}: $jobNumber'
|
||||
: AppLocalizations.of(context).newJobReceived;
|
||||
_showSnack(message, backgroundColor: Colors.green);
|
||||
_showSnack(message, backgroundColor: AppColors.success);
|
||||
}
|
||||
} catch (e) {
|
||||
developer.log('Error handling job_created event: $e', name: 'JobsView');
|
||||
@@ -204,7 +205,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
});
|
||||
_showSnack(
|
||||
AppLocalizations.of(context).jobsUpdated,
|
||||
backgroundColor: Colors.green,
|
||||
backgroundColor: AppColors.success,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
@@ -560,7 +561,6 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(AppLocalizations.of(context).availableJobs),
|
||||
backgroundColor: Colors.deepPurple[100],
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.logout),
|
||||
onPressed: () {
|
||||
@@ -694,7 +694,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
backgroundColor: AppColors.danger,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
child: Text(AppLocalizations.of(context).logout),
|
||||
@@ -766,8 +766,8 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: Text(AppLocalizations.of(context).refresh),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.deepPurple[100],
|
||||
foregroundColor: Colors.deepPurple[700],
|
||||
backgroundColor: AppColors.primarySoft,
|
||||
foregroundColor: AppColors.primaryStrong,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -792,7 +792,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
} else {
|
||||
_showSnack(
|
||||
AppLocalizations.of(context).offline,
|
||||
backgroundColor: Colors.red,
|
||||
backgroundColor: AppColors.danger,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -908,7 +908,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
if (mounted) {
|
||||
_showSnack(
|
||||
AppLocalizations.of(context).jobDeleted,
|
||||
backgroundColor: Colors.red,
|
||||
backgroundColor: AppColors.danger,
|
||||
);
|
||||
}
|
||||
} catch (e, st) {
|
||||
@@ -917,7 +917,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
if (mounted) {
|
||||
_showSnack(
|
||||
AppLocalizations.of(context).jobDeleteError,
|
||||
backgroundColor: Colors.red,
|
||||
backgroundColor: AppColors.danger,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
@@ -935,19 +935,19 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
Color statusColor;
|
||||
switch (job.statusColor) {
|
||||
case 'green':
|
||||
statusColor = Colors.green;
|
||||
statusColor = AppColors.success;
|
||||
break;
|
||||
case 'blue':
|
||||
statusColor = Colors.blue;
|
||||
statusColor = AppColors.primary;
|
||||
break;
|
||||
case 'orange':
|
||||
statusColor = Colors.orange;
|
||||
statusColor = AppColors.warning;
|
||||
break;
|
||||
case 'red':
|
||||
statusColor = Colors.red;
|
||||
statusColor = AppColors.danger;
|
||||
break;
|
||||
default:
|
||||
statusColor = Colors.grey;
|
||||
statusColor = AppColors.textMuted;
|
||||
}
|
||||
|
||||
// Determine card background color based on task completion
|
||||
@@ -965,9 +965,9 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
if (totalTasks == 0 || completedTasks == 0) {
|
||||
cardBg = null; // unchanged (default)
|
||||
} else if (completedTasks > 0 && completedTasks < totalTasks) {
|
||||
cardBg = Colors.yellow[50];
|
||||
cardBg = AppColors.warningSoft;
|
||||
} else if (completedTasks == totalTasks) {
|
||||
cardBg = Colors.green[50];
|
||||
cardBg = AppColors.successSoft;
|
||||
}
|
||||
// Build robust display strings with fallbacks
|
||||
final pickupName = _joinNonEmpty([job.pickupFirstName, job.pickupLastName]);
|
||||
@@ -1033,7 +1033,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
iconSize: 28,
|
||||
padding: const EdgeInsets.all(10),
|
||||
splashRadius: 24,
|
||||
icon: const Icon(Icons.delete, color: Colors.red),
|
||||
icon: const Icon(Icons.delete, color: AppColors.danger),
|
||||
tooltip: AppLocalizations.of(context).deleteJob,
|
||||
onPressed: () {
|
||||
if (isDeleting) {
|
||||
@@ -1233,13 +1233,13 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
? 0
|
||||
: completedTasks / totalTasks,
|
||||
minHeight: 8,
|
||||
backgroundColor: Colors.grey[200],
|
||||
backgroundColor: AppColors.border,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
completedTasks >= totalTasks
|
||||
? Colors.green
|
||||
? AppColors.success
|
||||
: (completedTasks > 0
|
||||
? Colors.amber
|
||||
: Colors.deepPurpleAccent),
|
||||
? AppColors.warning
|
||||
: AppColors.primary),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1336,7 +1336,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
Icon(
|
||||
Icons.arrow_downward,
|
||||
size: 16,
|
||||
color: Colors.blue[600],
|
||||
color: AppColors.primary,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
@@ -1375,7 +1375,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
||||
tooltip: 'Route planen',
|
||||
icon: const Icon(
|
||||
Icons.route,
|
||||
color: Colors.blueAccent,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
onPressed: () {
|
||||
if (_routeActionInProgress) return;
|
||||
|
||||
Reference in New Issue
Block a user