feat: erweiterte Chat-Funktionalität, UI-Verbesserungen und Lokalisierungsupdates

- Chat: Nachrichten-Status (read/unread), WebSocket-Verbesserungen
- App: Login-Optimierung, Job-Übersicht verbessert, neue Übersetzungen
- Backend: Dialog-Styling, Invoice-Generator, Job-Verwaltung erweitert
- Mehrsprachigkeit: Neue Übersetzungen für DE, EN, ES, ET, FR, LT, LV, PL, RU, TR
This commit is contained in:
2026-04-04 10:30:36 +02:00
parent d6132fabe1
commit bba5733783
55 changed files with 2708 additions and 697 deletions

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'app_state.dart';
import 'l10n/app_localizations.dart';
import 'l10n/localization_helpers.dart';
import 'services/websocket_service.dart';
import 'services/dart_mq.dart';
import 'services/chat_service.dart';
@@ -996,7 +997,9 @@ class _JobsViewState extends State<JobsView> with RouteAware {
: job.deliveryCompany));
final deliveryAddress =
hasMultipleDeliveryStations
? '${job.deliveryStations.length} Stationen'
? AppLocalizations.of(
context,
).deliveryStationsCount(job.deliveryStations.length)
: (firstDeliveryStation?.formattedAddress.isNotEmpty == true
? firstDeliveryStation!.formattedAddress
: _joinNonEmpty([
@@ -1116,7 +1119,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
Text(
job.jobNumber.isNotEmpty
? job.jobNumber
: job.title,
: localizeKnownText(context, job.title),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
@@ -1571,19 +1574,19 @@ class _JobsViewState extends State<JobsView> with RouteAware {
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(job.title),
title: Text(localizeKnownText(context, job.title)),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${AppLocalizations.of(context).status}: ${job.statusDisplayText}',
'${AppLocalizations.of(context).status}: ${_localizedStatusText(job.status)}',
style: const TextStyle(fontWeight: FontWeight.w500),
),
const SizedBox(height: 8),
Text(
'${AppLocalizations.of(context).priority}: ${job.priorityDisplayText}',
'${AppLocalizations.of(context).priority}: ${_localizedPriorityText(job.priority)}',
style: const TextStyle(fontWeight: FontWeight.w500),
),
const SizedBox(height: 8),
@@ -1612,7 +1615,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
style: const TextStyle(fontWeight: FontWeight.w600),
),
const SizedBox(height: 8),
Text(job.description),
Text(localizeKnownText(context, job.description)),
],
// CargoItems section
if (job.cargoItems.isNotEmpty) ...[
@@ -1657,7 +1660,9 @@ class _JobsViewState extends State<JobsView> with RouteAware {
if (job.deliveryStations.isNotEmpty) ...[
const SizedBox(height: 16),
Text(
'${AppLocalizations.of(context).delivery} (${job.deliveryStations.length})',
AppLocalizations.of(
context,
).deliveryStationsCount(job.deliveryStations.length),
style: const TextStyle(fontWeight: FontWeight.w600),
),
const SizedBox(height: 8),
@@ -1674,7 +1679,11 @@ class _JobsViewState extends State<JobsView> with RouteAware {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Station ${station.stationOrder + 1}: ${station.displayName}',
localizedStationLabel(
context,
station.stationOrder + 1,
suffix: station.displayName,
),
style: const TextStyle(fontWeight: FontWeight.w500),
),
if (station.formattedAddress.isNotEmpty) ...[
@@ -1855,12 +1864,49 @@ class _JobsViewState extends State<JobsView> with RouteAware {
if (station.stationOrder == stationOrder) {
final suffix =
station.displayName.isNotEmpty ? station.displayName : station.city;
return suffix.isNotEmpty
? 'Station ${stationOrder + 1}: $suffix'
: 'Station ${stationOrder + 1}';
return localizedStationLabel(context, stationOrder + 1, suffix: suffix);
}
}
return 'Station ${stationOrder + 1}';
return AppLocalizations.of(context).stationNumber(stationOrder + 1);
}
String _localizedStatusText(String status) {
final l10n = AppLocalizations.of(context);
switch (status.toLowerCase()) {
case 'created':
return l10n.statusCreated;
case 'pending':
return l10n.statusPending;
case 'assigned':
return l10n.statusAssigned;
case 'in_progress':
case 'started':
return l10n.statusInProgress;
case 'completed':
case 'done':
return l10n.statusCompleted;
case 'cancelled':
return l10n.statusCancelled;
case 'failed':
return l10n.statusFailed;
default:
return localizeKnownText(context, status);
}
}
String _localizedPriorityText(String priority) {
final l10n = AppLocalizations.of(context);
switch (priority.toLowerCase()) {
case 'low':
return l10n.priorityLow;
case 'high':
return l10n.priorityHigh;
case 'urgent':
return l10n.priorityUrgent;
case 'normal':
default:
return l10n.priorityMedium;
}
}
}