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:
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'l10n/app_localizations.dart';
|
import 'l10n/app_localizations.dart';
|
||||||
|
import 'l10n/localization_helpers.dart';
|
||||||
import 'models/delivery_station.dart';
|
import 'models/delivery_station.dart';
|
||||||
import 'models/job.dart';
|
import 'models/job.dart';
|
||||||
import 'services/database_service.dart';
|
import 'services/database_service.dart';
|
||||||
@@ -51,6 +52,8 @@ class _CargoItemsViewState extends State<CargoItemsView> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = AppLocalizations.of(context);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(widget.job.jobNumber),
|
title: Text(widget.job.jobNumber),
|
||||||
@@ -93,7 +96,7 @@ class _CargoItemsViewState extends State<CargoItemsView> {
|
|||||||
Text(
|
Text(
|
||||||
widget.job.jobNumber.isNotEmpty
|
widget.job.jobNumber.isNotEmpty
|
||||||
? widget.job.jobNumber
|
? widget.job.jobNumber
|
||||||
: widget.job.title,
|
: localizeKnownText(context, widget.job.title),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -164,7 +167,7 @@ class _CargoItemsViewState extends State<CargoItemsView> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
'Lieferstationen (${_deliveryStations.length})',
|
l10n.deliveryStationsCount(_deliveryStations.length),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -221,12 +224,12 @@ class _CargoItemsViewState extends State<CargoItemsView> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
'Keine Lieferstationen',
|
AppLocalizations.of(context).noDeliveryStations,
|
||||||
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
|
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
'Dieser Job enthält aktuell keine Lieferstationen.',
|
AppLocalizations.of(context).noDeliveryStationsMessage,
|
||||||
style: TextStyle(fontSize: 14, color: Colors.grey[500]),
|
style: TextStyle(fontSize: 14, color: Colors.grey[500]),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
@@ -255,6 +258,7 @@ class _CargoItemsViewState extends State<CargoItemsView> {
|
|||||||
station.company.isNotEmpty && station.company != title
|
station.company.isNotEmpty && station.company != title
|
||||||
? station.company
|
? station.company
|
||||||
: null;
|
: null;
|
||||||
|
final l10n = AppLocalizations.of(context);
|
||||||
final addressLines =
|
final addressLines =
|
||||||
<String>[
|
<String>[
|
||||||
[
|
[
|
||||||
@@ -289,7 +293,7 @@ class _CargoItemsViewState extends State<CargoItemsView> {
|
|||||||
stationTitle:
|
stationTitle:
|
||||||
station.displayName.isNotEmpty
|
station.displayName.isNotEmpty
|
||||||
? station.displayName
|
? station.displayName
|
||||||
: 'Station ${station.stationOrder + 1}',
|
: l10n.stationNumber(station.stationOrder + 1),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -313,7 +317,7 @@ class _CargoItemsViewState extends State<CargoItemsView> {
|
|||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Station ${station.stationOrder + 1}',
|
l10n.stationNumber(station.stationOrder + 1),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@@ -327,7 +331,9 @@ class _CargoItemsViewState extends State<CargoItemsView> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
title.isNotEmpty ? title : 'Unbenannte Station',
|
title.isNotEmpty
|
||||||
|
? localizeKnownText(context, title)
|
||||||
|
: l10n.unnamedStation,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -359,7 +365,7 @@ class _CargoItemsViewState extends State<CargoItemsView> {
|
|||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_buildDetailItem(
|
_buildDetailItem(
|
||||||
Icons.phone_outlined,
|
Icons.phone_outlined,
|
||||||
'Telefon',
|
l10n.phone,
|
||||||
station.phone,
|
station.phone,
|
||||||
Colors.green,
|
Colors.green,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'dart:typed_data';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:image/image.dart' as img;
|
import 'package:image/image.dart' as img;
|
||||||
import 'l10n/app_localizations.dart';
|
import 'l10n/app_localizations.dart';
|
||||||
|
import 'l10n/localization_helpers.dart';
|
||||||
import 'app_state.dart';
|
import 'app_state.dart';
|
||||||
import 'models/chat.dart';
|
import 'models/chat.dart';
|
||||||
import 'models/chat_message.dart';
|
import 'models/chat_message.dart';
|
||||||
@@ -195,9 +196,7 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
|
|||||||
if (sender == null || sender.isEmpty) {
|
if (sender == null || sender.isEmpty) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(content: Text(AppLocalizations.of(context).noSenderMessage)),
|
||||||
content: Text(AppLocalizations.of(context).noSenderMessage),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -233,7 +232,6 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _chatService.saveOutgoingMessage(result);
|
|
||||||
_syncActiveChatFromService();
|
_syncActiveChatFromService();
|
||||||
|
|
||||||
_messageController.clear();
|
_messageController.clear();
|
||||||
@@ -250,7 +248,10 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
|
|||||||
title: Column(
|
title: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(_activeChat.title, style: const TextStyle(fontSize: 16)),
|
Text(
|
||||||
|
localizedChatTitle(context, _activeChat),
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
if (isJobChat && _activeChat.jobNumber != null)
|
if (isJobChat && _activeChat.jobNumber != null)
|
||||||
Text(
|
Text(
|
||||||
'Job-Nr: ${_activeChat.jobNumber}',
|
'Job-Nr: ${_activeChat.jobNumber}',
|
||||||
@@ -540,9 +541,7 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
|
|||||||
if (sender == null || sender.isEmpty) {
|
if (sender == null || sender.isEmpty) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(content: Text(AppLocalizations.of(context).noSenderMessage)),
|
||||||
content: Text(AppLocalizations.of(context).noSenderMessage),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -589,7 +588,6 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _chatService.saveOutgoingMessage(result);
|
|
||||||
_syncActiveChatFromService();
|
_syncActiveChatFromService();
|
||||||
|
|
||||||
if (prepared.bytes.isNotEmpty) {
|
if (prepared.bytes.isNotEmpty) {
|
||||||
@@ -645,7 +643,7 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
|
|||||||
return '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
return '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
||||||
} else if (messageDate == today.subtract(const Duration(days: 1))) {
|
} else if (messageDate == today.subtract(const Duration(days: 1))) {
|
||||||
// Yesterday
|
// Yesterday
|
||||||
return 'Gestern ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
return '${AppLocalizations.of(context).yesterday} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
||||||
} else {
|
} else {
|
||||||
// Older - show date and time
|
// Older - show date and time
|
||||||
return '${dateTime.day.toString().padLeft(2, '0')}.${dateTime.month.toString().padLeft(2, '0')} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
return '${dateTime.day.toString().padLeft(2, '0')}.${dateTime.month.toString().padLeft(2, '0')} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
||||||
@@ -659,21 +657,27 @@ class _ChatDetailsViewState extends State<ChatDetailsView> {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(_activeChat.title),
|
title: Text(localizedChatTitle(context, _activeChat)),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('${AppLocalizations.of(context).status}: ${isJobChat ? AppLocalizations.of(context).chatTypeJob : AppLocalizations.of(context).chatTypeGeneral}'),
|
Text(
|
||||||
|
'${AppLocalizations.of(context).status}: ${isJobChat ? AppLocalizations.of(context).chatTypeJob : AppLocalizations.of(context).chatTypeGeneral}',
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
if (isJobChat && _activeChat.jobNumber != null) ...[
|
if (isJobChat && _activeChat.jobNumber != null) ...[
|
||||||
Text('${AppLocalizations.of(context).jobNumber}: ${_activeChat.jobNumber}'),
|
Text(
|
||||||
|
'${AppLocalizations.of(context).jobNumber}: ${_activeChat.jobNumber}',
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
],
|
],
|
||||||
Text('${AppLocalizations.of(context).messages}: ${_messages.length}'),
|
Text(
|
||||||
|
'${AppLocalizations.of(context).messages}: ${_messages.length}',
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
'Erstellt: ${_formatMessageTime(_messages.isNotEmpty ? _messages.first.createdAt : DateTime.now())}',
|
'${AppLocalizations.of(context).created}: ${_formatMessageTime(_messages.isNotEmpty ? _messages.first.createdAt : DateTime.now())}',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'l10n/app_localizations.dart';
|
import 'l10n/app_localizations.dart';
|
||||||
|
import 'l10n/localization_helpers.dart';
|
||||||
import 'models/chat.dart';
|
import 'models/chat.dart';
|
||||||
import 'services/chat_service.dart';
|
import 'services/chat_service.dart';
|
||||||
import 'widgets/offline_banner.dart';
|
import 'widgets/offline_banner.dart';
|
||||||
@@ -56,10 +57,7 @@ class _ChatsViewState extends State<ChatsView> {
|
|||||||
backgroundColor: Colors.deepPurple[100],
|
backgroundColor: Colors.deepPurple[100],
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [const OfflineBanner(), Expanded(child: _buildBody())],
|
||||||
const OfflineBanner(),
|
|
||||||
Expanded(child: _buildBody()),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -70,15 +68,15 @@ class _ChatsViewState extends State<ChatsView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_chats.isEmpty) {
|
if (_chats.isEmpty) {
|
||||||
return const Center(
|
return Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.chat_outlined, size: 64, color: Colors.grey),
|
const Icon(Icons.chat_outlined, size: 64, color: Colors.grey),
|
||||||
SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
'Keine Chats verfügbar',
|
AppLocalizations.of(context).noChatsAvailable,
|
||||||
style: TextStyle(fontSize: 16, color: Colors.grey),
|
style: const TextStyle(fontSize: 16, color: Colors.grey),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -98,7 +96,9 @@ class _ChatsViewState extends State<ChatsView> {
|
|||||||
final isJobChat = chat.type == ChatType.jobSpecific;
|
final isJobChat = chat.type == ChatType.jobSpecific;
|
||||||
final hasMessages = chat.messages.isNotEmpty;
|
final hasMessages = chat.messages.isNotEmpty;
|
||||||
final previewText =
|
final previewText =
|
||||||
hasMessages ? chat.lastMessagePreview : 'Noch keine Nachrichten';
|
hasMessages
|
||||||
|
? chat.lastMessagePreview
|
||||||
|
: AppLocalizations.of(context).noMessagesYet;
|
||||||
final timeLabel = hasMessages ? _formatTime(chat.lastMessageTime) : '--';
|
final timeLabel = hasMessages ? _formatTime(chat.lastMessageTime) : '--';
|
||||||
final jobId = chat.jobId?.trim();
|
final jobId = chat.jobId?.trim();
|
||||||
final jobNumber = chat.jobNumber?.trim();
|
final jobNumber = chat.jobNumber?.trim();
|
||||||
@@ -123,9 +123,7 @@ class _ChatsViewState extends State<ChatsView> {
|
|||||||
return 'Job $jobId';
|
return 'Job $jobId';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return chat.type == ChatType.general
|
return localizedChatTitle(context, chat);
|
||||||
? 'Allgemeine Nachrichten'
|
|
||||||
: chat.title;
|
|
||||||
}(), style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
|
}(), style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
previewText,
|
previewText,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'app_state.dart';
|
import 'app_state.dart';
|
||||||
import 'l10n/app_localizations.dart';
|
import 'l10n/app_localizations.dart';
|
||||||
|
import 'l10n/localization_helpers.dart';
|
||||||
import 'services/websocket_service.dart';
|
import 'services/websocket_service.dart';
|
||||||
import 'services/dart_mq.dart';
|
import 'services/dart_mq.dart';
|
||||||
import 'services/chat_service.dart';
|
import 'services/chat_service.dart';
|
||||||
@@ -996,7 +997,9 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
|||||||
: job.deliveryCompany));
|
: job.deliveryCompany));
|
||||||
final deliveryAddress =
|
final deliveryAddress =
|
||||||
hasMultipleDeliveryStations
|
hasMultipleDeliveryStations
|
||||||
? '${job.deliveryStations.length} Stationen'
|
? AppLocalizations.of(
|
||||||
|
context,
|
||||||
|
).deliveryStationsCount(job.deliveryStations.length)
|
||||||
: (firstDeliveryStation?.formattedAddress.isNotEmpty == true
|
: (firstDeliveryStation?.formattedAddress.isNotEmpty == true
|
||||||
? firstDeliveryStation!.formattedAddress
|
? firstDeliveryStation!.formattedAddress
|
||||||
: _joinNonEmpty([
|
: _joinNonEmpty([
|
||||||
@@ -1116,7 +1119,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
|||||||
Text(
|
Text(
|
||||||
job.jobNumber.isNotEmpty
|
job.jobNumber.isNotEmpty
|
||||||
? job.jobNumber
|
? job.jobNumber
|
||||||
: job.title,
|
: localizeKnownText(context, job.title),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -1571,19 +1574,19 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(job.title),
|
title: Text(localizeKnownText(context, job.title)),
|
||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'${AppLocalizations.of(context).status}: ${job.statusDisplayText}',
|
'${AppLocalizations.of(context).status}: ${_localizedStatusText(job.status)}',
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
'${AppLocalizations.of(context).priority}: ${job.priorityDisplayText}',
|
'${AppLocalizations.of(context).priority}: ${_localizedPriorityText(job.priority)}',
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
@@ -1612,7 +1615,7 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
|||||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(job.description),
|
Text(localizeKnownText(context, job.description)),
|
||||||
],
|
],
|
||||||
// CargoItems section
|
// CargoItems section
|
||||||
if (job.cargoItems.isNotEmpty) ...[
|
if (job.cargoItems.isNotEmpty) ...[
|
||||||
@@ -1657,7 +1660,9 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
|||||||
if (job.deliveryStations.isNotEmpty) ...[
|
if (job.deliveryStations.isNotEmpty) ...[
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
'${AppLocalizations.of(context).delivery} (${job.deliveryStations.length})',
|
AppLocalizations.of(
|
||||||
|
context,
|
||||||
|
).deliveryStationsCount(job.deliveryStations.length),
|
||||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
@@ -1674,7 +1679,11 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Station ${station.stationOrder + 1}: ${station.displayName}',
|
localizedStationLabel(
|
||||||
|
context,
|
||||||
|
station.stationOrder + 1,
|
||||||
|
suffix: station.displayName,
|
||||||
|
),
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
if (station.formattedAddress.isNotEmpty) ...[
|
if (station.formattedAddress.isNotEmpty) ...[
|
||||||
@@ -1855,12 +1864,49 @@ class _JobsViewState extends State<JobsView> with RouteAware {
|
|||||||
if (station.stationOrder == stationOrder) {
|
if (station.stationOrder == stationOrder) {
|
||||||
final suffix =
|
final suffix =
|
||||||
station.displayName.isNotEmpty ? station.displayName : station.city;
|
station.displayName.isNotEmpty ? station.displayName : station.city;
|
||||||
return suffix.isNotEmpty
|
return localizedStationLabel(context, stationOrder + 1, suffix: suffix);
|
||||||
? 'Station ${stationOrder + 1}: $suffix'
|
|
||||||
: 'Station ${stationOrder + 1}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,15 +11,28 @@ import 'app_localizations_lv.dart';
|
|||||||
import 'app_localizations_lt.dart';
|
import 'app_localizations_lt.dart';
|
||||||
|
|
||||||
/// Supported language codes
|
/// Supported language codes
|
||||||
const List<String> supportedLanguageCodes = ['de', 'en', 'es', 'fr', 'pl', 'ru', 'tr', 'et', 'lv', 'lt'];
|
const List<String> supportedLanguageCodes = [
|
||||||
|
'de',
|
||||||
|
'en',
|
||||||
|
'es',
|
||||||
|
'fr',
|
||||||
|
'pl',
|
||||||
|
'ru',
|
||||||
|
'tr',
|
||||||
|
'et',
|
||||||
|
'lv',
|
||||||
|
'lt',
|
||||||
|
];
|
||||||
|
|
||||||
/// AppLocalizations provides localized strings for the app
|
/// AppLocalizations provides localized strings for the app
|
||||||
abstract class AppLocalizations {
|
abstract class AppLocalizations {
|
||||||
static AppLocalizations of(BuildContext context) {
|
static AppLocalizations of(BuildContext context) {
|
||||||
return Localizations.of<AppLocalizations>(context, AppLocalizations) ?? AppLocalizationsDe();
|
return Localizations.of<AppLocalizations>(context, AppLocalizations) ??
|
||||||
|
AppLocalizationsDe();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate();
|
static const LocalizationsDelegate<AppLocalizations> delegate =
|
||||||
|
_AppLocalizationsDelegate();
|
||||||
|
|
||||||
/// Language name
|
/// Language name
|
||||||
String get languageName;
|
String get languageName;
|
||||||
@@ -41,6 +54,7 @@ abstract class AppLocalizations {
|
|||||||
String get refresh;
|
String get refresh;
|
||||||
String get version;
|
String get version;
|
||||||
String get unknown;
|
String get unknown;
|
||||||
|
String get yesterday;
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
String get jobs;
|
String get jobs;
|
||||||
@@ -58,7 +72,14 @@ abstract class AppLocalizations {
|
|||||||
String get welcomeBack;
|
String get welcomeBack;
|
||||||
String get loginSubtitle;
|
String get loginSubtitle;
|
||||||
String get email;
|
String get email;
|
||||||
|
String get emailAddress;
|
||||||
|
String get emailAddressHint;
|
||||||
|
String get emailAddressRequired;
|
||||||
|
String get emailAddressInvalid;
|
||||||
String get password;
|
String get password;
|
||||||
|
String get passwordHint;
|
||||||
|
String get passwordRequired;
|
||||||
|
String get passwordMinLength;
|
||||||
String get login;
|
String get login;
|
||||||
String get loggingIn;
|
String get loggingIn;
|
||||||
String get forgotPassword;
|
String get forgotPassword;
|
||||||
@@ -101,6 +122,15 @@ abstract class AppLocalizations {
|
|||||||
String get deleteJob;
|
String get deleteJob;
|
||||||
String get jobRemoved;
|
String get jobRemoved;
|
||||||
String get newJobReceived;
|
String get newJobReceived;
|
||||||
|
String get jobDetails;
|
||||||
|
String get jobTasks;
|
||||||
|
String get deliveryStations;
|
||||||
|
String deliveryStationsCount(int count);
|
||||||
|
String get noDeliveryStations;
|
||||||
|
String get noDeliveryStationsMessage;
|
||||||
|
String get phone;
|
||||||
|
String get unnamedStation;
|
||||||
|
String stationNumber(int number);
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
String get tasks;
|
String get tasks;
|
||||||
@@ -182,6 +212,9 @@ abstract class AppLocalizations {
|
|||||||
String get chatTypeGeneral;
|
String get chatTypeGeneral;
|
||||||
String get jobNumber;
|
String get jobNumber;
|
||||||
String get messages;
|
String get messages;
|
||||||
|
String get generalMessages;
|
||||||
|
String get noMessagesYet;
|
||||||
|
String get noChatsAvailable;
|
||||||
String get selectPhoto;
|
String get selectPhoto;
|
||||||
String get unreadMessages;
|
String get unreadMessages;
|
||||||
|
|
||||||
@@ -217,16 +250,20 @@ abstract class AppLocalizations {
|
|||||||
|
|
||||||
// ==================== STATUS ====================
|
// ==================== STATUS ====================
|
||||||
String get statusCreated;
|
String get statusCreated;
|
||||||
|
String get statusPending;
|
||||||
String get statusAssigned;
|
String get statusAssigned;
|
||||||
String get statusInProgress;
|
String get statusInProgress;
|
||||||
String get statusCompleted;
|
String get statusCompleted;
|
||||||
|
String get statusCancelled;
|
||||||
|
String get statusFailed;
|
||||||
String get priorityLow;
|
String get priorityLow;
|
||||||
String get priorityMedium;
|
String get priorityMedium;
|
||||||
String get priorityHigh;
|
String get priorityHigh;
|
||||||
String get priorityUrgent;
|
String get priorityUrgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
|
class _AppLocalizationsDelegate
|
||||||
|
extends LocalizationsDelegate<AppLocalizations> {
|
||||||
const _AppLocalizationsDelegate();
|
const _AppLocalizationsDelegate();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get unknown => 'Unbekannt';
|
String get unknown => 'Unbekannt';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get yesterday => 'Gestern';
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
@override
|
@override
|
||||||
String get jobs => 'Jobs';
|
String get jobs => 'Jobs';
|
||||||
@@ -88,9 +91,32 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get email => 'E-Mail';
|
String get email => 'E-Mail';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddress => 'E-Mail-Adresse';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressHint => 'Geben Sie Ihre E-Mail-Adresse ein';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressRequired => 'Bitte geben Sie Ihre E-Mail-Adresse ein';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressInvalid =>
|
||||||
|
'Bitte geben Sie eine gültige E-Mail-Adresse ein';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get password => 'Passwort';
|
String get password => 'Passwort';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordHint => 'Geben Sie Ihr Passwort ein';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordRequired => 'Bitte geben Sie Ihr Passwort ein';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordMinLength =>
|
||||||
|
'Das Passwort muss mindestens 6 Zeichen lang sein';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get login => 'Anmelden';
|
String get login => 'Anmelden';
|
||||||
|
|
||||||
@@ -101,7 +127,8 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get forgotPassword => 'Passwort vergessen?';
|
String get forgotPassword => 'Passwort vergessen?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get forgotPasswordMessage => 'Passwort vergessen Funktion noch nicht implementiert';
|
String get forgotPasswordMessage =>
|
||||||
|
'Passwort vergessen Funktion noch nicht implementiert';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get loginSuccess => 'Erfolgreich abgemeldet';
|
String get loginSuccess => 'Erfolgreich abgemeldet';
|
||||||
@@ -110,10 +137,12 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get loginFailed => 'Anmeldung fehlgeschlagen';
|
String get loginFailed => 'Anmeldung fehlgeschlagen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get connectionFailed => 'Verbindung zum Server fehlgeschlagen (Timeout).';
|
String get connectionFailed =>
|
||||||
|
'Verbindung zum Server fehlgeschlagen (Timeout).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get connectionTimeout => 'Verbindung zum Server fehlgeschlagen (Timeout).';
|
String get connectionTimeout =>
|
||||||
|
'Verbindung zum Server fehlgeschlagen (Timeout).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get connecting => 'Verbindung zum Server wird hergestellt...';
|
String get connecting => 'Verbindung zum Server wird hergestellt...';
|
||||||
@@ -212,6 +241,34 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newJobReceived => 'Neuer Job erhalten';
|
String get newJobReceived => 'Neuer Job erhalten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobDetails => 'Auftragsdetails';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobTasks => 'Aufgaben eines Auftrags';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deliveryStations => 'Lieferstationen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String deliveryStationsCount(int count) => 'Lieferstationen ($count)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStations => 'Keine Lieferstationen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStationsMessage =>
|
||||||
|
'Dieser Job enthält aktuell keine Lieferstationen.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get phone => 'Telefon';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get unnamedStation => 'Unbenannte Station';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String stationNumber(int number) => 'Station $number';
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
@override
|
@override
|
||||||
String get tasks => 'Aufgaben';
|
String get tasks => 'Aufgaben';
|
||||||
@@ -229,7 +286,8 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get confirmationRequired => 'Bestätigung erforderlich';
|
String get confirmationRequired => 'Bestätigung erforderlich';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get confirmationDescription => 'Klicken Sie auf den Button um die Aufgabe zu erledigen.';
|
String get confirmationDescription =>
|
||||||
|
'Klicken Sie auf den Button um die Aufgabe zu erledigen.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get checklist => 'Checkliste';
|
String get checklist => 'Checkliste';
|
||||||
@@ -241,7 +299,8 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get completeTask => 'Aufgabe abschließen';
|
String get completeTask => 'Aufgabe abschließen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get completeTaskConfirm => 'Möchten Sie diese Aufgabe als erledigt markieren?';
|
String get completeTaskConfirm =>
|
||||||
|
'Möchten Sie diese Aufgabe als erledigt markieren?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get completeTaskNote => 'Notiz (optional)';
|
String get completeTaskNote => 'Notiz (optional)';
|
||||||
@@ -280,7 +339,8 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get signatureError => 'Fehler beim Speichern der Unterschrift';
|
String get signatureError => 'Fehler beim Speichern der Unterschrift';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get signatureInstruction => 'Bitte unterschreiben Sie im Feld unten (Maus oder Finger).';
|
String get signatureInstruction =>
|
||||||
|
'Bitte unterschreiben Sie im Feld unten (Maus oder Finger).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get photoCapture => 'Fotos aufnehmen';
|
String get photoCapture => 'Fotos aufnehmen';
|
||||||
@@ -371,10 +431,12 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get cameraNotAvailable => 'Kamera nicht verfügbar';
|
String get cameraNotAvailable => 'Kamera nicht verfügbar';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedMessage => 'Auf dieser Plattform wird die Kamera nicht unterstützt.';
|
String get cameraNotSupportedMessage =>
|
||||||
|
'Auf dieser Plattform wird die Kamera nicht unterstützt.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedOnPlatform => 'Nicht unterstützt auf dieser Plattform';
|
String get cameraNotSupportedOnPlatform =>
|
||||||
|
'Nicht unterstützt auf dieser Plattform';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get maxPhotosReached => 'Maximum erreicht';
|
String get maxPhotosReached => 'Maximum erreicht';
|
||||||
@@ -389,13 +451,15 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get cameraInitializing => 'Kamera wird initialisiert...';
|
String get cameraInitializing => 'Kamera wird initialisiert...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get cameraLoadingMessage => 'Bitte warten Sie, während die Kamera geladen wird';
|
String get cameraLoadingMessage =>
|
||||||
|
'Bitte warten Sie, während die Kamera geladen wird';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addPhotos => 'Fotos hinzufügen';
|
String get addPhotos => 'Fotos hinzufügen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addPhotosInstruction => 'Verwenden Sie den Button „Foto auswählen", um Bilder von Ihrer Kamera oder Festplatte hinzuzufügen.';
|
String get addPhotosInstruction =>
|
||||||
|
'Verwenden Sie den Button „Foto auswählen", um Bilder von Ihrer Kamera oder Festplatte hinzuzufügen.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get photoOf => 'von';
|
String get photoOf => 'von';
|
||||||
@@ -411,13 +475,15 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get noSender => 'Kein Absender verfügbar';
|
String get noSender => 'Kein Absender verfügbar';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get noSenderMessage => 'Kein Absender verfügbar. Bitte erneut anmelden.';
|
String get noSenderMessage =>
|
||||||
|
'Kein Absender verfügbar. Bitte erneut anmelden.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get noRecipient => 'Kein Empfänger konfiguriert';
|
String get noRecipient => 'Kein Empfänger konfiguriert';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get noRecipientMessage => 'Kein Empfänger für diesen Chat konfiguriert.';
|
String get noRecipientMessage =>
|
||||||
|
'Kein Empfänger für diesen Chat konfiguriert.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get messageSendError => 'Nachricht konnte nicht gesendet werden.';
|
String get messageSendError => 'Nachricht konnte nicht gesendet werden.';
|
||||||
@@ -443,6 +509,15 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get messages => 'Nachrichten';
|
String get messages => 'Nachrichten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get generalMessages => 'Allgemeine Nachrichten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noMessagesYet => 'Noch keine Nachrichten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noChatsAvailable => 'Keine Chats verfügbar';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get selectPhoto => 'Foto auswählen';
|
String get selectPhoto => 'Foto auswählen';
|
||||||
|
|
||||||
@@ -482,7 +557,8 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get noCargoItems => 'Keine Frachtgüter';
|
String get noCargoItems => 'Keine Frachtgüter';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get noCargoItemsMessage => 'Für diesen Job sind keine Frachtgüter definiert.';
|
String get noCargoItemsMessage =>
|
||||||
|
'Für diesen Job sind keine Frachtgüter definiert.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get article => 'Artikel';
|
String get article => 'Artikel';
|
||||||
@@ -528,6 +604,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCreated => 'Erstellt';
|
String get statusCreated => 'Erstellt';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get statusPending => 'Wartend';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get statusAssigned => 'Zugewiesen';
|
String get statusAssigned => 'Zugewiesen';
|
||||||
|
|
||||||
@@ -537,6 +616,12 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCompleted => 'Abgeschlossen';
|
String get statusCompleted => 'Abgeschlossen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get statusCancelled => 'Abgebrochen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get statusFailed => 'Fehlgeschlagen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get priorityLow => 'Niedrig';
|
String get priorityLow => 'Niedrig';
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get unknown => 'Unknown';
|
String get unknown => 'Unknown';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get yesterday => 'Yesterday';
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
@override
|
@override
|
||||||
String get jobs => 'Jobs';
|
String get jobs => 'Jobs';
|
||||||
@@ -88,9 +91,30 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get email => 'Email';
|
String get email => 'Email';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddress => 'Email Address';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressHint => 'Enter your email address';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressRequired => 'Please enter your email address';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressInvalid => 'Please enter a valid email address';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get password => 'Password';
|
String get password => 'Password';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordHint => 'Enter your password';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordRequired => 'Please enter your password';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordMinLength => 'Password must be at least 6 characters long';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get login => 'Login';
|
String get login => 'Login';
|
||||||
|
|
||||||
@@ -101,7 +125,8 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get forgotPassword => 'Forgot Password?';
|
String get forgotPassword => 'Forgot Password?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get forgotPasswordMessage => 'Forgot password feature not yet implemented';
|
String get forgotPasswordMessage =>
|
||||||
|
'Forgot password feature not yet implemented';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get loginSuccess => 'Successfully logged out';
|
String get loginSuccess => 'Successfully logged out';
|
||||||
@@ -212,6 +237,34 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newJobReceived => 'New job received';
|
String get newJobReceived => 'New job received';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobDetails => 'Job Details';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobTasks => 'Job Tasks';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deliveryStations => 'Delivery Stations';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String deliveryStationsCount(int count) => 'Delivery Stations ($count)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStations => 'No Delivery Stations';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStationsMessage =>
|
||||||
|
'This job currently contains no delivery stations.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get phone => 'Phone';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get unnamedStation => 'Unnamed Station';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String stationNumber(int number) => 'Station $number';
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
@override
|
@override
|
||||||
String get tasks => 'Tasks';
|
String get tasks => 'Tasks';
|
||||||
@@ -229,7 +282,8 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get confirmationRequired => 'Confirmation Required';
|
String get confirmationRequired => 'Confirmation Required';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get confirmationDescription => 'Click the button to complete the task.';
|
String get confirmationDescription =>
|
||||||
|
'Click the button to complete the task.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get checklist => 'Checklist';
|
String get checklist => 'Checklist';
|
||||||
@@ -241,7 +295,8 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get completeTask => 'Complete Task';
|
String get completeTask => 'Complete Task';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get completeTaskConfirm => 'Do you want to mark this task as completed?';
|
String get completeTaskConfirm =>
|
||||||
|
'Do you want to mark this task as completed?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get completeTaskNote => 'Note (optional)';
|
String get completeTaskNote => 'Note (optional)';
|
||||||
@@ -280,7 +335,8 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get signatureError => 'Error saving signature';
|
String get signatureError => 'Error saving signature';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get signatureInstruction => 'Please sign in the field below (mouse or finger).';
|
String get signatureInstruction =>
|
||||||
|
'Please sign in the field below (mouse or finger).';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get photoCapture => 'Take Photos';
|
String get photoCapture => 'Take Photos';
|
||||||
@@ -371,7 +427,8 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get cameraNotAvailable => 'Camera not available';
|
String get cameraNotAvailable => 'Camera not available';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedMessage => 'The camera is not supported on this platform.';
|
String get cameraNotSupportedMessage =>
|
||||||
|
'The camera is not supported on this platform.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedOnPlatform => 'Not supported on this platform';
|
String get cameraNotSupportedOnPlatform => 'Not supported on this platform';
|
||||||
@@ -395,7 +452,8 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get addPhotos => 'Add photos';
|
String get addPhotos => 'Add photos';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addPhotosInstruction => 'Use the "Select photo" button to add images from your camera or hard drive.';
|
String get addPhotosInstruction =>
|
||||||
|
'Use the "Select photo" button to add images from your camera or hard drive.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get photoOf => 'of';
|
String get photoOf => 'of';
|
||||||
@@ -443,6 +501,15 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get messages => 'Messages';
|
String get messages => 'Messages';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get generalMessages => 'General Messages';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noMessagesYet => 'No messages yet';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noChatsAvailable => 'No chats available';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get selectPhoto => 'Select Photo';
|
String get selectPhoto => 'Select Photo';
|
||||||
|
|
||||||
@@ -528,6 +595,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCreated => 'Created';
|
String get statusCreated => 'Created';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get statusPending => 'Pending';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get statusAssigned => 'Assigned';
|
String get statusAssigned => 'Assigned';
|
||||||
|
|
||||||
@@ -537,6 +607,12 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCompleted => 'Completed';
|
String get statusCompleted => 'Completed';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get statusCancelled => 'Cancelled';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get statusFailed => 'Failed';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get priorityLow => 'Low';
|
String get priorityLow => 'Low';
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get unknown => 'Desconocido';
|
String get unknown => 'Desconocido';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get yesterday => 'Ayer';
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
@override
|
@override
|
||||||
String get jobs => 'Trabajos';
|
String get jobs => 'Trabajos';
|
||||||
@@ -64,8 +67,33 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
String get loginSubtitle => 'Inicie sesión en su cuenta';
|
String get loginSubtitle => 'Inicie sesión en su cuenta';
|
||||||
@override
|
@override
|
||||||
String get email => 'Correo electrónico';
|
String get email => 'Correo electrónico';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddress => 'Dirección de correo electrónico';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressHint =>
|
||||||
|
'Introduzca su dirección de correo electrónico';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressRequired =>
|
||||||
|
'Por favor, introduzca su dirección de correo electrónico';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressInvalid =>
|
||||||
|
'Por favor, introduzca una dirección de correo electrónico válida';
|
||||||
@override
|
@override
|
||||||
String get password => 'Contraseña';
|
String get password => 'Contraseña';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordHint => 'Introduzca su contraseña';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordRequired => 'Por favor, introduzca su contraseña';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordMinLength =>
|
||||||
|
'La contraseña debe tener al menos 6 caracteres';
|
||||||
@override
|
@override
|
||||||
String get login => 'Iniciar sesión';
|
String get login => 'Iniciar sesión';
|
||||||
@override
|
@override
|
||||||
@@ -73,15 +101,18 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get forgotPassword => '¿Olvidó su contraseña?';
|
String get forgotPassword => '¿Olvidó su contraseña?';
|
||||||
@override
|
@override
|
||||||
String get forgotPasswordMessage => 'Función de contraseña olvidada aún no implementada';
|
String get forgotPasswordMessage =>
|
||||||
|
'Función de contraseña olvidada aún no implementada';
|
||||||
@override
|
@override
|
||||||
String get loginSuccess => 'Sesión cerrada correctamente';
|
String get loginSuccess => 'Sesión cerrada correctamente';
|
||||||
@override
|
@override
|
||||||
String get loginFailed => 'Error al iniciar sesión';
|
String get loginFailed => 'Error al iniciar sesión';
|
||||||
@override
|
@override
|
||||||
String get connectionFailed => 'Error de conexión al servidor (Tiempo agotado).';
|
String get connectionFailed =>
|
||||||
|
'Error de conexión al servidor (Tiempo agotado).';
|
||||||
@override
|
@override
|
||||||
String get connectionTimeout => 'Error de conexión al servidor (Tiempo agotado).';
|
String get connectionTimeout =>
|
||||||
|
'Error de conexión al servidor (Tiempo agotado).';
|
||||||
@override
|
@override
|
||||||
String get connecting => 'Conectando al servidor...';
|
String get connecting => 'Conectando al servidor...';
|
||||||
@override
|
@override
|
||||||
@@ -149,6 +180,34 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newJobReceived => 'Nuevo trabajo recibido';
|
String get newJobReceived => 'Nuevo trabajo recibido';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobDetails => 'Detalles del pedido';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobTasks => 'Tareas del pedido';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deliveryStations => 'Estaciones de entrega';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String deliveryStationsCount(int count) => 'Estaciones de entrega ($count)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStations => 'No hay estaciones de entrega';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStationsMessage =>
|
||||||
|
'Este trabajo no contiene estaciones de entrega actualmente.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get phone => 'Teléfono';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get unnamedStation => 'Estación sin nombre';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String stationNumber(int number) => 'Estación $number';
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
@override
|
@override
|
||||||
String get tasks => 'Tareas';
|
String get tasks => 'Tareas';
|
||||||
@@ -161,7 +220,8 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get confirmationRequired => 'Confirmación requerida';
|
String get confirmationRequired => 'Confirmación requerida';
|
||||||
@override
|
@override
|
||||||
String get confirmationDescription => 'Haga clic en el botón para completar la tarea.';
|
String get confirmationDescription =>
|
||||||
|
'Haga clic en el botón para completar la tarea.';
|
||||||
@override
|
@override
|
||||||
String get checklist => 'Lista de verificación';
|
String get checklist => 'Lista de verificación';
|
||||||
@override
|
@override
|
||||||
@@ -195,7 +255,8 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get signatureError => 'Error al guardar la firma';
|
String get signatureError => 'Error al guardar la firma';
|
||||||
@override
|
@override
|
||||||
String get signatureInstruction => 'Por favor, firme en el campo de abajo (ratón o dedo).';
|
String get signatureInstruction =>
|
||||||
|
'Por favor, firme en el campo de abajo (ratón o dedo).';
|
||||||
@override
|
@override
|
||||||
String get photoCapture => 'Tomar fotos';
|
String get photoCapture => 'Tomar fotos';
|
||||||
@override
|
@override
|
||||||
@@ -243,11 +304,14 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get enterBarcode => 'Ingresar código de barras';
|
String get enterBarcode => 'Ingresar código de barras';
|
||||||
@override
|
@override
|
||||||
String get barcodeEnterDescription => 'Por favor ingrese los códigos de barras:';
|
String get barcodeEnterDescription =>
|
||||||
|
'Por favor ingrese los códigos de barras:';
|
||||||
@override
|
@override
|
||||||
String barcodeNumberRequired(int number) => 'Código de barras $number (requerido)';
|
String barcodeNumberRequired(int number) =>
|
||||||
|
'Código de barras $number (requerido)';
|
||||||
@override
|
@override
|
||||||
String barcodeNumberOptional(int number) => 'Código de barras $number (opcional)';
|
String barcodeNumberOptional(int number) =>
|
||||||
|
'Código de barras $number (opcional)';
|
||||||
@override
|
@override
|
||||||
String get barcodeError => 'Error al escanear el código de barras';
|
String get barcodeError => 'Error al escanear el código de barras';
|
||||||
@override
|
@override
|
||||||
@@ -257,7 +321,8 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraNotAvailable => 'Cámara no disponible';
|
String get cameraNotAvailable => 'Cámara no disponible';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedMessage => 'La cámara no es compatible con esta plataforma.';
|
String get cameraNotSupportedMessage =>
|
||||||
|
'La cámara no es compatible con esta plataforma.';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedOnPlatform => 'No soportado en esta plataforma';
|
String get cameraNotSupportedOnPlatform => 'No soportado en esta plataforma';
|
||||||
@override
|
@override
|
||||||
@@ -269,11 +334,13 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraInitializing => 'Inicializando cámara...';
|
String get cameraInitializing => 'Inicializando cámara...';
|
||||||
@override
|
@override
|
||||||
String get cameraLoadingMessage => 'Por favor espere mientras se carga la cámara';
|
String get cameraLoadingMessage =>
|
||||||
|
'Por favor espere mientras se carga la cámara';
|
||||||
@override
|
@override
|
||||||
String get addPhotos => 'Añadir fotos';
|
String get addPhotos => 'Añadir fotos';
|
||||||
@override
|
@override
|
||||||
String get addPhotosInstruction => 'Use el botón "Seleccionar foto" para añadir imágenes de su cámara o disco duro.';
|
String get addPhotosInstruction =>
|
||||||
|
'Use el botón "Seleccionar foto" para añadir imágenes de su cámara o disco duro.';
|
||||||
@override
|
@override
|
||||||
String get photoOf => 'de';
|
String get photoOf => 'de';
|
||||||
|
|
||||||
@@ -285,11 +352,13 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noSender => 'No hay remitente disponible';
|
String get noSender => 'No hay remitente disponible';
|
||||||
@override
|
@override
|
||||||
String get noSenderMessage => 'No hay remitente disponible. Por favor inicie sesión de nuevo.';
|
String get noSenderMessage =>
|
||||||
|
'No hay remitente disponible. Por favor inicie sesión de nuevo.';
|
||||||
@override
|
@override
|
||||||
String get noRecipient => 'No hay destinatario configurado';
|
String get noRecipient => 'No hay destinatario configurado';
|
||||||
@override
|
@override
|
||||||
String get noRecipientMessage => 'No hay destinatario configurado para este chat.';
|
String get noRecipientMessage =>
|
||||||
|
'No hay destinatario configurado para este chat.';
|
||||||
@override
|
@override
|
||||||
String get messageSendError => 'El mensaje no pudo ser enviado.';
|
String get messageSendError => 'El mensaje no pudo ser enviado.';
|
||||||
@override
|
@override
|
||||||
@@ -306,6 +375,15 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
String get jobNumber => 'Número de trabajo';
|
String get jobNumber => 'Número de trabajo';
|
||||||
@override
|
@override
|
||||||
String get messages => 'Mensajes';
|
String get messages => 'Mensajes';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get generalMessages => 'Mensajes generales';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noMessagesYet => 'Todavía no hay mensajes';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noChatsAvailable => 'No hay chats disponibles';
|
||||||
@override
|
@override
|
||||||
String get selectPhoto => 'Seleccionar foto';
|
String get selectPhoto => 'Seleccionar foto';
|
||||||
@override
|
@override
|
||||||
@@ -327,7 +405,8 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noCargoItems => 'Sin artículos de carga';
|
String get noCargoItems => 'Sin artículos de carga';
|
||||||
@override
|
@override
|
||||||
String get noCargoItemsMessage => 'No hay artículos de carga definidos para este trabajo.';
|
String get noCargoItemsMessage =>
|
||||||
|
'No hay artículos de carga definidos para este trabajo.';
|
||||||
@override
|
@override
|
||||||
String get article => 'Artículo';
|
String get article => 'Artículo';
|
||||||
|
|
||||||
@@ -369,12 +448,18 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCreated => 'Creado';
|
String get statusCreated => 'Creado';
|
||||||
@override
|
@override
|
||||||
|
String get statusPending => 'Pendiente';
|
||||||
|
@override
|
||||||
String get statusAssigned => 'Asignado';
|
String get statusAssigned => 'Asignado';
|
||||||
@override
|
@override
|
||||||
String get statusInProgress => 'En progreso';
|
String get statusInProgress => 'En progreso';
|
||||||
@override
|
@override
|
||||||
String get statusCompleted => 'Completado';
|
String get statusCompleted => 'Completado';
|
||||||
@override
|
@override
|
||||||
|
String get statusCancelled => 'Cancelado';
|
||||||
|
@override
|
||||||
|
String get statusFailed => 'Fallido';
|
||||||
|
@override
|
||||||
String get priorityLow => 'Baja';
|
String get priorityLow => 'Baja';
|
||||||
@override
|
@override
|
||||||
String get priorityMedium => 'Media';
|
String get priorityMedium => 'Media';
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get unknown => 'Tundmatu';
|
String get unknown => 'Tundmatu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get yesterday => 'Eile';
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
@override
|
@override
|
||||||
String get jobs => 'Tööd';
|
String get jobs => 'Tööd';
|
||||||
@@ -64,8 +67,29 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
String get loginSubtitle => 'Logige oma kontosse sisse';
|
String get loginSubtitle => 'Logige oma kontosse sisse';
|
||||||
@override
|
@override
|
||||||
String get email => 'E-post';
|
String get email => 'E-post';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddress => 'E-posti aadress';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressHint => 'Sisestage oma e-posti aadress';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressRequired => 'Palun sisestage oma e-posti aadress';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressInvalid => 'Palun sisestage kehtiv e-posti aadress';
|
||||||
@override
|
@override
|
||||||
String get password => 'Parool';
|
String get password => 'Parool';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordHint => 'Sisestage oma parool';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordRequired => 'Palun sisestage oma parool';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordMinLength => 'Parool peab olema vähemalt 6 tähemärki pikk';
|
||||||
@override
|
@override
|
||||||
String get login => 'Logi sisse';
|
String get login => 'Logi sisse';
|
||||||
@override
|
@override
|
||||||
@@ -73,15 +97,18 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get forgotPassword => 'Unustasid parooli?';
|
String get forgotPassword => 'Unustasid parooli?';
|
||||||
@override
|
@override
|
||||||
String get forgotPasswordMessage => 'Unustatud parooli funktsioon pole veel rakendatud';
|
String get forgotPasswordMessage =>
|
||||||
|
'Unustatud parooli funktsioon pole veel rakendatud';
|
||||||
@override
|
@override
|
||||||
String get loginSuccess => 'Edukalt välja logitud';
|
String get loginSuccess => 'Edukalt välja logitud';
|
||||||
@override
|
@override
|
||||||
String get loginFailed => 'Sisselogimine ebaõnnestus';
|
String get loginFailed => 'Sisselogimine ebaõnnestus';
|
||||||
@override
|
@override
|
||||||
String get connectionFailed => 'Serveriga ühenduse loomine ebaõnnestus (Aegunud).';
|
String get connectionFailed =>
|
||||||
|
'Serveriga ühenduse loomine ebaõnnestus (Aegunud).';
|
||||||
@override
|
@override
|
||||||
String get connectionTimeout => 'Serveriga ühenduse loomine ebaõnnestus (Aegunud).';
|
String get connectionTimeout =>
|
||||||
|
'Serveriga ühenduse loomine ebaõnnestus (Aegunud).';
|
||||||
@override
|
@override
|
||||||
String get connecting => 'Serveriga ühendamine...';
|
String get connecting => 'Serveriga ühendamine...';
|
||||||
@override
|
@override
|
||||||
@@ -149,6 +176,34 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newJobReceived => 'Uus töö saadud';
|
String get newJobReceived => 'Uus töö saadud';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobDetails => 'Töö üksikasjad';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobTasks => 'Töö ülesanded';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deliveryStations => 'Tarnejaamad';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String deliveryStationsCount(int count) => 'Tarnejaamad ($count)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStations => 'Tarnejaamu pole';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStationsMessage =>
|
||||||
|
'Sellel tööl ei ole praegu tarnejaamu.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get phone => 'Telefon';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get unnamedStation => 'Nimetu jaam';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String stationNumber(int number) => 'Jaam $number';
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
@override
|
@override
|
||||||
String get tasks => 'Ülesanded';
|
String get tasks => 'Ülesanded';
|
||||||
@@ -161,7 +216,8 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get confirmationRequired => 'Vajalik kinnitus';
|
String get confirmationRequired => 'Vajalik kinnitus';
|
||||||
@override
|
@override
|
||||||
String get confirmationDescription => 'Ülesande lõpuleviimiseks klõpsake nuppu.';
|
String get confirmationDescription =>
|
||||||
|
'Ülesande lõpuleviimiseks klõpsake nuppu.';
|
||||||
@override
|
@override
|
||||||
String get checklist => 'Kontrollnimekiri';
|
String get checklist => 'Kontrollnimekiri';
|
||||||
@override
|
@override
|
||||||
@@ -169,7 +225,8 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get completeTask => 'Lõpeta ülesanne';
|
String get completeTask => 'Lõpeta ülesanne';
|
||||||
@override
|
@override
|
||||||
String get completeTaskConfirm => 'Kas soovite selle ülesande lõpetatuks märgistada?';
|
String get completeTaskConfirm =>
|
||||||
|
'Kas soovite selle ülesande lõpetatuks märgistada?';
|
||||||
@override
|
@override
|
||||||
String get completeTaskNote => 'Märkus (valikuline)';
|
String get completeTaskNote => 'Märkus (valikuline)';
|
||||||
@override
|
@override
|
||||||
@@ -195,7 +252,8 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get signatureError => 'Viga allkirja salvestamisel';
|
String get signatureError => 'Viga allkirja salvestamisel';
|
||||||
@override
|
@override
|
||||||
String get signatureInstruction => 'Palun allkirjastage allolevas väljas (hiir või sõrm).';
|
String get signatureInstruction =>
|
||||||
|
'Palun allkirjastage allolevas väljas (hiir või sõrm).';
|
||||||
@override
|
@override
|
||||||
String get photoCapture => 'Tee pilte';
|
String get photoCapture => 'Tee pilte';
|
||||||
@override
|
@override
|
||||||
@@ -257,7 +315,8 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraNotAvailable => 'Kaamera pole saadaval';
|
String get cameraNotAvailable => 'Kaamera pole saadaval';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedMessage => 'Kaamerat ei toetata sellel platvormil.';
|
String get cameraNotSupportedMessage =>
|
||||||
|
'Kaamerat ei toetata sellel platvormil.';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedOnPlatform => 'Sellel platvormil ei toetata';
|
String get cameraNotSupportedOnPlatform => 'Sellel platvormil ei toetata';
|
||||||
@override
|
@override
|
||||||
@@ -273,7 +332,8 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get addPhotos => 'Lisa fotod';
|
String get addPhotos => 'Lisa fotod';
|
||||||
@override
|
@override
|
||||||
String get addPhotosInstruction => 'Kasutage nuppu "Vali foto", et lisada pilte kaamerast või kõvakettalt.';
|
String get addPhotosInstruction =>
|
||||||
|
'Kasutage nuppu "Vali foto", et lisada pilte kaamerast või kõvakettalt.';
|
||||||
@override
|
@override
|
||||||
String get photoOf => '/';
|
String get photoOf => '/';
|
||||||
|
|
||||||
@@ -285,11 +345,13 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noSender => 'Saatja pole saadaval';
|
String get noSender => 'Saatja pole saadaval';
|
||||||
@override
|
@override
|
||||||
String get noSenderMessage => 'Saatja pole saadaval. Palun logige uuesti sisse.';
|
String get noSenderMessage =>
|
||||||
|
'Saatja pole saadaval. Palun logige uuesti sisse.';
|
||||||
@override
|
@override
|
||||||
String get noRecipient => 'Vastuvõtjat pole konfigureeritud';
|
String get noRecipient => 'Vastuvõtjat pole konfigureeritud';
|
||||||
@override
|
@override
|
||||||
String get noRecipientMessage => 'Selle vestluse jaoks pole vastuvõtjat konfigureeritud.';
|
String get noRecipientMessage =>
|
||||||
|
'Selle vestluse jaoks pole vastuvõtjat konfigureeritud.';
|
||||||
@override
|
@override
|
||||||
String get messageSendError => 'Sõnumi saatmine ebaõnnestus.';
|
String get messageSendError => 'Sõnumi saatmine ebaõnnestus.';
|
||||||
@override
|
@override
|
||||||
@@ -306,6 +368,15 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
String get jobNumber => 'Töö number';
|
String get jobNumber => 'Töö number';
|
||||||
@override
|
@override
|
||||||
String get messages => 'Sõnumid';
|
String get messages => 'Sõnumid';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get generalMessages => 'Üldised sõnumid';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noMessagesYet => 'Sõnumeid veel pole';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noChatsAvailable => 'Vestlusi pole saadaval';
|
||||||
@override
|
@override
|
||||||
String get selectPhoto => 'Vali foto';
|
String get selectPhoto => 'Vali foto';
|
||||||
@override
|
@override
|
||||||
@@ -369,12 +440,18 @@ class AppLocalizationsEt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCreated => 'Loodud';
|
String get statusCreated => 'Loodud';
|
||||||
@override
|
@override
|
||||||
|
String get statusPending => 'Ootel';
|
||||||
|
@override
|
||||||
String get statusAssigned => 'Määratud';
|
String get statusAssigned => 'Määratud';
|
||||||
@override
|
@override
|
||||||
String get statusInProgress => 'Töös';
|
String get statusInProgress => 'Töös';
|
||||||
@override
|
@override
|
||||||
String get statusCompleted => 'Lõpetatud';
|
String get statusCompleted => 'Lõpetatud';
|
||||||
@override
|
@override
|
||||||
|
String get statusCancelled => 'Tühistatud';
|
||||||
|
@override
|
||||||
|
String get statusFailed => 'Ebaõnnestunud';
|
||||||
|
@override
|
||||||
String get priorityLow => 'Madal';
|
String get priorityLow => 'Madal';
|
||||||
@override
|
@override
|
||||||
String get priorityMedium => 'Keskmine';
|
String get priorityMedium => 'Keskmine';
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get unknown => 'Inconnu';
|
String get unknown => 'Inconnu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get yesterday => 'Hier';
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
@override
|
@override
|
||||||
String get jobs => 'Emplois';
|
String get jobs => 'Emplois';
|
||||||
@@ -64,8 +67,30 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get loginSubtitle => 'Connectez-vous à votre compte';
|
String get loginSubtitle => 'Connectez-vous à votre compte';
|
||||||
@override
|
@override
|
||||||
String get email => 'E-mail';
|
String get email => 'E-mail';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddress => 'Adresse e-mail';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressHint => 'Saisissez votre adresse e-mail';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressRequired => 'Veuillez saisir votre adresse e-mail';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressInvalid => 'Veuillez saisir une adresse e-mail valide';
|
||||||
@override
|
@override
|
||||||
String get password => 'Mot de passe';
|
String get password => 'Mot de passe';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordHint => 'Saisissez votre mot de passe';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordRequired => 'Veuillez saisir votre mot de passe';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordMinLength =>
|
||||||
|
'Le mot de passe doit contenir au moins 6 caractères';
|
||||||
@override
|
@override
|
||||||
String get login => 'Connexion';
|
String get login => 'Connexion';
|
||||||
@override
|
@override
|
||||||
@@ -73,15 +98,18 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get forgotPassword => 'Mot de passe oublié?';
|
String get forgotPassword => 'Mot de passe oublié?';
|
||||||
@override
|
@override
|
||||||
String get forgotPasswordMessage => 'Fonction mot de passe oublié pas encore implémentée';
|
String get forgotPasswordMessage =>
|
||||||
|
'Fonction mot de passe oublié pas encore implémentée';
|
||||||
@override
|
@override
|
||||||
String get loginSuccess => 'Déconnexion réussie';
|
String get loginSuccess => 'Déconnexion réussie';
|
||||||
@override
|
@override
|
||||||
String get loginFailed => 'Échec de la connexion';
|
String get loginFailed => 'Échec de la connexion';
|
||||||
@override
|
@override
|
||||||
String get connectionFailed => 'Échec de la connexion au serveur (Délai dépassé).';
|
String get connectionFailed =>
|
||||||
|
'Échec de la connexion au serveur (Délai dépassé).';
|
||||||
@override
|
@override
|
||||||
String get connectionTimeout => 'Échec de la connexion au serveur (Délai dépassé).';
|
String get connectionTimeout =>
|
||||||
|
'Échec de la connexion au serveur (Délai dépassé).';
|
||||||
@override
|
@override
|
||||||
String get connecting => 'Connexion au serveur...';
|
String get connecting => 'Connexion au serveur...';
|
||||||
@override
|
@override
|
||||||
@@ -137,7 +165,8 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get jobsUpdated => 'Emplois actualisés';
|
String get jobsUpdated => 'Emplois actualisés';
|
||||||
@override
|
@override
|
||||||
String get connectionRestored => 'Connexion restaurée. Chargement des emplois...';
|
String get connectionRestored =>
|
||||||
|
'Connexion restaurée. Chargement des emplois...';
|
||||||
@override
|
@override
|
||||||
String get connectionLost => 'Connexion perdue. Hors ligne.';
|
String get connectionLost => 'Connexion perdue. Hors ligne.';
|
||||||
@override
|
@override
|
||||||
@@ -149,6 +178,34 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newJobReceived => 'Nouvel emploi reçu';
|
String get newJobReceived => 'Nouvel emploi reçu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobDetails => 'Détails de la commande';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobTasks => 'Tâches de la commande';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deliveryStations => 'Stations de livraison';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String deliveryStationsCount(int count) => 'Stations de livraison ($count)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStations => 'Aucune station de livraison';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStationsMessage =>
|
||||||
|
'Cette mission ne contient actuellement aucune station de livraison.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get phone => 'Téléphone';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get unnamedStation => 'Station sans nom';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String stationNumber(int number) => 'Station $number';
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
@override
|
@override
|
||||||
String get tasks => 'Tâches';
|
String get tasks => 'Tâches';
|
||||||
@@ -161,7 +218,8 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get confirmationRequired => 'Confirmation requise';
|
String get confirmationRequired => 'Confirmation requise';
|
||||||
@override
|
@override
|
||||||
String get confirmationDescription => 'Cliquez sur le bouton pour terminer la tâche.';
|
String get confirmationDescription =>
|
||||||
|
'Cliquez sur le bouton pour terminer la tâche.';
|
||||||
@override
|
@override
|
||||||
String get checklist => 'Liste de contrôle';
|
String get checklist => 'Liste de contrôle';
|
||||||
@override
|
@override
|
||||||
@@ -169,7 +227,8 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get completeTask => 'Terminer la tâche';
|
String get completeTask => 'Terminer la tâche';
|
||||||
@override
|
@override
|
||||||
String get completeTaskConfirm => 'Voulez-vous marquer cette tâche comme terminée?';
|
String get completeTaskConfirm =>
|
||||||
|
'Voulez-vous marquer cette tâche comme terminée?';
|
||||||
@override
|
@override
|
||||||
String get completeTaskNote => 'Note (optionnelle)';
|
String get completeTaskNote => 'Note (optionnelle)';
|
||||||
@override
|
@override
|
||||||
@@ -193,9 +252,11 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get clear => 'Effacer';
|
String get clear => 'Effacer';
|
||||||
@override
|
@override
|
||||||
String get signatureError => 'Erreur lors de l\'enregistrement de la signature';
|
String get signatureError =>
|
||||||
|
'Erreur lors de l\'enregistrement de la signature';
|
||||||
@override
|
@override
|
||||||
String get signatureInstruction => 'Veuillez signer dans le champ ci-dessous (souris ou doigt).';
|
String get signatureInstruction =>
|
||||||
|
'Veuillez signer dans le champ ci-dessous (souris ou doigt).';
|
||||||
@override
|
@override
|
||||||
String get photoCapture => 'Prendre des photos';
|
String get photoCapture => 'Prendre des photos';
|
||||||
@override
|
@override
|
||||||
@@ -221,7 +282,8 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get deletePhoto => 'Supprimer la photo';
|
String get deletePhoto => 'Supprimer la photo';
|
||||||
@override
|
@override
|
||||||
String get deletePhotoConfirm => 'Voulez-vous vraiment supprimer cette photo?';
|
String get deletePhotoConfirm =>
|
||||||
|
'Voulez-vous vraiment supprimer cette photo?';
|
||||||
@override
|
@override
|
||||||
String get barcode => 'Code-barres';
|
String get barcode => 'Code-barres';
|
||||||
@override
|
@override
|
||||||
@@ -257,9 +319,11 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraNotAvailable => 'Caméra non disponible';
|
String get cameraNotAvailable => 'Caméra non disponible';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedMessage => 'La caméra n\'est pas prise en charge sur cette plateforme.';
|
String get cameraNotSupportedMessage =>
|
||||||
|
'La caméra n\'est pas prise en charge sur cette plateforme.';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedOnPlatform => 'Non supporté sur cette plateforme';
|
String get cameraNotSupportedOnPlatform =>
|
||||||
|
'Non supporté sur cette plateforme';
|
||||||
@override
|
@override
|
||||||
String get maxPhotosReached => 'Maximum atteint';
|
String get maxPhotosReached => 'Maximum atteint';
|
||||||
@override
|
@override
|
||||||
@@ -269,11 +333,13 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraInitializing => 'Initialisation de la caméra...';
|
String get cameraInitializing => 'Initialisation de la caméra...';
|
||||||
@override
|
@override
|
||||||
String get cameraLoadingMessage => 'Veuillez patienter pendant le chargement de la caméra';
|
String get cameraLoadingMessage =>
|
||||||
|
'Veuillez patienter pendant le chargement de la caméra';
|
||||||
@override
|
@override
|
||||||
String get addPhotos => 'Ajouter des photos';
|
String get addPhotos => 'Ajouter des photos';
|
||||||
@override
|
@override
|
||||||
String get addPhotosInstruction => 'Utilisez le bouton "Sélectionner une photo" pour ajouter des images depuis votre appareil photo ou disque dur.';
|
String get addPhotosInstruction =>
|
||||||
|
'Utilisez le bouton "Sélectionner une photo" pour ajouter des images depuis votre appareil photo ou disque dur.';
|
||||||
@override
|
@override
|
||||||
String get photoOf => 'sur';
|
String get photoOf => 'sur';
|
||||||
|
|
||||||
@@ -285,11 +351,13 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noSender => 'Aucun expéditeur disponible';
|
String get noSender => 'Aucun expéditeur disponible';
|
||||||
@override
|
@override
|
||||||
String get noSenderMessage => 'Aucun expéditeur disponible. Veuillez vous reconnecter.';
|
String get noSenderMessage =>
|
||||||
|
'Aucun expéditeur disponible. Veuillez vous reconnecter.';
|
||||||
@override
|
@override
|
||||||
String get noRecipient => 'Aucun destinataire configuré';
|
String get noRecipient => 'Aucun destinataire configuré';
|
||||||
@override
|
@override
|
||||||
String get noRecipientMessage => 'Aucun destinataire configuré pour cette discussion.';
|
String get noRecipientMessage =>
|
||||||
|
'Aucun destinataire configuré pour cette discussion.';
|
||||||
@override
|
@override
|
||||||
String get messageSendError => 'Le message n\'a pas pu être envoyé.';
|
String get messageSendError => 'Le message n\'a pas pu être envoyé.';
|
||||||
@override
|
@override
|
||||||
@@ -306,6 +374,15 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get jobNumber => 'Numéro d\'emploi';
|
String get jobNumber => 'Numéro d\'emploi';
|
||||||
@override
|
@override
|
||||||
String get messages => 'Messages';
|
String get messages => 'Messages';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get generalMessages => 'Messages généraux';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noMessagesYet => 'Pas encore de messages';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noChatsAvailable => 'Aucune discussion disponible';
|
||||||
@override
|
@override
|
||||||
String get selectPhoto => 'Sélectionner une photo';
|
String get selectPhoto => 'Sélectionner une photo';
|
||||||
@override
|
@override
|
||||||
@@ -327,7 +404,8 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noCargoItems => 'Aucun article de cargaison';
|
String get noCargoItems => 'Aucun article de cargaison';
|
||||||
@override
|
@override
|
||||||
String get noCargoItemsMessage => 'Aucun article de cargaison défini pour cet emploi.';
|
String get noCargoItemsMessage =>
|
||||||
|
'Aucun article de cargaison défini pour cet emploi.';
|
||||||
@override
|
@override
|
||||||
String get article => 'Article';
|
String get article => 'Article';
|
||||||
|
|
||||||
@@ -369,12 +447,18 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCreated => 'Créé';
|
String get statusCreated => 'Créé';
|
||||||
@override
|
@override
|
||||||
|
String get statusPending => 'En attente';
|
||||||
|
@override
|
||||||
String get statusAssigned => 'Assigné';
|
String get statusAssigned => 'Assigné';
|
||||||
@override
|
@override
|
||||||
String get statusInProgress => 'En cours';
|
String get statusInProgress => 'En cours';
|
||||||
@override
|
@override
|
||||||
String get statusCompleted => 'Terminé';
|
String get statusCompleted => 'Terminé';
|
||||||
@override
|
@override
|
||||||
|
String get statusCancelled => 'Annulé';
|
||||||
|
@override
|
||||||
|
String get statusFailed => 'Échoué';
|
||||||
|
@override
|
||||||
String get priorityLow => 'Basse';
|
String get priorityLow => 'Basse';
|
||||||
@override
|
@override
|
||||||
String get priorityMedium => 'Moyenne';
|
String get priorityMedium => 'Moyenne';
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get unknown => 'Nežinoma';
|
String get unknown => 'Nežinoma';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get yesterday => 'Vakar';
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
@override
|
@override
|
||||||
String get jobs => 'Darbai';
|
String get jobs => 'Darbai';
|
||||||
@@ -64,8 +67,30 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
String get loginSubtitle => 'Prisijunkite prie savo paskyros';
|
String get loginSubtitle => 'Prisijunkite prie savo paskyros';
|
||||||
@override
|
@override
|
||||||
String get email => 'El. paštas';
|
String get email => 'El. paštas';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddress => 'El. pašto adresas';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressHint => 'Įveskite savo el. pašto adresą';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressRequired => 'Prašome įvesti savo el. pašto adresą';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressInvalid =>
|
||||||
|
'Prašome įvesti galiojantį el. pašto adresą';
|
||||||
@override
|
@override
|
||||||
String get password => 'Slaptažodis';
|
String get password => 'Slaptažodis';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordHint => 'Įveskite savo slaptažodį';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordRequired => 'Prašome įvesti savo slaptažodį';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordMinLength => 'Slaptažodis turi būti bent 6 simbolių ilgio';
|
||||||
@override
|
@override
|
||||||
String get login => 'Prisijungti';
|
String get login => 'Prisijungti';
|
||||||
@override
|
@override
|
||||||
@@ -73,15 +98,18 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get forgotPassword => 'Pamiršote slaptažodį?';
|
String get forgotPassword => 'Pamiršote slaptažodį?';
|
||||||
@override
|
@override
|
||||||
String get forgotPasswordMessage => 'Pamiršto slaptažodžio funkcija dar neįdiegta';
|
String get forgotPasswordMessage =>
|
||||||
|
'Pamiršto slaptažodžio funkcija dar neįdiegta';
|
||||||
@override
|
@override
|
||||||
String get loginSuccess => 'Sėkmingai atsijungta';
|
String get loginSuccess => 'Sėkmingai atsijungta';
|
||||||
@override
|
@override
|
||||||
String get loginFailed => 'Prisijungimas nepavyko';
|
String get loginFailed => 'Prisijungimas nepavyko';
|
||||||
@override
|
@override
|
||||||
String get connectionFailed => 'Nepavyko prisijungti prie serverio (Laikas baigėsi).';
|
String get connectionFailed =>
|
||||||
|
'Nepavyko prisijungti prie serverio (Laikas baigėsi).';
|
||||||
@override
|
@override
|
||||||
String get connectionTimeout => 'Nepavyko prisijungti prie serverio (Laikas baigėsi).';
|
String get connectionTimeout =>
|
||||||
|
'Nepavyko prisijungti prie serverio (Laikas baigėsi).';
|
||||||
@override
|
@override
|
||||||
String get connecting => 'Jungiamasi prie serverio...';
|
String get connecting => 'Jungiamasi prie serverio...';
|
||||||
@override
|
@override
|
||||||
@@ -149,6 +177,34 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newJobReceived => 'Gautas naujas darbas';
|
String get newJobReceived => 'Gautas naujas darbas';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobDetails => 'Užsakymo detalės';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobTasks => 'Užsakymo užduotys';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deliveryStations => 'Pristatymo stotelės';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String deliveryStationsCount(int count) => 'Pristatymo stotelės ($count)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStations => 'Nėra pristatymo stotelių';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStationsMessage =>
|
||||||
|
'Ši užduotis šiuo metu neturi pristatymo stotelių.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get phone => 'Telefonas';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get unnamedStation => 'Neįvardyta stotelė';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String stationNumber(int number) => 'Stotelė $number';
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
@override
|
@override
|
||||||
String get tasks => 'Užduotys';
|
String get tasks => 'Užduotys';
|
||||||
@@ -161,7 +217,8 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get confirmationRequired => 'Reikalingas patvirtinimas';
|
String get confirmationRequired => 'Reikalingas patvirtinimas';
|
||||||
@override
|
@override
|
||||||
String get confirmationDescription => 'Spustelėkite mygtuką, kad atliktumėte užduotį.';
|
String get confirmationDescription =>
|
||||||
|
'Spustelėkite mygtuką, kad atliktumėte užduotį.';
|
||||||
@override
|
@override
|
||||||
String get checklist => 'Patikros sąrašas';
|
String get checklist => 'Patikros sąrašas';
|
||||||
@override
|
@override
|
||||||
@@ -169,7 +226,8 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get completeTask => 'Baigti užduotį';
|
String get completeTask => 'Baigti užduotį';
|
||||||
@override
|
@override
|
||||||
String get completeTaskConfirm => 'Ar norite pažymėti šią užduotį kaip baigtą?';
|
String get completeTaskConfirm =>
|
||||||
|
'Ar norite pažymėti šią užduotį kaip baigtą?';
|
||||||
@override
|
@override
|
||||||
String get completeTaskNote => 'Pastaba (neprivaloma)';
|
String get completeTaskNote => 'Pastaba (neprivaloma)';
|
||||||
@override
|
@override
|
||||||
@@ -195,7 +253,8 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get signatureError => 'Klaida išsaugant parašą';
|
String get signatureError => 'Klaida išsaugant parašą';
|
||||||
@override
|
@override
|
||||||
String get signatureInstruction => 'Prašome pasirašyti laukelyje žemiau (pele arba pirštu).';
|
String get signatureInstruction =>
|
||||||
|
'Prašome pasirašyti laukelyje žemiau (pele arba pirštu).';
|
||||||
@override
|
@override
|
||||||
String get photoCapture => 'Daryti nuotraukas';
|
String get photoCapture => 'Daryti nuotraukas';
|
||||||
@override
|
@override
|
||||||
@@ -245,9 +304,11 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get barcodeEnterDescription => 'Prašome įvesti brūkšninius kodus:';
|
String get barcodeEnterDescription => 'Prašome įvesti brūkšninius kodus:';
|
||||||
@override
|
@override
|
||||||
String barcodeNumberRequired(int number) => 'Brūkšninis kodas $number (būtinas)';
|
String barcodeNumberRequired(int number) =>
|
||||||
|
'Brūkšninis kodas $number (būtinas)';
|
||||||
@override
|
@override
|
||||||
String barcodeNumberOptional(int number) => 'Brūkšninis kodas $number (neprivalomas)';
|
String barcodeNumberOptional(int number) =>
|
||||||
|
'Brūkšninis kodas $number (neprivalomas)';
|
||||||
@override
|
@override
|
||||||
String get barcodeError => 'Klaida skaitant brūkšninį kodą';
|
String get barcodeError => 'Klaida skaitant brūkšninį kodą';
|
||||||
@override
|
@override
|
||||||
@@ -257,7 +318,8 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraNotAvailable => 'Kamera nepasiekiama';
|
String get cameraNotAvailable => 'Kamera nepasiekiama';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedMessage => 'Šioje platformoje kamera nepalaikoma.';
|
String get cameraNotSupportedMessage =>
|
||||||
|
'Šioje platformoje kamera nepalaikoma.';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedOnPlatform => 'Nepalaikoma šioje platformoje';
|
String get cameraNotSupportedOnPlatform => 'Nepalaikoma šioje platformoje';
|
||||||
@override
|
@override
|
||||||
@@ -273,7 +335,8 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get addPhotos => 'Pridėti nuotraukas';
|
String get addPhotos => 'Pridėti nuotraukas';
|
||||||
@override
|
@override
|
||||||
String get addPhotosInstruction => 'Naudokite mygtuką "Pasirinkti nuotrauką", norėdami pridėti vaizdų iš fotoaparato ar standžiojo disko.';
|
String get addPhotosInstruction =>
|
||||||
|
'Naudokite mygtuką "Pasirinkti nuotrauką", norėdami pridėti vaizdų iš fotoaparato ar standžiojo disko.';
|
||||||
@override
|
@override
|
||||||
String get photoOf => 'iš';
|
String get photoOf => 'iš';
|
||||||
|
|
||||||
@@ -285,7 +348,8 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noSender => 'Siuntėjas nepasiekiamas';
|
String get noSender => 'Siuntėjas nepasiekiamas';
|
||||||
@override
|
@override
|
||||||
String get noSenderMessage => 'Siuntėjas nepasiekiamas. Prašome prisijungti dar kartą.';
|
String get noSenderMessage =>
|
||||||
|
'Siuntėjas nepasiekiamas. Prašome prisijungti dar kartą.';
|
||||||
@override
|
@override
|
||||||
String get noRecipient => 'Gavėjas nesukonfigūruotas';
|
String get noRecipient => 'Gavėjas nesukonfigūruotas';
|
||||||
@override
|
@override
|
||||||
@@ -306,6 +370,15 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
String get jobNumber => 'Darbo numeris';
|
String get jobNumber => 'Darbo numeris';
|
||||||
@override
|
@override
|
||||||
String get messages => 'Žinutės';
|
String get messages => 'Žinutės';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get generalMessages => 'Bendri pranešimai';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noMessagesYet => 'Pranešimų dar nėra';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noChatsAvailable => 'Nėra galimų pokalbių';
|
||||||
@override
|
@override
|
||||||
String get selectPhoto => 'Pasirinkti nuotrauką';
|
String get selectPhoto => 'Pasirinkti nuotrauką';
|
||||||
@override
|
@override
|
||||||
@@ -327,7 +400,8 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noCargoItems => 'Nėra krovinių pozicijų';
|
String get noCargoItems => 'Nėra krovinių pozicijų';
|
||||||
@override
|
@override
|
||||||
String get noCargoItemsMessage => 'Šiam darbui nėra apibrėžtų krovinių pozicijų.';
|
String get noCargoItemsMessage =>
|
||||||
|
'Šiam darbui nėra apibrėžtų krovinių pozicijų.';
|
||||||
@override
|
@override
|
||||||
String get article => 'Pozicija';
|
String get article => 'Pozicija';
|
||||||
|
|
||||||
@@ -369,12 +443,18 @@ class AppLocalizationsLt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCreated => 'Sukurta';
|
String get statusCreated => 'Sukurta';
|
||||||
@override
|
@override
|
||||||
|
String get statusPending => 'Laukiama';
|
||||||
|
@override
|
||||||
String get statusAssigned => 'Priskirta';
|
String get statusAssigned => 'Priskirta';
|
||||||
@override
|
@override
|
||||||
String get statusInProgress => 'Vykdoma';
|
String get statusInProgress => 'Vykdoma';
|
||||||
@override
|
@override
|
||||||
String get statusCompleted => 'Baigta';
|
String get statusCompleted => 'Baigta';
|
||||||
@override
|
@override
|
||||||
|
String get statusCancelled => 'Atšaukta';
|
||||||
|
@override
|
||||||
|
String get statusFailed => 'Nepavyko';
|
||||||
|
@override
|
||||||
String get priorityLow => 'Žemas';
|
String get priorityLow => 'Žemas';
|
||||||
@override
|
@override
|
||||||
String get priorityMedium => 'Vidutinis';
|
String get priorityMedium => 'Vidutinis';
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get unknown => 'Nezināms';
|
String get unknown => 'Nezināms';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get yesterday => 'Vakar';
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
@override
|
@override
|
||||||
String get jobs => 'Darbi';
|
String get jobs => 'Darbi';
|
||||||
@@ -64,8 +67,29 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
String get loginSubtitle => 'Pierakstieties savā kontā';
|
String get loginSubtitle => 'Pierakstieties savā kontā';
|
||||||
@override
|
@override
|
||||||
String get email => 'E-pasts';
|
String get email => 'E-pasts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddress => 'E-pasta adrese';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressHint => 'Ievadiet savu e-pasta adresi';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressRequired => 'Lūdzu, ievadiet savu e-pasta adresi';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressInvalid => 'Lūdzu, ievadiet derīgu e-pasta adresi';
|
||||||
@override
|
@override
|
||||||
String get password => 'Parole';
|
String get password => 'Parole';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordHint => 'Ievadiet savu paroli';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordRequired => 'Lūdzu, ievadiet savu paroli';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordMinLength => 'Parolei jābūt vismaz 6 rakstzīmes garai';
|
||||||
@override
|
@override
|
||||||
String get login => 'Pierakstīties';
|
String get login => 'Pierakstīties';
|
||||||
@override
|
@override
|
||||||
@@ -73,7 +97,8 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get forgotPassword => 'Aizmirsāt paroli?';
|
String get forgotPassword => 'Aizmirsāt paroli?';
|
||||||
@override
|
@override
|
||||||
String get forgotPasswordMessage => 'Aizmirstās paroles funkcija vēl nav ieviesta';
|
String get forgotPasswordMessage =>
|
||||||
|
'Aizmirstās paroles funkcija vēl nav ieviesta';
|
||||||
@override
|
@override
|
||||||
String get loginSuccess => 'Veiksmīgi izrakstījās';
|
String get loginSuccess => 'Veiksmīgi izrakstījās';
|
||||||
@override
|
@override
|
||||||
@@ -149,6 +174,34 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newJobReceived => 'Saņemts jauns darbs';
|
String get newJobReceived => 'Saņemts jauns darbs';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobDetails => 'Darba detaļas';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobTasks => 'Darba uzdevumi';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deliveryStations => 'Piegādes stacijas';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String deliveryStationsCount(int count) => 'Piegādes stacijas ($count)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStations => 'Nav piegādes staciju';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStationsMessage =>
|
||||||
|
'Šajā darbā pašlaik nav piegādes staciju.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get phone => 'Tālrunis';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get unnamedStation => 'Nenosaukta stacija';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String stationNumber(int number) => 'Stacija $number';
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
@override
|
@override
|
||||||
String get tasks => 'Uzdevumi';
|
String get tasks => 'Uzdevumi';
|
||||||
@@ -161,7 +214,8 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get confirmationRequired => 'Nepieciešams apstiprinājums';
|
String get confirmationRequired => 'Nepieciešams apstiprinājums';
|
||||||
@override
|
@override
|
||||||
String get confirmationDescription => 'Noklikšķiniet uz pogas, lai pabeigtu uzdevumu.';
|
String get confirmationDescription =>
|
||||||
|
'Noklikšķiniet uz pogas, lai pabeigtu uzdevumu.';
|
||||||
@override
|
@override
|
||||||
String get checklist => 'Pārbaudes saraksts';
|
String get checklist => 'Pārbaudes saraksts';
|
||||||
@override
|
@override
|
||||||
@@ -169,7 +223,8 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get completeTask => 'Pabeigt uzdevumu';
|
String get completeTask => 'Pabeigt uzdevumu';
|
||||||
@override
|
@override
|
||||||
String get completeTaskConfirm => 'Vai vēlaties atzīmēt šo uzdevumu kā pabeigtu?';
|
String get completeTaskConfirm =>
|
||||||
|
'Vai vēlaties atzīmēt šo uzdevumu kā pabeigtu?';
|
||||||
@override
|
@override
|
||||||
String get completeTaskNote => 'Piezīme (neobligāta)';
|
String get completeTaskNote => 'Piezīme (neobligāta)';
|
||||||
@override
|
@override
|
||||||
@@ -195,7 +250,8 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get signatureError => 'Kļūda saglabājot parakstu';
|
String get signatureError => 'Kļūda saglabājot parakstu';
|
||||||
@override
|
@override
|
||||||
String get signatureInstruction => 'Lūdzu parakstieties zemāk esošajā laukā (pele vai pirksts).';
|
String get signatureInstruction =>
|
||||||
|
'Lūdzu parakstieties zemāk esošajā laukā (pele vai pirksts).';
|
||||||
@override
|
@override
|
||||||
String get photoCapture => 'Uzņemt fotogrāfijas';
|
String get photoCapture => 'Uzņemt fotogrāfijas';
|
||||||
@override
|
@override
|
||||||
@@ -257,7 +313,8 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraNotAvailable => 'Kamera nav pieejama';
|
String get cameraNotAvailable => 'Kamera nav pieejama';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedMessage => 'Šajā platformā kamera netiek atbalstīta.';
|
String get cameraNotSupportedMessage =>
|
||||||
|
'Šajā platformā kamera netiek atbalstīta.';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedOnPlatform => 'Šajā platformā netiek atbalstīts';
|
String get cameraNotSupportedOnPlatform => 'Šajā platformā netiek atbalstīts';
|
||||||
@override
|
@override
|
||||||
@@ -269,11 +326,13 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraInitializing => 'Kamera tiek inicializēta...';
|
String get cameraInitializing => 'Kamera tiek inicializēta...';
|
||||||
@override
|
@override
|
||||||
String get cameraLoadingMessage => 'Lūdzu, uzgaidiet, kamēr kamera tiek ielādēta';
|
String get cameraLoadingMessage =>
|
||||||
|
'Lūdzu, uzgaidiet, kamēr kamera tiek ielādēta';
|
||||||
@override
|
@override
|
||||||
String get addPhotos => 'Pievienot fotogrāfijas';
|
String get addPhotos => 'Pievienot fotogrāfijas';
|
||||||
@override
|
@override
|
||||||
String get addPhotosInstruction => 'Izmantojiet pogu "Izvēlēties fotogrāfiju", lai pievienotu attēlus no kameras vai cietā diska.';
|
String get addPhotosInstruction =>
|
||||||
|
'Izmantojiet pogu "Izvēlēties fotogrāfiju", lai pievienotu attēlus no kameras vai cietā diska.';
|
||||||
@override
|
@override
|
||||||
String get photoOf => 'no';
|
String get photoOf => 'no';
|
||||||
|
|
||||||
@@ -285,7 +344,8 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noSender => 'Sūtītājs nav pieejams';
|
String get noSender => 'Sūtītājs nav pieejams';
|
||||||
@override
|
@override
|
||||||
String get noSenderMessage => 'Sūtītājs nav pieejams. Lūdzu, piesakieties vēlreiz.';
|
String get noSenderMessage =>
|
||||||
|
'Sūtītājs nav pieejams. Lūdzu, piesakieties vēlreiz.';
|
||||||
@override
|
@override
|
||||||
String get noRecipient => 'Saņēmējs nav konfigurēts';
|
String get noRecipient => 'Saņēmējs nav konfigurēts';
|
||||||
@override
|
@override
|
||||||
@@ -306,6 +366,15 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
String get jobNumber => 'Darba numurs';
|
String get jobNumber => 'Darba numurs';
|
||||||
@override
|
@override
|
||||||
String get messages => 'Ziņojumi';
|
String get messages => 'Ziņojumi';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get generalMessages => 'Vispārīgi ziņojumi';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noMessagesYet => 'Ziņojumu vēl nav';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noChatsAvailable => 'Nav pieejamu tērzēšanu';
|
||||||
@override
|
@override
|
||||||
String get selectPhoto => 'Izvēlēties fotogrāfiju';
|
String get selectPhoto => 'Izvēlēties fotogrāfiju';
|
||||||
@override
|
@override
|
||||||
@@ -369,12 +438,18 @@ class AppLocalizationsLv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCreated => 'Izveidots';
|
String get statusCreated => 'Izveidots';
|
||||||
@override
|
@override
|
||||||
|
String get statusPending => 'Gaida';
|
||||||
|
@override
|
||||||
String get statusAssigned => 'Piešķirts';
|
String get statusAssigned => 'Piešķirts';
|
||||||
@override
|
@override
|
||||||
String get statusInProgress => 'Procesā';
|
String get statusInProgress => 'Procesā';
|
||||||
@override
|
@override
|
||||||
String get statusCompleted => 'Pabeigts';
|
String get statusCompleted => 'Pabeigts';
|
||||||
@override
|
@override
|
||||||
|
String get statusCancelled => 'Atcelts';
|
||||||
|
@override
|
||||||
|
String get statusFailed => 'Neizdevās';
|
||||||
|
@override
|
||||||
String get priorityLow => 'Zema';
|
String get priorityLow => 'Zema';
|
||||||
@override
|
@override
|
||||||
String get priorityMedium => 'Vidēja';
|
String get priorityMedium => 'Vidēja';
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get unknown => 'Nieznany';
|
String get unknown => 'Nieznany';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get yesterday => 'Wczoraj';
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
@override
|
@override
|
||||||
String get jobs => 'Zadania';
|
String get jobs => 'Zadania';
|
||||||
@@ -64,8 +67,29 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
String get loginSubtitle => 'Zaloguj się do swojego konta';
|
String get loginSubtitle => 'Zaloguj się do swojego konta';
|
||||||
@override
|
@override
|
||||||
String get email => 'E-mail';
|
String get email => 'E-mail';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddress => 'Adres e-mail';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressHint => 'Wpisz adres e-mail';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressRequired => 'Proszę wpisać adres e-mail';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressInvalid => 'Proszę wpisać prawidłowy adres e-mail';
|
||||||
@override
|
@override
|
||||||
String get password => 'Hasło';
|
String get password => 'Hasło';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordHint => 'Wpisz hasło';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordRequired => 'Proszę wpisać hasło';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordMinLength => 'Hasło musi mieć co najmniej 6 znaków';
|
||||||
@override
|
@override
|
||||||
String get login => 'Zaloguj';
|
String get login => 'Zaloguj';
|
||||||
@override
|
@override
|
||||||
@@ -73,7 +97,8 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get forgotPassword => 'Zapomniałeś hasła?';
|
String get forgotPassword => 'Zapomniałeś hasła?';
|
||||||
@override
|
@override
|
||||||
String get forgotPasswordMessage => 'Funkcja zapomnianego hasła jeszcze nie zaimplementowana';
|
String get forgotPasswordMessage =>
|
||||||
|
'Funkcja zapomnianego hasła jeszcze nie zaimplementowana';
|
||||||
@override
|
@override
|
||||||
String get loginSuccess => 'Pomyślnie wylogowano';
|
String get loginSuccess => 'Pomyślnie wylogowano';
|
||||||
@override
|
@override
|
||||||
@@ -93,7 +118,8 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noJobsAssigned => 'Brak przypisanych zadań';
|
String get noJobsAssigned => 'Brak przypisanych zadań';
|
||||||
@override
|
@override
|
||||||
String get noJobsMessage => 'Twoje przypisane zadania będą wyświetlane tutaj.';
|
String get noJobsMessage =>
|
||||||
|
'Twoje przypisane zadania będą wyświetlane tutaj.';
|
||||||
@override
|
@override
|
||||||
String get pullToRefresh => 'Przeciągnij w dół, aby odświeżyć';
|
String get pullToRefresh => 'Przeciągnij w dół, aby odświeżyć';
|
||||||
@override
|
@override
|
||||||
@@ -149,6 +175,34 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newJobReceived => 'Otrzymano nowe zadanie';
|
String get newJobReceived => 'Otrzymano nowe zadanie';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobDetails => 'Szczegóły zlecenia';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobTasks => 'Zadania zlecenia';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deliveryStations => 'Stacje dostawy';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String deliveryStationsCount(int count) => 'Stacje dostawy ($count)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStations => 'Brak stacji dostawy';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStationsMessage =>
|
||||||
|
'To zlecenie nie zawiera obecnie żadnych stacji dostawy.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get phone => 'Telefon';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get unnamedStation => 'Nienazwana stacja';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String stationNumber(int number) => 'Stacja $number';
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
@override
|
@override
|
||||||
String get tasks => 'Zadania';
|
String get tasks => 'Zadania';
|
||||||
@@ -161,7 +215,8 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get confirmationRequired => 'Wymagane potwierdzenie';
|
String get confirmationRequired => 'Wymagane potwierdzenie';
|
||||||
@override
|
@override
|
||||||
String get confirmationDescription => 'Kliknij przycisk, aby ukończyć zadanie.';
|
String get confirmationDescription =>
|
||||||
|
'Kliknij przycisk, aby ukończyć zadanie.';
|
||||||
@override
|
@override
|
||||||
String get checklist => 'Lista kontrolna';
|
String get checklist => 'Lista kontrolna';
|
||||||
@override
|
@override
|
||||||
@@ -169,7 +224,8 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get completeTask => 'Ukończ zadanie';
|
String get completeTask => 'Ukończ zadanie';
|
||||||
@override
|
@override
|
||||||
String get completeTaskConfirm => 'Czy chcesz oznaczyć to zadanie jako ukończone?';
|
String get completeTaskConfirm =>
|
||||||
|
'Czy chcesz oznaczyć to zadanie jako ukończone?';
|
||||||
@override
|
@override
|
||||||
String get completeTaskNote => 'Notatka (opcjonalnie)';
|
String get completeTaskNote => 'Notatka (opcjonalnie)';
|
||||||
@override
|
@override
|
||||||
@@ -195,7 +251,8 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get signatureError => 'Błąd podczas zapisywania podpisu';
|
String get signatureError => 'Błąd podczas zapisywania podpisu';
|
||||||
@override
|
@override
|
||||||
String get signatureInstruction => 'Proszę podpisać się w polu poniżej (mysz lub palec).';
|
String get signatureInstruction =>
|
||||||
|
'Proszę podpisać się w polu poniżej (mysz lub palec).';
|
||||||
@override
|
@override
|
||||||
String get photoCapture => 'Zrób zdjęcia';
|
String get photoCapture => 'Zrób zdjęcia';
|
||||||
@override
|
@override
|
||||||
@@ -247,7 +304,8 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String barcodeNumberRequired(int number) => 'Kod kreskowy $number (wymagany)';
|
String barcodeNumberRequired(int number) => 'Kod kreskowy $number (wymagany)';
|
||||||
@override
|
@override
|
||||||
String barcodeNumberOptional(int number) => 'Kod kreskowy $number (opcjonalny)';
|
String barcodeNumberOptional(int number) =>
|
||||||
|
'Kod kreskowy $number (opcjonalny)';
|
||||||
@override
|
@override
|
||||||
String get barcodeError => 'Błąd podczas skanowania kodu kreskowego';
|
String get barcodeError => 'Błąd podczas skanowania kodu kreskowego';
|
||||||
@override
|
@override
|
||||||
@@ -257,7 +315,8 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraNotAvailable => 'Kamera niedostępna';
|
String get cameraNotAvailable => 'Kamera niedostępna';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedMessage => 'Kamera nie jest obsługiwana na tej platformie.';
|
String get cameraNotSupportedMessage =>
|
||||||
|
'Kamera nie jest obsługiwana na tej platformie.';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedOnPlatform => 'Nieobsługiwane na tej platformie';
|
String get cameraNotSupportedOnPlatform => 'Nieobsługiwane na tej platformie';
|
||||||
@override
|
@override
|
||||||
@@ -273,7 +332,8 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get addPhotos => 'Dodaj zdjęcia';
|
String get addPhotos => 'Dodaj zdjęcia';
|
||||||
@override
|
@override
|
||||||
String get addPhotosInstruction => 'Użyj przycisku "Wybierz zdjęcie", aby dodać obrazy z kamery lub dysku twardego.';
|
String get addPhotosInstruction =>
|
||||||
|
'Użyj przycisku "Wybierz zdjęcie", aby dodać obrazy z kamery lub dysku twardego.';
|
||||||
@override
|
@override
|
||||||
String get photoOf => 'z';
|
String get photoOf => 'z';
|
||||||
|
|
||||||
@@ -285,11 +345,13 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noSender => 'Brak dostępnego nadawcy';
|
String get noSender => 'Brak dostępnego nadawcy';
|
||||||
@override
|
@override
|
||||||
String get noSenderMessage => 'Brak dostępnego nadawcy. Proszę zalogować się ponownie.';
|
String get noSenderMessage =>
|
||||||
|
'Brak dostępnego nadawcy. Proszę zalogować się ponownie.';
|
||||||
@override
|
@override
|
||||||
String get noRecipient => 'Brak skonfigurowanego odbiorcy';
|
String get noRecipient => 'Brak skonfigurowanego odbiorcy';
|
||||||
@override
|
@override
|
||||||
String get noRecipientMessage => 'Brak skonfigurowanego odbiorcy dla tego czatu.';
|
String get noRecipientMessage =>
|
||||||
|
'Brak skonfigurowanego odbiorcy dla tego czatu.';
|
||||||
@override
|
@override
|
||||||
String get messageSendError => 'Wiadomość nie mogła zostać wysłana.';
|
String get messageSendError => 'Wiadomość nie mogła zostać wysłana.';
|
||||||
@override
|
@override
|
||||||
@@ -306,6 +368,15 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
String get jobNumber => 'Numer zadania';
|
String get jobNumber => 'Numer zadania';
|
||||||
@override
|
@override
|
||||||
String get messages => 'Wiadomości';
|
String get messages => 'Wiadomości';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get generalMessages => 'Wiadomości ogólne';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noMessagesYet => 'Brak wiadomości';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noChatsAvailable => 'Brak dostępnych czatów';
|
||||||
@override
|
@override
|
||||||
String get selectPhoto => 'Wybierz zdjęcie';
|
String get selectPhoto => 'Wybierz zdjęcie';
|
||||||
@override
|
@override
|
||||||
@@ -327,7 +398,8 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noCargoItems => 'Brak pozycji ładunku';
|
String get noCargoItems => 'Brak pozycji ładunku';
|
||||||
@override
|
@override
|
||||||
String get noCargoItemsMessage => 'Brak pozycji ładunku zdefiniowanych dla tego zadania.';
|
String get noCargoItemsMessage =>
|
||||||
|
'Brak pozycji ładunku zdefiniowanych dla tego zadania.';
|
||||||
@override
|
@override
|
||||||
String get article => 'Pozycja';
|
String get article => 'Pozycja';
|
||||||
|
|
||||||
@@ -369,12 +441,18 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCreated => 'Utworzono';
|
String get statusCreated => 'Utworzono';
|
||||||
@override
|
@override
|
||||||
|
String get statusPending => 'Oczekujące';
|
||||||
|
@override
|
||||||
String get statusAssigned => 'Przypisano';
|
String get statusAssigned => 'Przypisano';
|
||||||
@override
|
@override
|
||||||
String get statusInProgress => 'W trakcie';
|
String get statusInProgress => 'W trakcie';
|
||||||
@override
|
@override
|
||||||
String get statusCompleted => 'Ukończono';
|
String get statusCompleted => 'Ukończono';
|
||||||
@override
|
@override
|
||||||
|
String get statusCancelled => 'Anulowano';
|
||||||
|
@override
|
||||||
|
String get statusFailed => 'Nieudane';
|
||||||
|
@override
|
||||||
String get priorityLow => 'Niski';
|
String get priorityLow => 'Niski';
|
||||||
@override
|
@override
|
||||||
String get priorityMedium => 'Średni';
|
String get priorityMedium => 'Średni';
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get unknown => 'Неизвестно';
|
String get unknown => 'Неизвестно';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get yesterday => 'Вчера';
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
@override
|
@override
|
||||||
String get jobs => 'Задания';
|
String get jobs => 'Задания';
|
||||||
@@ -64,8 +67,30 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
String get loginSubtitle => 'Войдите в свою учетную запись';
|
String get loginSubtitle => 'Войдите в свою учетную запись';
|
||||||
@override
|
@override
|
||||||
String get email => 'Эл. почта';
|
String get email => 'Эл. почта';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddress => 'Адрес эл. почты';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressHint => 'Введите адрес эл. почты';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressRequired => 'Пожалуйста, введите адрес эл. почты';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressInvalid =>
|
||||||
|
'Пожалуйста, введите корректный адрес эл. почты';
|
||||||
@override
|
@override
|
||||||
String get password => 'Пароль';
|
String get password => 'Пароль';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordHint => 'Введите пароль';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordRequired => 'Пожалуйста, введите пароль';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordMinLength => 'Пароль должен содержать не менее 6 символов';
|
||||||
@override
|
@override
|
||||||
String get login => 'Войти';
|
String get login => 'Войти';
|
||||||
@override
|
@override
|
||||||
@@ -73,7 +98,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get forgotPassword => 'Забыли пароль?';
|
String get forgotPassword => 'Забыли пароль?';
|
||||||
@override
|
@override
|
||||||
String get forgotPasswordMessage => 'Функция восстановления пароля еще не реализована';
|
String get forgotPasswordMessage =>
|
||||||
|
'Функция восстановления пароля еще не реализована';
|
||||||
@override
|
@override
|
||||||
String get loginSuccess => 'Успешный выход из системы';
|
String get loginSuccess => 'Успешный выход из системы';
|
||||||
@override
|
@override
|
||||||
@@ -93,7 +119,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noJobsAssigned => 'Нет назначенных заданий';
|
String get noJobsAssigned => 'Нет назначенных заданий';
|
||||||
@override
|
@override
|
||||||
String get noJobsMessage => 'Ваши назначенные задания будут отображаться здесь.';
|
String get noJobsMessage =>
|
||||||
|
'Ваши назначенные задания будут отображаться здесь.';
|
||||||
@override
|
@override
|
||||||
String get pullToRefresh => 'Потяните вниз, чтобы обновить';
|
String get pullToRefresh => 'Потяните вниз, чтобы обновить';
|
||||||
@override
|
@override
|
||||||
@@ -137,7 +164,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get jobsUpdated => 'Задания обновлены';
|
String get jobsUpdated => 'Задания обновлены';
|
||||||
@override
|
@override
|
||||||
String get connectionRestored => 'Соединение восстановлено. Загрузка заданий...';
|
String get connectionRestored =>
|
||||||
|
'Соединение восстановлено. Загрузка заданий...';
|
||||||
@override
|
@override
|
||||||
String get connectionLost => 'Соединение потеряно. Офлайн.';
|
String get connectionLost => 'Соединение потеряно. Офлайн.';
|
||||||
@override
|
@override
|
||||||
@@ -149,6 +177,34 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newJobReceived => 'Получено новое задание';
|
String get newJobReceived => 'Получено новое задание';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobDetails => 'Детали заказа';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobTasks => 'Задачи заказа';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deliveryStations => 'Точки доставки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String deliveryStationsCount(int count) => 'Точки доставки ($count)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStations => 'Нет точек доставки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStationsMessage =>
|
||||||
|
'Для этого заказа сейчас нет точек доставки.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get phone => 'Телефон';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get unnamedStation => 'Станция без названия';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String stationNumber(int number) => 'Станция $number';
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
@override
|
@override
|
||||||
String get tasks => 'Задачи';
|
String get tasks => 'Задачи';
|
||||||
@@ -161,7 +217,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get confirmationRequired => 'Требуется подтверждение';
|
String get confirmationRequired => 'Требуется подтверждение';
|
||||||
@override
|
@override
|
||||||
String get confirmationDescription => 'Нажмите кнопку, чтобы выполнить задачу.';
|
String get confirmationDescription =>
|
||||||
|
'Нажмите кнопку, чтобы выполнить задачу.';
|
||||||
@override
|
@override
|
||||||
String get checklist => 'Контрольный список';
|
String get checklist => 'Контрольный список';
|
||||||
@override
|
@override
|
||||||
@@ -169,7 +226,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get completeTask => 'Завершить задачу';
|
String get completeTask => 'Завершить задачу';
|
||||||
@override
|
@override
|
||||||
String get completeTaskConfirm => 'Хотите отметить эту задачу как выполненную?';
|
String get completeTaskConfirm =>
|
||||||
|
'Хотите отметить эту задачу как выполненную?';
|
||||||
@override
|
@override
|
||||||
String get completeTaskNote => 'Примечание (необязательно)';
|
String get completeTaskNote => 'Примечание (необязательно)';
|
||||||
@override
|
@override
|
||||||
@@ -195,7 +253,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get signatureError => 'Ошибка при сохранении подписи';
|
String get signatureError => 'Ошибка при сохранении подписи';
|
||||||
@override
|
@override
|
||||||
String get signatureInstruction => 'Пожалуйста, подпишитесь в поле ниже (мышь или палец).';
|
String get signatureInstruction =>
|
||||||
|
'Пожалуйста, подпишитесь в поле ниже (мышь или палец).';
|
||||||
@override
|
@override
|
||||||
String get photoCapture => 'Сделать фото';
|
String get photoCapture => 'Сделать фото';
|
||||||
@override
|
@override
|
||||||
@@ -247,7 +306,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String barcodeNumberRequired(int number) => 'Штрих-код $number (обязательно)';
|
String barcodeNumberRequired(int number) => 'Штрих-код $number (обязательно)';
|
||||||
@override
|
@override
|
||||||
String barcodeNumberOptional(int number) => 'Штрих-код $number (необязательно)';
|
String barcodeNumberOptional(int number) =>
|
||||||
|
'Штрих-код $number (необязательно)';
|
||||||
@override
|
@override
|
||||||
String get barcodeError => 'Ошибка при сканировании штрих-кода';
|
String get barcodeError => 'Ошибка при сканировании штрих-кода';
|
||||||
@override
|
@override
|
||||||
@@ -257,9 +317,11 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraNotAvailable => 'Камера недоступна';
|
String get cameraNotAvailable => 'Камера недоступна';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedMessage => 'Камера не поддерживается на этой платформе.';
|
String get cameraNotSupportedMessage =>
|
||||||
|
'Камера не поддерживается на этой платформе.';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedOnPlatform => 'Не поддерживается на этой платформе';
|
String get cameraNotSupportedOnPlatform =>
|
||||||
|
'Не поддерживается на этой платформе';
|
||||||
@override
|
@override
|
||||||
String get maxPhotosReached => 'Максимум достигнут';
|
String get maxPhotosReached => 'Максимум достигнут';
|
||||||
@override
|
@override
|
||||||
@@ -269,11 +331,13 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraInitializing => 'Инициализация камеры...';
|
String get cameraInitializing => 'Инициализация камеры...';
|
||||||
@override
|
@override
|
||||||
String get cameraLoadingMessage => 'Пожалуйста, подождите, пока загружается камера';
|
String get cameraLoadingMessage =>
|
||||||
|
'Пожалуйста, подождите, пока загружается камера';
|
||||||
@override
|
@override
|
||||||
String get addPhotos => 'Добавить фото';
|
String get addPhotos => 'Добавить фото';
|
||||||
@override
|
@override
|
||||||
String get addPhotosInstruction => 'Используйте кнопку "Выбрать фото", чтобы добавить изображения с камеры или жёсткого диска.';
|
String get addPhotosInstruction =>
|
||||||
|
'Используйте кнопку "Выбрать фото", чтобы добавить изображения с камеры или жёсткого диска.';
|
||||||
@override
|
@override
|
||||||
String get photoOf => 'из';
|
String get photoOf => 'из';
|
||||||
|
|
||||||
@@ -285,7 +349,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noSender => 'Отправитель недоступен';
|
String get noSender => 'Отправитель недоступен';
|
||||||
@override
|
@override
|
||||||
String get noSenderMessage => 'Отправитель недоступен. Пожалуйста, войдите снова.';
|
String get noSenderMessage =>
|
||||||
|
'Отправитель недоступен. Пожалуйста, войдите снова.';
|
||||||
@override
|
@override
|
||||||
String get noRecipient => 'Получатель не настроен';
|
String get noRecipient => 'Получатель не настроен';
|
||||||
@override
|
@override
|
||||||
@@ -306,6 +371,15 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
String get jobNumber => 'Номер задания';
|
String get jobNumber => 'Номер задания';
|
||||||
@override
|
@override
|
||||||
String get messages => 'Сообщения';
|
String get messages => 'Сообщения';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get generalMessages => 'Общие сообщения';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noMessagesYet => 'Сообщений пока нет';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noChatsAvailable => 'Нет доступных чатов';
|
||||||
@override
|
@override
|
||||||
String get selectPhoto => 'Выбрать фото';
|
String get selectPhoto => 'Выбрать фото';
|
||||||
@override
|
@override
|
||||||
@@ -327,7 +401,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noCargoItems => 'Нет позиций груза';
|
String get noCargoItems => 'Нет позиций груза';
|
||||||
@override
|
@override
|
||||||
String get noCargoItemsMessage => 'Для этого задания не определены позиции груза.';
|
String get noCargoItemsMessage =>
|
||||||
|
'Для этого задания не определены позиции груза.';
|
||||||
@override
|
@override
|
||||||
String get article => 'Позиция';
|
String get article => 'Позиция';
|
||||||
|
|
||||||
@@ -369,12 +444,18 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCreated => 'Создано';
|
String get statusCreated => 'Создано';
|
||||||
@override
|
@override
|
||||||
|
String get statusPending => 'В ожидании';
|
||||||
|
@override
|
||||||
String get statusAssigned => 'Назначено';
|
String get statusAssigned => 'Назначено';
|
||||||
@override
|
@override
|
||||||
String get statusInProgress => 'В процессе';
|
String get statusInProgress => 'В процессе';
|
||||||
@override
|
@override
|
||||||
String get statusCompleted => 'Завершено';
|
String get statusCompleted => 'Завершено';
|
||||||
@override
|
@override
|
||||||
|
String get statusCancelled => 'Отменено';
|
||||||
|
@override
|
||||||
|
String get statusFailed => 'Не удалось';
|
||||||
|
@override
|
||||||
String get priorityLow => 'Низкий';
|
String get priorityLow => 'Низкий';
|
||||||
@override
|
@override
|
||||||
String get priorityMedium => 'Средний';
|
String get priorityMedium => 'Средний';
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get unknown => 'Bilinmiyor';
|
String get unknown => 'Bilinmiyor';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get yesterday => 'Dün';
|
||||||
|
|
||||||
// ==================== NAVIGATION ====================
|
// ==================== NAVIGATION ====================
|
||||||
@override
|
@override
|
||||||
String get jobs => 'İşler';
|
String get jobs => 'İşler';
|
||||||
@@ -64,8 +67,29 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
String get loginSubtitle => 'Hesabınıza giriş yapın';
|
String get loginSubtitle => 'Hesabınıza giriş yapın';
|
||||||
@override
|
@override
|
||||||
String get email => 'E-posta';
|
String get email => 'E-posta';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddress => 'E-posta adresi';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressHint => 'E-posta adresinizi girin';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressRequired => 'Lütfen e-posta adresinizi girin';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get emailAddressInvalid => 'Lütfen geçerli bir e-posta adresi girin';
|
||||||
@override
|
@override
|
||||||
String get password => 'Şifre';
|
String get password => 'Şifre';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordHint => 'Şifrenizi girin';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordRequired => 'Lütfen şifrenizi girin';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get passwordMinLength => 'Şifre en az 6 karakter olmalıdır';
|
||||||
@override
|
@override
|
||||||
String get login => 'Giriş';
|
String get login => 'Giriş';
|
||||||
@override
|
@override
|
||||||
@@ -73,7 +97,8 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get forgotPassword => 'Şifrenizi mi unuttunuz?';
|
String get forgotPassword => 'Şifrenizi mi unuttunuz?';
|
||||||
@override
|
@override
|
||||||
String get forgotPasswordMessage => 'Şifremi unuttum özelliği henüz uygulanmadı';
|
String get forgotPasswordMessage =>
|
||||||
|
'Şifremi unuttum özelliği henüz uygulanmadı';
|
||||||
@override
|
@override
|
||||||
String get loginSuccess => 'Başarıyla çıkış yapıldı';
|
String get loginSuccess => 'Başarıyla çıkış yapıldı';
|
||||||
@override
|
@override
|
||||||
@@ -137,7 +162,8 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get jobsUpdated => 'İşler güncellendi';
|
String get jobsUpdated => 'İşler güncellendi';
|
||||||
@override
|
@override
|
||||||
String get connectionRestored => 'Bağlantı geri yüklendi. İşler yükleniyor...';
|
String get connectionRestored =>
|
||||||
|
'Bağlantı geri yüklendi. İşler yükleniyor...';
|
||||||
@override
|
@override
|
||||||
String get connectionLost => 'Bağlantı kesildi. Çevrimdışı.';
|
String get connectionLost => 'Bağlantı kesildi. Çevrimdışı.';
|
||||||
@override
|
@override
|
||||||
@@ -149,6 +175,34 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newJobReceived => 'Yeni iş alındı';
|
String get newJobReceived => 'Yeni iş alındı';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobDetails => 'İş detayları';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get jobTasks => 'İş görevleri';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deliveryStations => 'Teslimat durakları';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String deliveryStationsCount(int count) => 'Teslimat durakları ($count)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStations => 'Teslimat durağı yok';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noDeliveryStationsMessage =>
|
||||||
|
'Bu iş şu anda hiçbir teslimat durağı içermiyor.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get phone => 'Telefon';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get unnamedStation => 'Adsız durak';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String stationNumber(int number) => 'Durak $number';
|
||||||
|
|
||||||
// ==================== TASKS ====================
|
// ==================== TASKS ====================
|
||||||
@override
|
@override
|
||||||
String get tasks => 'Görevler';
|
String get tasks => 'Görevler';
|
||||||
@@ -161,7 +215,8 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get confirmationRequired => 'Onay gerekli';
|
String get confirmationRequired => 'Onay gerekli';
|
||||||
@override
|
@override
|
||||||
String get confirmationDescription => 'Görevi tamamlamak için butona tıklayın.';
|
String get confirmationDescription =>
|
||||||
|
'Görevi tamamlamak için butona tıklayın.';
|
||||||
@override
|
@override
|
||||||
String get checklist => 'Kontrol listesi';
|
String get checklist => 'Kontrol listesi';
|
||||||
@override
|
@override
|
||||||
@@ -169,7 +224,8 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get completeTask => 'Görevi tamamla';
|
String get completeTask => 'Görevi tamamla';
|
||||||
@override
|
@override
|
||||||
String get completeTaskConfirm => 'Bu görevi tamamlandı olarak işaretlemek istiyor musunuz?';
|
String get completeTaskConfirm =>
|
||||||
|
'Bu görevi tamamlandı olarak işaretlemek istiyor musunuz?';
|
||||||
@override
|
@override
|
||||||
String get completeTaskNote => 'Not (isteğe bağlı)';
|
String get completeTaskNote => 'Not (isteğe bağlı)';
|
||||||
@override
|
@override
|
||||||
@@ -195,7 +251,8 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get signatureError => 'İmza kaydedilirken hata oluştu';
|
String get signatureError => 'İmza kaydedilirken hata oluştu';
|
||||||
@override
|
@override
|
||||||
String get signatureInstruction => 'Lütfen aşağıdaki alana imzanızı atın (fare veya parmak).';
|
String get signatureInstruction =>
|
||||||
|
'Lütfen aşağıdaki alana imzanızı atın (fare veya parmak).';
|
||||||
@override
|
@override
|
||||||
String get photoCapture => 'Fotoğraf çek';
|
String get photoCapture => 'Fotoğraf çek';
|
||||||
@override
|
@override
|
||||||
@@ -221,7 +278,8 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get deletePhoto => 'Fotoğrafı sil';
|
String get deletePhoto => 'Fotoğrafı sil';
|
||||||
@override
|
@override
|
||||||
String get deletePhotoConfirm => 'Bu fotoğrafı gerçekten silmek istiyor musunuz?';
|
String get deletePhotoConfirm =>
|
||||||
|
'Bu fotoğrafı gerçekten silmek istiyor musunuz?';
|
||||||
@override
|
@override
|
||||||
String get barcode => 'Barkod';
|
String get barcode => 'Barkod';
|
||||||
@override
|
@override
|
||||||
@@ -257,7 +315,8 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get cameraNotAvailable => 'Kamera kullanılamıyor';
|
String get cameraNotAvailable => 'Kamera kullanılamıyor';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedMessage => 'Bu platformda kamera desteklenmiyor.';
|
String get cameraNotSupportedMessage =>
|
||||||
|
'Bu platformda kamera desteklenmiyor.';
|
||||||
@override
|
@override
|
||||||
String get cameraNotSupportedOnPlatform => 'Bu platformda desteklenmiyor';
|
String get cameraNotSupportedOnPlatform => 'Bu platformda desteklenmiyor';
|
||||||
@override
|
@override
|
||||||
@@ -273,7 +332,8 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get addPhotos => 'Fotoğraf ekle';
|
String get addPhotos => 'Fotoğraf ekle';
|
||||||
@override
|
@override
|
||||||
String get addPhotosInstruction => 'Kamera veya sabit diskten görüntü eklemek için "Fotoğraf seç" düğmesini kullanın.';
|
String get addPhotosInstruction =>
|
||||||
|
'Kamera veya sabit diskten görüntü eklemek için "Fotoğraf seç" düğmesini kullanın.';
|
||||||
@override
|
@override
|
||||||
String get photoOf => '/';
|
String get photoOf => '/';
|
||||||
|
|
||||||
@@ -285,7 +345,8 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get noSender => 'Gönderen mevcut değil';
|
String get noSender => 'Gönderen mevcut değil';
|
||||||
@override
|
@override
|
||||||
String get noSenderMessage => 'Gönderen mevcut değil. Lütfen tekrar giriş yapın.';
|
String get noSenderMessage =>
|
||||||
|
'Gönderen mevcut değil. Lütfen tekrar giriş yapın.';
|
||||||
@override
|
@override
|
||||||
String get noRecipient => 'Alıcı yapılandırılmamış';
|
String get noRecipient => 'Alıcı yapılandırılmamış';
|
||||||
@override
|
@override
|
||||||
@@ -306,6 +367,15 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
String get jobNumber => 'İş numarası';
|
String get jobNumber => 'İş numarası';
|
||||||
@override
|
@override
|
||||||
String get messages => 'Mesajlar';
|
String get messages => 'Mesajlar';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get generalMessages => 'Genel mesajlar';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noMessagesYet => 'Henüz mesaj yok';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get noChatsAvailable => 'Kullanılabilir sohbet yok';
|
||||||
@override
|
@override
|
||||||
String get selectPhoto => 'Fotoğraf seç';
|
String get selectPhoto => 'Fotoğraf seç';
|
||||||
@override
|
@override
|
||||||
@@ -369,12 +439,18 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get statusCreated => 'Oluşturuldu';
|
String get statusCreated => 'Oluşturuldu';
|
||||||
@override
|
@override
|
||||||
|
String get statusPending => 'Beklemede';
|
||||||
|
@override
|
||||||
String get statusAssigned => 'Atandı';
|
String get statusAssigned => 'Atandı';
|
||||||
@override
|
@override
|
||||||
String get statusInProgress => 'Devam ediyor';
|
String get statusInProgress => 'Devam ediyor';
|
||||||
@override
|
@override
|
||||||
String get statusCompleted => 'Tamamlandı';
|
String get statusCompleted => 'Tamamlandı';
|
||||||
@override
|
@override
|
||||||
|
String get statusCancelled => 'İptal edildi';
|
||||||
|
@override
|
||||||
|
String get statusFailed => 'Başarısız';
|
||||||
|
@override
|
||||||
String get priorityLow => 'Düşük';
|
String get priorityLow => 'Düşük';
|
||||||
@override
|
@override
|
||||||
String get priorityMedium => 'Orta';
|
String get priorityMedium => 'Orta';
|
||||||
|
|||||||
50
app/lib/l10n/localization_helpers.dart
Normal file
50
app/lib/l10n/localization_helpers.dart
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import '../models/chat.dart';
|
||||||
|
import 'app_localizations.dart';
|
||||||
|
|
||||||
|
String localizeKnownText(BuildContext context, String text) {
|
||||||
|
final l10n = AppLocalizations.of(context);
|
||||||
|
switch (text.trim()) {
|
||||||
|
case 'Auftragsdetails':
|
||||||
|
return l10n.jobDetails;
|
||||||
|
case 'Aufgaben eines Auftrags':
|
||||||
|
return l10n.jobTasks;
|
||||||
|
case 'Unterschrift':
|
||||||
|
return l10n.signature;
|
||||||
|
case 'Allgemeine Nachrichten':
|
||||||
|
return l10n.generalMessages;
|
||||||
|
case 'Telefon':
|
||||||
|
return l10n.phone;
|
||||||
|
case 'Erstellt':
|
||||||
|
return l10n.created;
|
||||||
|
case 'E-Mail-Adresse':
|
||||||
|
return l10n.emailAddress;
|
||||||
|
case 'Passwort':
|
||||||
|
return l10n.password;
|
||||||
|
case 'Anmelden':
|
||||||
|
return l10n.login;
|
||||||
|
default:
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String localizedStationLabel(
|
||||||
|
BuildContext context,
|
||||||
|
int number, {
|
||||||
|
String? suffix,
|
||||||
|
}) {
|
||||||
|
final base = AppLocalizations.of(context).stationNumber(number);
|
||||||
|
final trimmedSuffix = suffix?.trim() ?? '';
|
||||||
|
if (trimmedSuffix.isEmpty) {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
return '$base: ${localizeKnownText(context, trimmedSuffix)}';
|
||||||
|
}
|
||||||
|
|
||||||
|
String localizedChatTitle(BuildContext context, Chat chat) {
|
||||||
|
if (chat.type == ChatType.general) {
|
||||||
|
return AppLocalizations.of(context).generalMessages;
|
||||||
|
}
|
||||||
|
return localizeKnownText(context, chat.title);
|
||||||
|
}
|
||||||
@@ -34,6 +34,8 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
bool _logoutNoticeShown = false;
|
bool _logoutNoticeShown = false;
|
||||||
bool _hasNavigatedToJobs = false;
|
bool _hasNavigatedToJobs = false;
|
||||||
String _appVersion = '';
|
String _appVersion = '';
|
||||||
|
String? _pendingLoginEmail;
|
||||||
|
String? _pendingLoginPassword;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -52,7 +54,13 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
_logoutNoticeShown = true;
|
_logoutNoticeShown = true;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).loginSuccess), backgroundColor: Colors.green, duration: const Duration(seconds: 1)));
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(AppLocalizations.of(context).loginSuccess),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
duration: const Duration(seconds: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,23 +94,66 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
// Listen to connection status changes via dart_mq
|
// Listen to connection status changes via dart_mq
|
||||||
// Note: Don't reset _isLoggingIn here - the login flow in _handleLogin
|
// Note: Don't reset _isLoggingIn here - the login flow in _handleLogin
|
||||||
// manages button state through its own error/success handling.
|
// manages button state through its own error/success handling.
|
||||||
_connectionStatusSubscription = DartMQ().subscribe<bool>(MQTopics.connectionStatus, (isConnected) {
|
_connectionStatusSubscription = DartMQ().subscribe<bool>(
|
||||||
|
MQTopics.connectionStatus,
|
||||||
|
(isConnected) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Listen to authentication responses via dart_mq
|
// Listen to authentication responses via dart_mq
|
||||||
_authResponseSubscription = DartMQ().subscribe<Map<String, dynamic>>(MQTopics.authResponse, (response) {
|
_authResponseSubscription = DartMQ().subscribe<Map<String, dynamic>>(
|
||||||
|
MQTopics.authResponse,
|
||||||
|
(response) {
|
||||||
final responseTime = DateTime.now();
|
final responseTime = DateTime.now();
|
||||||
developer.log('=== AUTHENTICATION RESPONSE RECEIVED ===', name: 'LoginView');
|
developer.log(
|
||||||
developer.log('Timestamp: ${responseTime.toIso8601String()}', name: 'LoginView');
|
'=== AUTHENTICATION RESPONSE RECEIVED ===',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
|
developer.log(
|
||||||
|
'Timestamp: ${responseTime.toIso8601String()}',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
developer.log('Response data: $response', name: 'LoginView');
|
developer.log('Response data: $response', name: 'LoginView');
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_handleAuthResponse(response);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
developer.log(
|
||||||
|
'Widget not mounted - skipping UI updates for auth response',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
developer.log(
|
||||||
|
'Authentication response processing completed',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _clearPendingLoginCredentials() {
|
||||||
|
_pendingLoginEmail = null;
|
||||||
|
_pendingLoginPassword = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleAuthResponse(Map<String, dynamic> response) async {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
|
final pendingEmail = _pendingLoginEmail?.trim();
|
||||||
|
final pendingPassword = _pendingLoginPassword;
|
||||||
|
final hadPendingLogin =
|
||||||
|
pendingEmail != null &&
|
||||||
|
pendingEmail.isNotEmpty &&
|
||||||
|
pendingPassword != null &&
|
||||||
|
pendingPassword.isNotEmpty;
|
||||||
|
_clearPendingLoginCredentials();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoggingIn = false;
|
_isLoggingIn = false;
|
||||||
});
|
});
|
||||||
@@ -110,29 +161,52 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
if (response['success'] == true) {
|
if (response['success'] == true) {
|
||||||
// Prevent duplicate navigation from multiple auth responses
|
// Prevent duplicate navigation from multiple auth responses
|
||||||
if (_hasNavigatedToJobs) {
|
if (_hasNavigatedToJobs) {
|
||||||
developer.log('Already navigated to jobs view - ignoring duplicate auth response', name: 'LoginView');
|
developer.log(
|
||||||
|
'Already navigated to jobs view - ignoring duplicate auth response',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_hasNavigatedToJobs = true;
|
|
||||||
|
|
||||||
final message = response['message'] ?? 'Anmeldung erfolgreich';
|
final message = response['message'] ?? 'Anmeldung erfolgreich';
|
||||||
final email = _emailController.text.trim();
|
final savedCredentials = await DatabaseService().loadCredentials();
|
||||||
final password = _passwordController.text;
|
final effectiveEmail =
|
||||||
|
(pendingEmail != null && pendingEmail.isNotEmpty)
|
||||||
|
? pendingEmail
|
||||||
|
: (savedCredentials?.email ??
|
||||||
|
_appState.loggedInEmail ??
|
||||||
|
_emailController.text.trim());
|
||||||
|
final effectivePassword =
|
||||||
|
(pendingPassword != null && pendingPassword.isNotEmpty)
|
||||||
|
? pendingPassword
|
||||||
|
: (savedCredentials?.password ?? _passwordController.text);
|
||||||
|
|
||||||
developer.log('=== LOGIN SUCCESS ===', name: 'LoginView');
|
developer.log('=== LOGIN SUCCESS ===', name: 'LoginView');
|
||||||
developer.log('Email: $email', name: 'LoginView');
|
developer.log('Email: $effectiveEmail', name: 'LoginView');
|
||||||
developer.log('Message: $message', name: 'LoginView');
|
developer.log('Message: $message', name: 'LoginView');
|
||||||
|
|
||||||
// Store email as login identifier
|
if (effectiveEmail.isNotEmpty) {
|
||||||
_appState.setLoggedInEmail(email);
|
_appState.setLoggedInEmail(effectiveEmail);
|
||||||
|
}
|
||||||
|
if (effectiveEmail.isNotEmpty && effectivePassword.isNotEmpty) {
|
||||||
|
await DatabaseService().saveCredentials(
|
||||||
|
effectiveEmail,
|
||||||
|
effectivePassword,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Save credentials for auto-login on app restart
|
if (!mounted) return;
|
||||||
DatabaseService().saveCredentials(email, password);
|
_hasNavigatedToJobs = true;
|
||||||
|
|
||||||
// Navigate directly to jobs view - jobs will be loaded there
|
// Navigate directly to jobs view - jobs will be loaded there
|
||||||
developer.log('Navigating to jobs view - jobs will be loaded there...', name: 'LoginView');
|
developer.log(
|
||||||
|
'Navigating to jobs view - jobs will be loaded there...',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
Navigator.of(context).pushReplacementNamed('/jobs');
|
Navigator.of(context).pushReplacementNamed('/jobs');
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final errorMessage = response['message'] ?? 'Unbekannter Fehler';
|
final errorMessage = response['message'] ?? 'Unbekannter Fehler';
|
||||||
final errorCode = response['code'] ?? 'No code';
|
final errorCode = response['code'] ?? 'No code';
|
||||||
|
|
||||||
@@ -141,15 +215,23 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
developer.log('Error code: $errorCode', name: 'LoginView');
|
developer.log('Error code: $errorCode', name: 'LoginView');
|
||||||
developer.log('Full error response: $response', name: 'LoginView');
|
developer.log('Full error response: $response', name: 'LoginView');
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('${AppLocalizations.of(context).loginFailed}: $errorMessage'), backgroundColor: Colors.red, duration: const Duration(seconds: 1)));
|
if (!hadPendingLogin || !mounted) {
|
||||||
}
|
developer.log(
|
||||||
});
|
'Ignoring auth failure in LoginView because no manual login attempt is pending',
|
||||||
} else {
|
name: 'LoginView',
|
||||||
developer.log('Widget not mounted - skipping UI updates for auth response', name: 'LoginView');
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
developer.log('Authentication response processing completed', name: 'LoginView');
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
});
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
'${AppLocalizations.of(context).loginFailed}: $errorMessage',
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
duration: const Duration(seconds: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleLogin() async {
|
Future<void> _handleLogin() async {
|
||||||
@@ -158,34 +240,62 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
|
|
||||||
developer.log('=== LOGIN ATTEMPT STARTED ===', name: 'LoginView');
|
developer.log('=== LOGIN ATTEMPT STARTED ===', name: 'LoginView');
|
||||||
developer.log('Session ID: $sessionId', name: 'LoginView');
|
developer.log('Session ID: $sessionId', name: 'LoginView');
|
||||||
developer.log('Timestamp: ${loginStartTime.toIso8601String()}', name: 'LoginView');
|
developer.log(
|
||||||
|
'Timestamp: ${loginStartTime.toIso8601String()}',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
|
|
||||||
if (!_formKey.currentState!.validate()) {
|
if (!_formKey.currentState!.validate()) {
|
||||||
developer.log('Login validation failed - form is invalid', name: 'LoginView');
|
developer.log(
|
||||||
|
'Login validation failed - form is invalid',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_isLoggingIn) {
|
if (_isLoggingIn) {
|
||||||
developer.log('Login already in progress - ignoring duplicate request', name: 'LoginView');
|
developer.log(
|
||||||
|
'Login already in progress - ignoring duplicate request',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String email = _emailController.text.trim();
|
String email = _emailController.text.trim();
|
||||||
|
String password = _passwordController.text;
|
||||||
|
_pendingLoginEmail = email;
|
||||||
|
_pendingLoginPassword = password;
|
||||||
|
|
||||||
developer.log('Login attempt for email: $email', name: 'LoginView');
|
developer.log('Login attempt for email: $email', name: 'LoginView');
|
||||||
developer.log('Password length: ${_passwordController.text.length} characters', name: 'LoginView');
|
developer.log(
|
||||||
|
'Password length: ${_passwordController.text.length} characters',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
|
|
||||||
// Capture ScaffoldMessenger and localizations before any async operations
|
// Capture ScaffoldMessenger and localizations before any async operations
|
||||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||||
final localizations = AppLocalizations.of(context);
|
final localizations = AppLocalizations.of(context);
|
||||||
|
|
||||||
if (!_stompService.isConnected) {
|
if (!_stompService.isConnected) {
|
||||||
developer.log('Not connected to STOMP server - establishing connection first', name: 'LoginView');
|
developer.log(
|
||||||
developer.log('STOMP service connection state: ${_stompService.isConnected}', name: 'LoginView');
|
'Not connected to STOMP server - establishing connection first',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
|
developer.log(
|
||||||
|
'STOMP service connection state: ${_stompService.isConnected}',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
|
|
||||||
// Always attempt connection to fixed STOMP endpoint (no discovery gating)
|
// Always attempt connection to fixed STOMP endpoint (no discovery gating)
|
||||||
// Show connecting message
|
// Show connecting message
|
||||||
if (!widget.suppressConnectionSnack) {
|
if (!widget.suppressConnectionSnack) {
|
||||||
scaffoldMessenger.showSnackBar(SnackBar(content: Text(localizations.connecting), backgroundColor: Colors.blue, duration: const Duration(seconds: 1)));
|
scaffoldMessenger.showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(localizations.connecting),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
duration: const Duration(seconds: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set loading state
|
// Set loading state
|
||||||
@@ -202,20 +312,29 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
// Wait for connection to be established with a timeout
|
// Wait for connection to be established with a timeout
|
||||||
try {
|
try {
|
||||||
final completer = Completer<bool>();
|
final completer = Completer<bool>();
|
||||||
final subscription = DartMQ().subscribe<bool>(MQTopics.connectionStatus, (isConnected) {
|
final subscription = DartMQ().subscribe<bool>(
|
||||||
|
MQTopics.connectionStatus,
|
||||||
|
(isConnected) {
|
||||||
if (isConnected && !completer.isCompleted) {
|
if (isConnected && !completer.isCompleted) {
|
||||||
completer.complete(true);
|
completer.complete(true);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
await completer.future.timeout(const Duration(seconds: 12));
|
await completer.future.timeout(const Duration(seconds: 12));
|
||||||
subscription.cancel();
|
subscription.cancel();
|
||||||
developer.log('STOMP connection established - proceeding with login', name: 'LoginView');
|
developer.log(
|
||||||
|
'STOMP connection established - proceeding with login',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
} on TimeoutException {
|
} on TimeoutException {
|
||||||
developer.log('STOMP connection timed out', name: 'LoginView');
|
developer.log('STOMP connection timed out', name: 'LoginView');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
developer.log('STOMP already connected after connect - proceeding with login', name: 'LoginView');
|
developer.log(
|
||||||
|
'STOMP already connected after connect - proceeding with login',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if connection was successful
|
// Check if connection was successful
|
||||||
@@ -223,43 +342,74 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_isLoggingIn = false;
|
_isLoggingIn = false;
|
||||||
});
|
});
|
||||||
scaffoldMessenger.showSnackBar(SnackBar(content: Text(localizations.connectionTimeout), backgroundColor: Colors.red, duration: const Duration(seconds: 2)));
|
scaffoldMessenger.showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(localizations.connectionTimeout),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_clearPendingLoginCredentials();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoggingIn = false;
|
_isLoggingIn = false;
|
||||||
});
|
});
|
||||||
developer.log('Error connecting to STOMP server: $e', name: 'LoginView');
|
developer.log(
|
||||||
|
'Error connecting to STOMP server: $e',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
developer.log('Stack trace: $stackTrace', name: 'LoginView');
|
developer.log('Stack trace: $stackTrace', name: 'LoginView');
|
||||||
scaffoldMessenger.showSnackBar(SnackBar(content: Text('${localizations.connectionError}: $e'), backgroundColor: Colors.red, duration: const Duration(seconds: 1)));
|
scaffoldMessenger.showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('${localizations.connectionError}: $e'),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
duration: const Duration(seconds: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_clearPendingLoginCredentials();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
developer.log('Pre-login checks passed - initiating login request', name: 'LoginView');
|
developer.log(
|
||||||
developer.log('Connection status: connected=${_stompService.isConnected}', name: 'LoginView');
|
'Pre-login checks passed - initiating login request',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
|
developer.log(
|
||||||
|
'Connection status: connected=${_stompService.isConnected}',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoggingIn = true;
|
_isLoggingIn = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
String password = _passwordController.text;
|
developer.log(
|
||||||
|
'Sending login request via STOMP service...',
|
||||||
developer.log('Sending login request via STOMP service...', name: 'LoginView');
|
name: 'LoginView',
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Send login request via STOMP
|
// Send login request via STOMP
|
||||||
await _stompService.login(email, password);
|
await _stompService.login(email, password);
|
||||||
|
|
||||||
final requestSentTime = DateTime.now();
|
final requestSentTime = DateTime.now();
|
||||||
final requestDuration = requestSentTime.difference(loginStartTime).inMilliseconds;
|
final requestDuration =
|
||||||
developer.log('Login request sent successfully after ${requestDuration}ms', name: 'LoginView');
|
requestSentTime.difference(loginStartTime).inMilliseconds;
|
||||||
|
developer.log(
|
||||||
|
'Login request sent successfully after ${requestDuration}ms',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
final errorTime = DateTime.now();
|
final errorTime = DateTime.now();
|
||||||
final errorDuration = errorTime.difference(loginStartTime).inMilliseconds;
|
final errorDuration = errorTime.difference(loginStartTime).inMilliseconds;
|
||||||
|
|
||||||
developer.log('LOGIN ERROR: Exception during login request after ${errorDuration}ms', name: 'LoginView');
|
developer.log(
|
||||||
|
'LOGIN ERROR: Exception during login request after ${errorDuration}ms',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
developer.log('Error: $e', name: 'LoginView');
|
developer.log('Error: $e', name: 'LoginView');
|
||||||
developer.log('Stack trace: $stackTrace', name: 'LoginView');
|
developer.log('Stack trace: $stackTrace', name: 'LoginView');
|
||||||
|
|
||||||
@@ -267,16 +417,28 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
_isLoggingIn = false;
|
_isLoggingIn = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
scaffoldMessenger.showSnackBar(SnackBar(content: Text('${localizations.loginError}: $e'), backgroundColor: Colors.red, duration: const Duration(seconds: 1)));
|
scaffoldMessenger.showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('${localizations.loginError}: $e'),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
duration: const Duration(seconds: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_clearPendingLoginCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The auth response will be handled by the stream listener
|
// The auth response will be handled by the stream listener
|
||||||
// _isLoggingIn will be set to false in the listener
|
// _isLoggingIn will be set to false in the listener
|
||||||
developer.log('Login request phase completed - waiting for auth response', name: 'LoginView');
|
developer.log(
|
||||||
|
'Login request phase completed - waiting for auth response',
|
||||||
|
name: 'LoginView',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = AppLocalizations.of(context);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.grey[50],
|
backgroundColor: Colors.grey[50],
|
||||||
body: Column(
|
body: Column(
|
||||||
@@ -293,25 +455,54 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
// Logo oder App-Name
|
// Logo oder App-Name
|
||||||
Icon(Icons.account_circle, size: 100, color: Colors.deepPurple),
|
Icon(
|
||||||
|
Icons.account_circle,
|
||||||
|
size: 100,
|
||||||
|
color: Colors.deepPurple,
|
||||||
|
),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
Text(AppLocalizations.of(context).welcomeBack, style: Theme.of(context).textTheme.headlineMedium?.copyWith(fontWeight: FontWeight.bold, color: Colors.grey[800]), textAlign: TextAlign.center),
|
Text(
|
||||||
|
l10n.welcomeBack,
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.headlineMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey[800],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
Text(AppLocalizations.of(context).loginSubtitle, style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Colors.grey[600]), textAlign: TextAlign.center),
|
Text(
|
||||||
|
l10n.loginSubtitle,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge
|
||||||
|
?.copyWith(color: Colors.grey[600]),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
// E-Mail-Feld
|
// E-Mail-Feld
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _emailController,
|
controller: _emailController,
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
decoration: InputDecoration(labelText: 'E-Mail-Adresse', hintText: 'Geben Sie Ihre E-Mail-Adresse ein', prefixIcon: const Icon(Icons.email_outlined), border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), filled: true, fillColor: Colors.white),
|
decoration: InputDecoration(
|
||||||
|
labelText: l10n.emailAddress,
|
||||||
|
hintText: l10n.emailAddressHint,
|
||||||
|
prefixIcon: const Icon(Icons.email_outlined),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.white,
|
||||||
|
),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Bitte geben Sie Ihre E-Mail-Adresse ein';
|
return l10n.emailAddressRequired;
|
||||||
}
|
}
|
||||||
if (!RegExp(r'^[A-Za-z0-9_.+-]+@[A-Za-z0-9-]+\.[A-Za-z0-9.-]+$').hasMatch(value)) {
|
if (!RegExp(
|
||||||
return 'Bitte geben Sie eine gültige E-Mail-Adresse ein';
|
r'^[A-Za-z0-9_.+-]+@[A-Za-z0-9-]+\.[A-Za-z0-9.-]+$',
|
||||||
|
).hasMatch(value)) {
|
||||||
|
return l10n.emailAddressInvalid;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
@@ -323,27 +514,33 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
obscureText: !_isPasswordVisible,
|
obscureText: !_isPasswordVisible,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Passwort',
|
labelText: l10n.password,
|
||||||
hintText: 'Geben Sie Ihr Passwort ein',
|
hintText: l10n.passwordHint,
|
||||||
prefixIcon: const Icon(Icons.lock_outlined),
|
prefixIcon: const Icon(Icons.lock_outlined),
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
icon: Icon(_isPasswordVisible ? Icons.visibility : Icons.visibility_off),
|
icon: Icon(
|
||||||
|
_isPasswordVisible
|
||||||
|
? Icons.visibility
|
||||||
|
: Icons.visibility_off,
|
||||||
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isPasswordVisible = !_isPasswordVisible;
|
_isPasswordVisible = !_isPasswordVisible;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
),
|
),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Bitte geben Sie Ihr Passwort ein';
|
return l10n.passwordRequired;
|
||||||
}
|
}
|
||||||
if (value.length < 6) {
|
if (value.length < 6) {
|
||||||
return 'Das Passwort muss mindestens 6 Zeichen lang sein';
|
return l10n.passwordMinLength;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
@@ -356,16 +553,71 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Hier würde die "Passwort vergessen" Funktionalität implementiert werden
|
// Hier würde die "Passwort vergessen" Funktionalität implementiert werden
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).forgotPasswordMessage), duration: const Duration(seconds: 1)));
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(l10n.forgotPasswordMessage),
|
||||||
|
duration: const Duration(seconds: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Text(AppLocalizations.of(context).forgotPassword, style: const TextStyle(color: Colors.deepPurple, fontWeight: FontWeight.w500)),
|
child: Text(
|
||||||
|
l10n.forgotPassword,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.deepPurple,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
// Verbindungsstatus
|
// Verbindungsstatus
|
||||||
// Anmelden Button
|
// Anmelden Button
|
||||||
ElevatedButton(onPressed: _isLoggingIn ? null : _handleLogin, style: ElevatedButton.styleFrom(backgroundColor: Colors.deepPurple, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), elevation: 2), child: _isLoggingIn ? Row(mainAxisAlignment: MainAxisAlignment.center, children: const [SizedBox(width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2.5, valueColor: AlwaysStoppedAnimation<Color>(Colors.white))), SizedBox(width: 12), Text('Verbinden…', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600))]) : const Text('Anmelden', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600))),
|
ElevatedButton(
|
||||||
|
onPressed: _isLoggingIn ? null : _handleLogin,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.deepPurple,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
_isLoggingIn
|
||||||
|
? Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2.5,
|
||||||
|
valueColor:
|
||||||
|
AlwaysStoppedAnimation<Color>(
|
||||||
|
Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
l10n.loggingIn,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
l10n.login,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -375,7 +627,15 @@ class _LoginViewState extends State<LoginView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Version number at the bottom
|
// Version number at the bottom
|
||||||
if (_appVersion.isNotEmpty) Padding(padding: const EdgeInsets.only(bottom: 16.0), child: Text('Version $_appVersion', style: TextStyle(fontSize: 12, color: Colors.grey[500]), textAlign: TextAlign.center)),
|
if (_appVersion.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
|
child: Text(
|
||||||
|
'Version $_appVersion',
|
||||||
|
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import 'services/chat_service.dart';
|
|||||||
import 'app_state.dart';
|
import 'app_state.dart';
|
||||||
import 'navigation_observer.dart';
|
import 'navigation_observer.dart';
|
||||||
import 'services/notification_service.dart';
|
import 'services/notification_service.dart';
|
||||||
|
import 'services/websocket_service.dart';
|
||||||
import 'l10n/app_localizations.dart';
|
import 'l10n/app_localizations.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
@@ -43,14 +44,59 @@ void main() async {
|
|||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatefulWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyApp> createState() => _MyAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
||||||
|
static const Duration _resumeReconnectThreshold = Duration(seconds: 30);
|
||||||
|
final AppState _appState = AppState();
|
||||||
|
final WebSocketService _webSocketService = WebSocketService();
|
||||||
|
DateTime? _lastPausedAt;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
if (state == AppLifecycleState.paused) {
|
||||||
|
_lastPausedAt = DateTime.now();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == AppLifecycleState.resumed) {
|
||||||
|
final pausedAt = _lastPausedAt;
|
||||||
|
_lastPausedAt = null;
|
||||||
|
if (pausedAt == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final standbyDuration = DateTime.now().difference(pausedAt);
|
||||||
|
if (standbyDuration < _resumeReconnectThreshold ||
|
||||||
|
!_appState.isLoggedIn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_webSocketService.reconnectForAppResume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Check if user is already logged in
|
// Check if user is already logged in
|
||||||
final appState = AppState();
|
final initialRoute = _appState.isLoggedIn ? '/jobs' : '/login';
|
||||||
final initialRoute = appState.isLoggedIn ? '/jobs' : '/login';
|
|
||||||
|
|
||||||
return ValueListenableBuilder<Locale>(
|
return ValueListenableBuilder<Locale>(
|
||||||
valueListenable: localeNotifier,
|
valueListenable: localeNotifier,
|
||||||
@@ -58,11 +104,20 @@ class MyApp extends StatelessWidget {
|
|||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'VotianLT App',
|
title: 'VotianLT App',
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true),
|
theme: ThemeData(
|
||||||
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
// Localization configuration
|
// Localization configuration
|
||||||
locale: locale,
|
locale: locale,
|
||||||
localizationsDelegates: const [AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate],
|
localizationsDelegates: const [
|
||||||
supportedLocales: supportedLanguageCodes.map((code) => Locale(code)).toList(),
|
AppLocalizations.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales:
|
||||||
|
supportedLanguageCodes.map((code) => Locale(code)).toList(),
|
||||||
navigatorObservers: [routeObserver],
|
navigatorObservers: [routeObserver],
|
||||||
initialRoute: initialRoute,
|
initialRoute: initialRoute,
|
||||||
onGenerateRoute: (settings) {
|
onGenerateRoute: (settings) {
|
||||||
@@ -70,21 +125,30 @@ class MyApp extends StatelessWidget {
|
|||||||
case '/login':
|
case '/login':
|
||||||
final arg = settings.arguments;
|
final arg = settings.arguments;
|
||||||
final suppress = (arg is bool) ? arg : false;
|
final suppress = (arg is bool) ? arg : false;
|
||||||
return MaterialPageRoute(builder: (_) => LoginView(suppressConnectionSnack: suppress));
|
return MaterialPageRoute(
|
||||||
|
builder: (_) => LoginView(suppressConnectionSnack: suppress),
|
||||||
|
);
|
||||||
case '/jobs':
|
case '/jobs':
|
||||||
return MaterialPageRoute(builder: (_) => const JobsView());
|
return MaterialPageRoute(builder: (_) => const JobsView());
|
||||||
case '/cargo_items':
|
case '/cargo_items':
|
||||||
final job = settings.arguments as Job;
|
final job = settings.arguments as Job;
|
||||||
return MaterialPageRoute(builder: (_) => CargoItemsView(job: job));
|
return MaterialPageRoute(
|
||||||
|
builder: (_) => CargoItemsView(job: job),
|
||||||
|
);
|
||||||
case '/chats':
|
case '/chats':
|
||||||
return MaterialPageRoute(builder: (_) => const ChatsView());
|
return MaterialPageRoute(builder: (_) => const ChatsView());
|
||||||
case '/chat_details':
|
case '/chat_details':
|
||||||
final chat = settings.arguments as Chat;
|
final chat = settings.arguments as Chat;
|
||||||
return MaterialPageRoute(builder: (_) => ChatDetailsView(chat: chat));
|
return MaterialPageRoute(
|
||||||
|
builder: (_) => ChatDetailsView(chat: chat),
|
||||||
|
);
|
||||||
case '/settings':
|
case '/settings':
|
||||||
return MaterialPageRoute(builder: (_) => const SettingsView());
|
return MaterialPageRoute(builder: (_) => const SettingsView());
|
||||||
default:
|
default:
|
||||||
return MaterialPageRoute(builder: (_) => const LoginView(suppressConnectionSnack: false));
|
return MaterialPageRoute(
|
||||||
|
builder:
|
||||||
|
(_) => const LoginView(suppressConnectionSnack: false),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -114,9 +178,27 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title)),
|
appBar: AppBar(
|
||||||
body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[const Text('You have pushed the button this many times:'), Text('$_counter', style: Theme.of(context).textTheme.headlineMedium)])),
|
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||||
floatingActionButton: FloatingActionButton(onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add)), // This trailing comma makes auto-formatting nicer for build methods.
|
title: Text(widget.title),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
const Text('You have pushed the button this many times:'),
|
||||||
|
Text(
|
||||||
|
'$_counter',
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: _incrementCounter,
|
||||||
|
tooltip: 'Increment',
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
), // This trailing comma makes auto-formatting nicer for build methods.
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ class ChatService {
|
|||||||
static const _jobIdPrefix = 'job:';
|
static const _jobIdPrefix = 'job:';
|
||||||
static const _jobNumberPrefix = 'job_number:';
|
static const _jobNumberPrefix = 'job_number:';
|
||||||
static const _generalPrefix = 'general:';
|
static const _generalPrefix = 'general:';
|
||||||
|
static const _defaultGeneralConversationKey =
|
||||||
|
'general:allgemeine-nachrichten';
|
||||||
|
|
||||||
final DatabaseService _databaseService = DatabaseService();
|
final DatabaseService _databaseService = DatabaseService();
|
||||||
final AppState _appState = AppState();
|
final AppState _appState = AppState();
|
||||||
@@ -103,9 +105,11 @@ class ChatService {
|
|||||||
|
|
||||||
_chats.removeWhere((chat) {
|
_chats.removeWhere((chat) {
|
||||||
final matchesKey = conversationKeys.contains(chat.id);
|
final matchesKey = conversationKeys.contains(chat.id);
|
||||||
final matchesId = trimmedJobId.isNotEmpty &&
|
final matchesId =
|
||||||
|
trimmedJobId.isNotEmpty &&
|
||||||
(chat.jobId?.trim().toLowerCase() == lowerJobId);
|
(chat.jobId?.trim().toLowerCase() == lowerJobId);
|
||||||
final matchesNumber = trimmedJobNumber.isNotEmpty &&
|
final matchesNumber =
|
||||||
|
trimmedJobNumber.isNotEmpty &&
|
||||||
(chat.jobNumber?.trim().toLowerCase() == lowerJobNumber);
|
(chat.jobNumber?.trim().toLowerCase() == lowerJobNumber);
|
||||||
return matchesKey || matchesId || matchesNumber;
|
return matchesKey || matchesId || matchesNumber;
|
||||||
});
|
});
|
||||||
@@ -129,18 +133,11 @@ class ChatService {
|
|||||||
|
|
||||||
// Messages with GENERAL messageType should always go to the default general chat
|
// Messages with GENERAL messageType should always go to the default general chat
|
||||||
if (message.messageType == ChatMessageType.general) {
|
if (message.messageType == ChatMessageType.general) {
|
||||||
final localId = _primaryLocalIdentifier();
|
|
||||||
if (localId != null && localId.isNotEmpty) {
|
|
||||||
final key = _conversationKeyForParticipants(
|
|
||||||
localId,
|
|
||||||
_appState.loggedInEmail!,
|
|
||||||
);
|
|
||||||
developer.log(
|
developer.log(
|
||||||
'[DEBUG_LOG] GENERAL message detected, routing to conversation key: $key (localId=$localId, receiver=${_appState.loggedInEmail})',
|
'[DEBUG_LOG] GENERAL message detected, routing to conversation key: $_defaultGeneralConversationKey',
|
||||||
name: 'ChatService',
|
name: 'ChatService',
|
||||||
);
|
);
|
||||||
return key;
|
return _defaultGeneralConversationKey;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Job-related messages go to job-specific chats
|
// Job-related messages go to job-specific chats
|
||||||
@@ -165,30 +162,11 @@ class ChatService {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: create conversation based on userId
|
|
||||||
final localId = _primaryLocalIdentifier();
|
|
||||||
if (localId != null && localId.isNotEmpty) {
|
|
||||||
final key = _conversationKeyForParticipants(
|
|
||||||
localId,
|
|
||||||
_appState.loggedInEmail!,
|
|
||||||
);
|
|
||||||
developer.log(
|
developer.log(
|
||||||
'[DEBUG_LOG] Using fallback routing, conversation key: $key',
|
'[DEBUG_LOG] No job context available, routing to default general chat',
|
||||||
name: 'ChatService',
|
name: 'ChatService',
|
||||||
);
|
);
|
||||||
return key;
|
return _defaultGeneralConversationKey;
|
||||||
}
|
|
||||||
|
|
||||||
developer.log(
|
|
||||||
'[DEBUG_LOG] No local identifier available for fallback routing',
|
|
||||||
name: 'ChatService',
|
|
||||||
);
|
|
||||||
return '$_generalPrefix${_appState.loggedInEmail!}';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _conversationKeyForParticipants(String a, String b) {
|
|
||||||
final participants = <String>[a.toLowerCase(), b.toLowerCase()]..sort();
|
|
||||||
return '$_generalPrefix${participants.join('|')}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveIncomingMessage(ChatMessage message) async {
|
Future<void> saveIncomingMessage(ChatMessage message) async {
|
||||||
@@ -205,6 +183,20 @@ class ChatService {
|
|||||||
await _persistMessage(message);
|
await _persistMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> markOutgoingMessageSynced(String messageId) async {
|
||||||
|
if (!_initialized) {
|
||||||
|
await initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
final conversationKey = await _databaseService.updateChatMessagePendingSync(
|
||||||
|
messageId,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
if (conversationKey != null && conversationKey.isNotEmpty) {
|
||||||
|
await _refreshConversation(conversationKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _persistMessage(ChatMessage message) async {
|
Future<void> _persistMessage(ChatMessage message) async {
|
||||||
final conversationKey = conversationKeyForMessage(message);
|
final conversationKey = conversationKeyForMessage(message);
|
||||||
|
|
||||||
@@ -239,7 +231,7 @@ class ChatService {
|
|||||||
|
|
||||||
Future<void> _loadChatsFromDatabase() async {
|
Future<void> _loadChatsFromDatabase() async {
|
||||||
await _databaseService.ensureInitialized();
|
await _databaseService.ensureInitialized();
|
||||||
final grouped = await _databaseService.loadAllChatMessagesGrouped();
|
final grouped = await _loadNormalizedChatGroups();
|
||||||
_chats.clear();
|
_chats.clear();
|
||||||
grouped.forEach((conversationKey, messages) {
|
grouped.forEach((conversationKey, messages) {
|
||||||
final chat = _buildChat(conversationKey, messages);
|
final chat = _buildChat(conversationKey, messages);
|
||||||
@@ -254,6 +246,14 @@ class ChatService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refreshConversation(String conversationKey) async {
|
Future<void> _refreshConversation(String conversationKey) async {
|
||||||
|
if (_isLegacyGeneralConversationKey(conversationKey)) {
|
||||||
|
await _databaseService.migrateConversationKey(
|
||||||
|
conversationKey,
|
||||||
|
_defaultGeneralConversationKey,
|
||||||
|
);
|
||||||
|
conversationKey = _defaultGeneralConversationKey;
|
||||||
|
}
|
||||||
|
|
||||||
final messages = await _databaseService.loadChatMessages(
|
final messages = await _databaseService.loadChatMessages(
|
||||||
conversationKey: conversationKey,
|
conversationKey: conversationKey,
|
||||||
);
|
);
|
||||||
@@ -317,15 +317,13 @@ class ChatService {
|
|||||||
|
|
||||||
final counterpartNormalized =
|
final counterpartNormalized =
|
||||||
counterpart != null &&
|
counterpart != null &&
|
||||||
counterpart.toLowerCase() == _appState.loggedInEmail!.toLowerCase()
|
counterpart.toLowerCase() ==
|
||||||
|
_appState.loggedInEmail!.toLowerCase()
|
||||||
? _appState.loggedInEmail!
|
? _appState.loggedInEmail!
|
||||||
: counterpart;
|
: counterpart;
|
||||||
|
|
||||||
final bool isDefaultGeneral =
|
final bool isDefaultGeneral =
|
||||||
!isJobChat &&
|
!isJobChat && conversationKey == _defaultGeneralConversationKey;
|
||||||
conversationKey.startsWith(_generalPrefix) &&
|
|
||||||
(counterpartNormalized?.toLowerCase() ==
|
|
||||||
_appState.loggedInEmail!.toLowerCase());
|
|
||||||
|
|
||||||
final title =
|
final title =
|
||||||
isJobChat
|
isJobChat
|
||||||
@@ -406,23 +404,46 @@ class ChatService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Map<String, List<ChatMessage>>> _loadNormalizedChatGroups() async {
|
||||||
|
var grouped = await _databaseService.loadAllChatMessagesGrouped();
|
||||||
|
final legacyGeneralKeys =
|
||||||
|
grouped.keys.where(_isLegacyGeneralConversationKey).toList();
|
||||||
|
if (legacyGeneralKeys.isEmpty) {
|
||||||
|
return grouped;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final key in legacyGeneralKeys) {
|
||||||
|
await _databaseService.migrateConversationKey(
|
||||||
|
key,
|
||||||
|
_defaultGeneralConversationKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
grouped = await _databaseService.loadAllChatMessagesGrouped();
|
||||||
|
return grouped;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isLegacyGeneralConversationKey(String conversationKey) {
|
||||||
|
return conversationKey != _defaultGeneralConversationKey &&
|
||||||
|
conversationKey.startsWith(_generalPrefix) &&
|
||||||
|
!conversationKey.startsWith(_jobIdPrefix) &&
|
||||||
|
!conversationKey.startsWith(_jobNumberPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
void _ensureDefaultGeneralChat() {
|
void _ensureDefaultGeneralChat() {
|
||||||
final localId = _primaryLocalIdentifier();
|
final receiver = _appState.loggedInEmail;
|
||||||
if (localId == null || localId.isEmpty) {
|
if (receiver == null || receiver.isEmpty) {
|
||||||
developer.log(
|
developer.log(
|
||||||
'[DEBUG_LOG] _ensureDefaultGeneralChat: No local identifier available, skipping',
|
'[DEBUG_LOG] _ensureDefaultGeneralChat: No receiver available, skipping',
|
||||||
name: 'ChatService',
|
name: 'ChatService',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final conversationKey = _conversationKeyForParticipants(
|
const conversationKey = _defaultGeneralConversationKey;
|
||||||
localId,
|
|
||||||
_appState.loggedInEmail!,
|
|
||||||
);
|
|
||||||
|
|
||||||
developer.log(
|
developer.log(
|
||||||
'[DEBUG_LOG] _ensureDefaultGeneralChat: Creating/ensuring default general chat with key: $conversationKey (localId=$localId, receiver=${_appState.loggedInEmail})',
|
'[DEBUG_LOG] _ensureDefaultGeneralChat: Creating/ensuring default general chat with key: $conversationKey (receiver=$receiver)',
|
||||||
name: 'ChatService',
|
name: 'ChatService',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -431,8 +452,7 @@ class ChatService {
|
|||||||
chat.id != conversationKey &&
|
chat.id != conversationKey &&
|
||||||
chat.type == ChatType.general &&
|
chat.type == ChatType.general &&
|
||||||
chat.receiver != null &&
|
chat.receiver != null &&
|
||||||
chat.receiver!.toLowerCase() ==
|
chat.receiver!.toLowerCase() == receiver.toLowerCase() &&
|
||||||
_appState.loggedInEmail!.toLowerCase() &&
|
|
||||||
chat.messages.isEmpty,
|
chat.messages.isEmpty,
|
||||||
);
|
);
|
||||||
final index = _chats.indexWhere((chat) => chat.id == conversationKey);
|
final index = _chats.indexWhere((chat) => chat.id == conversationKey);
|
||||||
@@ -446,7 +466,7 @@ class ChatService {
|
|||||||
Chat(
|
Chat(
|
||||||
id: conversationKey,
|
id: conversationKey,
|
||||||
title: 'Allgemeine Nachrichten',
|
title: 'Allgemeine Nachrichten',
|
||||||
receiver: _appState.loggedInEmail!,
|
receiver: receiver,
|
||||||
type: ChatType.general,
|
type: ChatType.general,
|
||||||
jobId: null,
|
jobId: null,
|
||||||
jobNumber: null,
|
jobNumber: null,
|
||||||
@@ -463,8 +483,7 @@ class ChatService {
|
|||||||
final existing = _chats[index];
|
final existing = _chats[index];
|
||||||
if (existing.type != ChatType.general ||
|
if (existing.type != ChatType.general ||
|
||||||
existing.receiver == null ||
|
existing.receiver == null ||
|
||||||
existing.receiver!.toLowerCase() !=
|
existing.receiver!.toLowerCase() != receiver.toLowerCase() ||
|
||||||
_appState.loggedInEmail!.toLowerCase() ||
|
|
||||||
(existing.messages.isEmpty &&
|
(existing.messages.isEmpty &&
|
||||||
existing.title != 'Allgemeine Nachrichten')) {
|
existing.title != 'Allgemeine Nachrichten')) {
|
||||||
developer.log(
|
developer.log(
|
||||||
@@ -477,7 +496,7 @@ class ChatService {
|
|||||||
existing.messages.isEmpty
|
existing.messages.isEmpty
|
||||||
? 'Allgemeine Nachrichten'
|
? 'Allgemeine Nachrichten'
|
||||||
: existing.title,
|
: existing.title,
|
||||||
receiver: _appState.loggedInEmail!,
|
receiver: receiver,
|
||||||
type: ChatType.general,
|
type: ChatType.general,
|
||||||
jobId: existing.jobId,
|
jobId: existing.jobId,
|
||||||
jobNumber: existing.jobNumber,
|
jobNumber: existing.jobNumber,
|
||||||
@@ -493,8 +512,4 @@ class ChatService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _primaryLocalIdentifier() {
|
|
||||||
return _appState.loggedInEmail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,10 @@ class DatabaseService {
|
|||||||
final completer = Completer<void>();
|
final completer = Completer<void>();
|
||||||
_initializingCompleter = completer;
|
_initializingCompleter = completer;
|
||||||
try {
|
try {
|
||||||
developer.log('Initializing ObjectBox database...', name: 'DatabaseService');
|
developer.log(
|
||||||
|
'Initializing ObjectBox database...',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
|
|
||||||
// Get database path
|
// Get database path
|
||||||
final docsDir = await getApplicationDocumentsDirectory();
|
final docsDir = await getApplicationDocumentsDirectory();
|
||||||
@@ -75,8 +78,6 @@ class DatabaseService {
|
|||||||
await initialize();
|
await initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Log database statistics
|
/// Log database statistics
|
||||||
Future<void> _logDatabaseStats() async {
|
Future<void> _logDatabaseStats() async {
|
||||||
try {
|
try {
|
||||||
@@ -164,7 +165,10 @@ class DatabaseService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
developer.log('Deleting job $jobId from database...', name: 'DatabaseService');
|
developer.log(
|
||||||
|
'Deleting job $jobId from database...',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
|
|
||||||
final jobBox = _store!.box<JobEntity>();
|
final jobBox = _store!.box<JobEntity>();
|
||||||
final query = jobBox.query(JobEntity_.jobId.equals(jobId)).build();
|
final query = jobBox.query(JobEntity_.jobId.equals(jobId)).build();
|
||||||
@@ -173,9 +177,15 @@ class DatabaseService {
|
|||||||
|
|
||||||
if (entities.isNotEmpty) {
|
if (entities.isNotEmpty) {
|
||||||
jobBox.remove(entities.first.id);
|
jobBox.remove(entities.first.id);
|
||||||
developer.log('Job $jobId deleted successfully', name: 'DatabaseService');
|
developer.log(
|
||||||
|
'Job $jobId deleted successfully',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
developer.log('Job $jobId not found in database', name: 'DatabaseService');
|
developer.log(
|
||||||
|
'Job $jobId not found in database',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
developer.log('Error deleting job: $e', name: 'DatabaseService');
|
developer.log('Error deleting job: $e', name: 'DatabaseService');
|
||||||
@@ -220,9 +230,13 @@ class DatabaseService {
|
|||||||
if (jobs.isNotEmpty) {
|
if (jobs.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
final chatBox = _store!.box<ChatMessageEntity>();
|
final chatBox = _store!.box<ChatMessageEntity>();
|
||||||
final query = chatBox.query(
|
final query =
|
||||||
(ChatMessageEntity_.jobId.notNull() | ChatMessageEntity_.jobNumber.notNull())
|
chatBox
|
||||||
).build();
|
.query(
|
||||||
|
(ChatMessageEntity_.jobId.notNull() |
|
||||||
|
ChatMessageEntity_.jobNumber.notNull()),
|
||||||
|
)
|
||||||
|
.build();
|
||||||
final messagesWithJobs = query.find();
|
final messagesWithJobs = query.find();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -282,7 +296,8 @@ class DatabaseService {
|
|||||||
final taskStatusBox = _store!.box<TaskStatusEntity>();
|
final taskStatusBox = _store!.box<TaskStatusEntity>();
|
||||||
|
|
||||||
// Find existing entity by taskId
|
// Find existing entity by taskId
|
||||||
final query = taskStatusBox.query(TaskStatusEntity_.taskId.equals(taskId)).build();
|
final query =
|
||||||
|
taskStatusBox.query(TaskStatusEntity_.taskId.equals(taskId)).build();
|
||||||
final existing = query.findFirst();
|
final existing = query.findFirst();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -321,7 +336,8 @@ class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final taskStatusBox = _store!.box<TaskStatusEntity>();
|
final taskStatusBox = _store!.box<TaskStatusEntity>();
|
||||||
final query = taskStatusBox.query(TaskStatusEntity_.taskId.equals(taskId)).build();
|
final query =
|
||||||
|
taskStatusBox.query(TaskStatusEntity_.taskId.equals(taskId)).build();
|
||||||
final entity = query.findFirst();
|
final entity = query.findFirst();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -449,7 +465,8 @@ class DatabaseService {
|
|||||||
final keys = jobIds.map((id) => 'job_seen:$id').toList();
|
final keys = jobIds.map((id) => 'job_seen:$id').toList();
|
||||||
|
|
||||||
for (final key in keys) {
|
for (final key in keys) {
|
||||||
final query = userDataBox.query(UserDataEntity_.key.equals(key)).build();
|
final query =
|
||||||
|
userDataBox.query(UserDataEntity_.key.equals(key)).build();
|
||||||
final entity = query.findFirst();
|
final entity = query.findFirst();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -545,7 +562,8 @@ class DatabaseService {
|
|||||||
final taskStatusBox = _store!.box<TaskStatusEntity>();
|
final taskStatusBox = _store!.box<TaskStatusEntity>();
|
||||||
|
|
||||||
// Find existing job entity by jobId
|
// Find existing job entity by jobId
|
||||||
final jobQuery = jobBox.query(JobEntity_.jobId.equals(normalized.id)).build();
|
final jobQuery =
|
||||||
|
jobBox.query(JobEntity_.jobId.equals(normalized.id)).build();
|
||||||
final existingJob = jobQuery.findFirst();
|
final existingJob = jobQuery.findFirst();
|
||||||
jobQuery.close();
|
jobQuery.close();
|
||||||
|
|
||||||
@@ -568,7 +586,10 @@ class DatabaseService {
|
|||||||
final taskIds = normalized.tasks.map((t) => t.id).toList();
|
final taskIds = normalized.tasks.map((t) => t.id).toList();
|
||||||
if (taskIds.isNotEmpty) {
|
if (taskIds.isNotEmpty) {
|
||||||
for (final taskId in taskIds) {
|
for (final taskId in taskIds) {
|
||||||
final query = taskStatusBox.query(TaskStatusEntity_.taskId.equals(taskId)).build();
|
final query =
|
||||||
|
taskStatusBox
|
||||||
|
.query(TaskStatusEntity_.taskId.equals(taskId))
|
||||||
|
.build();
|
||||||
final entities = query.find();
|
final entities = query.find();
|
||||||
query.close();
|
query.close();
|
||||||
for (final entity in entities) {
|
for (final entity in entities) {
|
||||||
@@ -617,7 +638,8 @@ class DatabaseService {
|
|||||||
|
|
||||||
if (trimmedJobId.isNotEmpty) {
|
if (trimmedJobId.isNotEmpty) {
|
||||||
// Delete job
|
// Delete job
|
||||||
final jobQuery = jobBox.query(JobEntity_.jobId.equals(trimmedJobId)).build();
|
final jobQuery =
|
||||||
|
jobBox.query(JobEntity_.jobId.equals(trimmedJobId)).build();
|
||||||
final jobEntities = jobQuery.find();
|
final jobEntities = jobQuery.find();
|
||||||
jobQuery.close();
|
jobQuery.close();
|
||||||
for (final entity in jobEntities) {
|
for (final entity in jobEntities) {
|
||||||
@@ -625,7 +647,10 @@ class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete job_seen flag
|
// Delete job_seen flag
|
||||||
final seenQuery = userDataBox.query(UserDataEntity_.key.equals('job_seen:$trimmedJobId')).build();
|
final seenQuery =
|
||||||
|
userDataBox
|
||||||
|
.query(UserDataEntity_.key.equals('job_seen:$trimmedJobId'))
|
||||||
|
.build();
|
||||||
final seenEntities = seenQuery.find();
|
final seenEntities = seenQuery.find();
|
||||||
seenQuery.close();
|
seenQuery.close();
|
||||||
for (final entity in seenEntities) {
|
for (final entity in seenEntities) {
|
||||||
@@ -633,7 +658,8 @@ class DatabaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final taskIds = job.tasks
|
final taskIds =
|
||||||
|
job.tasks
|
||||||
.map((task) => task.id.trim())
|
.map((task) => task.id.trim())
|
||||||
.where((id) => id.isNotEmpty)
|
.where((id) => id.isNotEmpty)
|
||||||
.toList();
|
.toList();
|
||||||
@@ -641,7 +667,10 @@ class DatabaseService {
|
|||||||
if (taskIds.isNotEmpty) {
|
if (taskIds.isNotEmpty) {
|
||||||
for (final taskId in taskIds) {
|
for (final taskId in taskIds) {
|
||||||
// Delete task status
|
// Delete task status
|
||||||
final taskQuery = taskStatusBox.query(TaskStatusEntity_.taskId.equals(taskId)).build();
|
final taskQuery =
|
||||||
|
taskStatusBox
|
||||||
|
.query(TaskStatusEntity_.taskId.equals(taskId))
|
||||||
|
.build();
|
||||||
final taskEntities = taskQuery.find();
|
final taskEntities = taskQuery.find();
|
||||||
taskQuery.close();
|
taskQuery.close();
|
||||||
for (final entity in taskEntities) {
|
for (final entity in taskEntities) {
|
||||||
@@ -649,7 +678,8 @@ class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete photos
|
// Delete photos
|
||||||
final photoQuery = photoBox.query(PhotoEntity_.taskId.equals(taskId)).build();
|
final photoQuery =
|
||||||
|
photoBox.query(PhotoEntity_.taskId.equals(taskId)).build();
|
||||||
final photoEntities = photoQuery.find();
|
final photoEntities = photoQuery.find();
|
||||||
photoQuery.close();
|
photoQuery.close();
|
||||||
for (final entity in photoEntities) {
|
for (final entity in photoEntities) {
|
||||||
@@ -960,9 +990,21 @@ class DatabaseService {
|
|||||||
|
|
||||||
/// Save login credentials for auto-login on app restart
|
/// Save login credentials for auto-login on app restart
|
||||||
Future<void> saveCredentials(String email, String password) async {
|
Future<void> saveCredentials(String email, String password) async {
|
||||||
await saveKeyValue('auth_email', email);
|
final normalizedEmail = email.trim();
|
||||||
|
if (normalizedEmail.isEmpty || password.isEmpty) {
|
||||||
|
developer.log(
|
||||||
|
'Skipping credential save because email or password is empty',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await saveKeyValue('auth_email', normalizedEmail);
|
||||||
await saveKeyValue('auth_password', password);
|
await saveKeyValue('auth_password', password);
|
||||||
developer.log('Credentials saved for $email', name: 'DatabaseService');
|
developer.log(
|
||||||
|
'Credentials saved for $normalizedEmail',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load saved login credentials
|
/// Load saved login credentials
|
||||||
@@ -970,11 +1012,29 @@ class DatabaseService {
|
|||||||
Future<({String email, String password})?> loadCredentials() async {
|
Future<({String email, String password})?> loadCredentials() async {
|
||||||
final email = await loadKeyValue('auth_email');
|
final email = await loadKeyValue('auth_email');
|
||||||
final password = await loadKeyValue('auth_password');
|
final password = await loadKeyValue('auth_password');
|
||||||
if (email != null && password != null) {
|
final normalizedEmail = email?.trim();
|
||||||
developer.log('Credentials loaded for $email', name: 'DatabaseService');
|
|
||||||
return (email: email, password: password);
|
if (normalizedEmail != null &&
|
||||||
|
normalizedEmail.isNotEmpty &&
|
||||||
|
password != null &&
|
||||||
|
password.isNotEmpty) {
|
||||||
|
developer.log(
|
||||||
|
'Credentials loaded for $normalizedEmail',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
|
return (email: normalizedEmail, password: password);
|
||||||
}
|
}
|
||||||
developer.log('No credentials found', name: 'DatabaseService');
|
|
||||||
|
if ((email != null && email.isNotEmpty) ||
|
||||||
|
(password != null && password.isNotEmpty)) {
|
||||||
|
developer.log(
|
||||||
|
'Stored credentials are incomplete or empty - removing them',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
|
await deleteCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
developer.log('No valid credentials found', name: 'DatabaseService');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1008,7 +1068,10 @@ class DatabaseService {
|
|||||||
final chatBox = _store!.box<ChatMessageEntity>();
|
final chatBox = _store!.box<ChatMessageEntity>();
|
||||||
|
|
||||||
// Find existing entity by messageId
|
// Find existing entity by messageId
|
||||||
final query = chatBox.query(ChatMessageEntity_.messageId.equals(message.id)).build();
|
final query =
|
||||||
|
chatBox
|
||||||
|
.query(ChatMessageEntity_.messageId.equals(message.id))
|
||||||
|
.build();
|
||||||
final existing = query.findFirst();
|
final existing = query.findFirst();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -1060,7 +1123,10 @@ class DatabaseService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final chatBox = _store!.box<ChatMessageEntity>();
|
final chatBox = _store!.box<ChatMessageEntity>();
|
||||||
final query = chatBox.query(ChatMessageEntity_.conversationKey.equals(fromKey)).build();
|
final query =
|
||||||
|
chatBox
|
||||||
|
.query(ChatMessageEntity_.conversationKey.equals(fromKey))
|
||||||
|
.build();
|
||||||
final entities = query.find();
|
final entities = query.find();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -1089,13 +1155,18 @@ class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final chatBox = _store!.box<ChatMessageEntity>();
|
final chatBox = _store!.box<ChatMessageEntity>();
|
||||||
final query = chatBox.query(
|
final query =
|
||||||
|
chatBox
|
||||||
|
.query(
|
||||||
ChatMessageEntity_.conversationKey.equals(conversationKey) &
|
ChatMessageEntity_.conversationKey.equals(conversationKey) &
|
||||||
ChatMessageEntity_.pendingSync.equals(true) &
|
ChatMessageEntity_.pendingSync.equals(true) &
|
||||||
ChatMessageEntity_.content.equals(message.content) &
|
ChatMessageEntity_.content.equals(message.content) &
|
||||||
ChatMessageEntity_.contentType.equals(chatContentTypeToString(message.contentType)) &
|
ChatMessageEntity_.contentType.equals(
|
||||||
ChatMessageEntity_.messageId.notEquals(message.id)
|
chatContentTypeToString(message.contentType),
|
||||||
).build();
|
) &
|
||||||
|
ChatMessageEntity_.messageId.notEquals(message.id),
|
||||||
|
)
|
||||||
|
.build();
|
||||||
final entities = query.find();
|
final entities = query.find();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -1123,7 +1194,11 @@ class DatabaseService {
|
|||||||
List<ChatMessageEntity> entities;
|
List<ChatMessageEntity> entities;
|
||||||
|
|
||||||
if (conversationKey != null) {
|
if (conversationKey != null) {
|
||||||
final query = chatBox.query(ChatMessageEntity_.conversationKey.equals(conversationKey))
|
final query =
|
||||||
|
chatBox
|
||||||
|
.query(
|
||||||
|
ChatMessageEntity_.conversationKey.equals(conversationKey),
|
||||||
|
)
|
||||||
.order(ChatMessageEntity_.createdAt)
|
.order(ChatMessageEntity_.createdAt)
|
||||||
.build();
|
.build();
|
||||||
entities = query.find();
|
entities = query.find();
|
||||||
@@ -1186,7 +1261,10 @@ class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final chatBox = _store!.box<ChatMessageEntity>();
|
final chatBox = _store!.box<ChatMessageEntity>();
|
||||||
final query = chatBox.query(ChatMessageEntity_.conversationKey.equals(conversationKey)).build();
|
final query =
|
||||||
|
chatBox
|
||||||
|
.query(ChatMessageEntity_.conversationKey.equals(conversationKey))
|
||||||
|
.build();
|
||||||
final entities = query.find();
|
final entities = query.find();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -1211,7 +1289,8 @@ class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final chatBox = _store!.box<ChatMessageEntity>();
|
final chatBox = _store!.box<ChatMessageEntity>();
|
||||||
final query = chatBox.query(ChatMessageEntity_.messageId.equals(messageId)).build();
|
final query =
|
||||||
|
chatBox.query(ChatMessageEntity_.messageId.equals(messageId)).build();
|
||||||
final entities = query.find();
|
final entities = query.find();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -1241,7 +1320,8 @@ class DatabaseService {
|
|||||||
|
|
||||||
final trimmedJobId = jobId?.trim() ?? '';
|
final trimmedJobId = jobId?.trim() ?? '';
|
||||||
final trimmedJobNumber = jobNumber?.trim() ?? '';
|
final trimmedJobNumber = jobNumber?.trim() ?? '';
|
||||||
final keysList = conversationKeys == null
|
final keysList =
|
||||||
|
conversationKeys == null
|
||||||
? <String>[]
|
? <String>[]
|
||||||
: conversationKeys
|
: conversationKeys
|
||||||
.map((key) => key.trim())
|
.map((key) => key.trim())
|
||||||
@@ -1249,7 +1329,9 @@ class DatabaseService {
|
|||||||
.toSet()
|
.toSet()
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (trimmedJobId.isEmpty && trimmedJobNumber.isEmpty && keysList.isEmpty) {
|
if (trimmedJobId.isEmpty &&
|
||||||
|
trimmedJobNumber.isEmpty &&
|
||||||
|
keysList.isEmpty) {
|
||||||
developer.log(
|
developer.log(
|
||||||
'No chat messages matched deletion criteria for jobId=$jobId jobNumber=$jobNumber',
|
'No chat messages matched deletion criteria for jobId=$jobId jobNumber=$jobNumber',
|
||||||
name: 'DatabaseService',
|
name: 'DatabaseService',
|
||||||
@@ -1261,20 +1343,29 @@ class DatabaseService {
|
|||||||
final entitiesToDelete = <ChatMessageEntity>[];
|
final entitiesToDelete = <ChatMessageEntity>[];
|
||||||
|
|
||||||
if (trimmedJobId.isNotEmpty) {
|
if (trimmedJobId.isNotEmpty) {
|
||||||
final query = chatBox.query(ChatMessageEntity_.jobId.equals(trimmedJobId)).build();
|
final query =
|
||||||
|
chatBox
|
||||||
|
.query(ChatMessageEntity_.jobId.equals(trimmedJobId))
|
||||||
|
.build();
|
||||||
entitiesToDelete.addAll(query.find());
|
entitiesToDelete.addAll(query.find());
|
||||||
query.close();
|
query.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trimmedJobNumber.isNotEmpty) {
|
if (trimmedJobNumber.isNotEmpty) {
|
||||||
final query = chatBox.query(ChatMessageEntity_.jobNumber.equals(trimmedJobNumber)).build();
|
final query =
|
||||||
|
chatBox
|
||||||
|
.query(ChatMessageEntity_.jobNumber.equals(trimmedJobNumber))
|
||||||
|
.build();
|
||||||
entitiesToDelete.addAll(query.find());
|
entitiesToDelete.addAll(query.find());
|
||||||
query.close();
|
query.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keysList.isNotEmpty) {
|
if (keysList.isNotEmpty) {
|
||||||
for (final key in keysList) {
|
for (final key in keysList) {
|
||||||
final query = chatBox.query(ChatMessageEntity_.conversationKey.equals(key)).build();
|
final query =
|
||||||
|
chatBox
|
||||||
|
.query(ChatMessageEntity_.conversationKey.equals(key))
|
||||||
|
.build();
|
||||||
entitiesToDelete.addAll(query.find());
|
entitiesToDelete.addAll(query.find());
|
||||||
query.close();
|
query.close();
|
||||||
}
|
}
|
||||||
@@ -1309,7 +1400,8 @@ class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final chatBox = _store!.box<ChatMessageEntity>();
|
final chatBox = _store!.box<ChatMessageEntity>();
|
||||||
final query = chatBox.query(ChatMessageEntity_.read.equals(false)).build();
|
final query =
|
||||||
|
chatBox.query(ChatMessageEntity_.read.equals(false)).build();
|
||||||
final count = query.count();
|
final count = query.count();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -1349,6 +1441,7 @@ class DatabaseService {
|
|||||||
/// Save a failed message to the queue
|
/// Save a failed message to the queue
|
||||||
Future<void> queueMessage(QueuedMessage message) async {
|
Future<void> queueMessage(QueuedMessage message) async {
|
||||||
try {
|
try {
|
||||||
|
await ensureInitialized();
|
||||||
if (_store == null) {
|
if (_store == null) {
|
||||||
developer.log('Database not initialized', name: 'DatabaseService');
|
developer.log('Database not initialized', name: 'DatabaseService');
|
||||||
return;
|
return;
|
||||||
@@ -1357,7 +1450,8 @@ class DatabaseService {
|
|||||||
final box = _store!.box<QueuedMessageEntity>();
|
final box = _store!.box<QueuedMessageEntity>();
|
||||||
|
|
||||||
// Find existing entity by messageId
|
// Find existing entity by messageId
|
||||||
final query = box.query(QueuedMessageEntity_.messageId.equals(message.id)).build();
|
final query =
|
||||||
|
box.query(QueuedMessageEntity_.messageId.equals(message.id)).build();
|
||||||
final existing = query.findFirst();
|
final existing = query.findFirst();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -1391,6 +1485,7 @@ class DatabaseService {
|
|||||||
/// Get all queued messages
|
/// Get all queued messages
|
||||||
Future<List<QueuedMessage>> getQueuedMessages() async {
|
Future<List<QueuedMessage>> getQueuedMessages() async {
|
||||||
try {
|
try {
|
||||||
|
await ensureInitialized();
|
||||||
if (_store == null) {
|
if (_store == null) {
|
||||||
developer.log('Database not initialized', name: 'DatabaseService');
|
developer.log('Database not initialized', name: 'DatabaseService');
|
||||||
return [];
|
return [];
|
||||||
@@ -1424,13 +1519,15 @@ class DatabaseService {
|
|||||||
/// Remove a successfully sent message from the queue
|
/// Remove a successfully sent message from the queue
|
||||||
Future<void> removeQueuedMessage(String messageId) async {
|
Future<void> removeQueuedMessage(String messageId) async {
|
||||||
try {
|
try {
|
||||||
|
await ensureInitialized();
|
||||||
if (_store == null) {
|
if (_store == null) {
|
||||||
developer.log('Database not initialized', name: 'DatabaseService');
|
developer.log('Database not initialized', name: 'DatabaseService');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final box = _store!.box<QueuedMessageEntity>();
|
final box = _store!.box<QueuedMessageEntity>();
|
||||||
final query = box.query(QueuedMessageEntity_.messageId.equals(messageId)).build();
|
final query =
|
||||||
|
box.query(QueuedMessageEntity_.messageId.equals(messageId)).build();
|
||||||
final entities = query.find();
|
final entities = query.find();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -1452,18 +1549,17 @@ class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update retry count for a message
|
/// Update retry count for a message
|
||||||
Future<void> updateMessageRetryCount(
|
Future<void> updateMessageRetryCount(String messageId, int retryCount) async {
|
||||||
String messageId,
|
|
||||||
int retryCount,
|
|
||||||
) async {
|
|
||||||
try {
|
try {
|
||||||
|
await ensureInitialized();
|
||||||
if (_store == null) {
|
if (_store == null) {
|
||||||
developer.log('Database not initialized', name: 'DatabaseService');
|
developer.log('Database not initialized', name: 'DatabaseService');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final box = _store!.box<QueuedMessageEntity>();
|
final box = _store!.box<QueuedMessageEntity>();
|
||||||
final query = box.query(QueuedMessageEntity_.messageId.equals(messageId)).build();
|
final query =
|
||||||
|
box.query(QueuedMessageEntity_.messageId.equals(messageId)).build();
|
||||||
final entity = query.findFirst();
|
final entity = query.findFirst();
|
||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
@@ -1488,16 +1584,14 @@ class DatabaseService {
|
|||||||
/// Clear all queued messages (for cleanup)
|
/// Clear all queued messages (for cleanup)
|
||||||
Future<void> clearQueuedMessages() async {
|
Future<void> clearQueuedMessages() async {
|
||||||
try {
|
try {
|
||||||
|
await ensureInitialized();
|
||||||
if (_store == null) {
|
if (_store == null) {
|
||||||
developer.log('Database not initialized', name: 'DatabaseService');
|
developer.log('Database not initialized', name: 'DatabaseService');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_store!.box<QueuedMessageEntity>().removeAll();
|
_store!.box<QueuedMessageEntity>().removeAll();
|
||||||
developer.log(
|
developer.log('Cleared all queued messages', name: 'DatabaseService');
|
||||||
'Cleared all queued messages',
|
|
||||||
name: 'DatabaseService',
|
|
||||||
);
|
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
developer.log(
|
developer.log(
|
||||||
'Error clearing queued messages: $e',
|
'Error clearing queued messages: $e',
|
||||||
@@ -1507,12 +1601,49 @@ class DatabaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String?> updateChatMessagePendingSync(
|
||||||
|
String messageId,
|
||||||
|
bool pendingSync,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await ensureInitialized();
|
||||||
|
if (_store == null) {
|
||||||
|
developer.log('Database not initialized', name: 'DatabaseService');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final chatBox = _store!.box<ChatMessageEntity>();
|
||||||
|
final query =
|
||||||
|
chatBox.query(ChatMessageEntity_.messageId.equals(messageId)).build();
|
||||||
|
final entity = query.findFirst();
|
||||||
|
query.close();
|
||||||
|
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.pendingSync = pendingSync;
|
||||||
|
chatBox.put(entity);
|
||||||
|
return entity.conversationKey;
|
||||||
|
} catch (e, st) {
|
||||||
|
developer.log(
|
||||||
|
'Error updating pendingSync for message $messageId: $e',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
|
developer.log('Stack trace: $st', name: 'DatabaseService');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Language preference persistence ----------------------------------------------------
|
// Language preference persistence ----------------------------------------------------
|
||||||
|
|
||||||
/// Save language preference
|
/// Save language preference
|
||||||
Future<void> saveLanguagePreference(String languageCode) async {
|
Future<void> saveLanguagePreference(String languageCode) async {
|
||||||
await saveKeyValue('language_preference', languageCode);
|
await saveKeyValue('language_preference', languageCode);
|
||||||
developer.log('Language preference saved: $languageCode', name: 'DatabaseService');
|
developer.log(
|
||||||
|
'Language preference saved: $languageCode',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load saved language preference
|
/// Load saved language preference
|
||||||
@@ -1520,7 +1651,10 @@ class DatabaseService {
|
|||||||
Future<String?> loadLanguagePreference() async {
|
Future<String?> loadLanguagePreference() async {
|
||||||
final languageCode = await loadKeyValue('language_preference');
|
final languageCode = await loadKeyValue('language_preference');
|
||||||
if (languageCode != null) {
|
if (languageCode != null) {
|
||||||
developer.log('Language preference loaded: $languageCode', name: 'DatabaseService');
|
developer.log(
|
||||||
|
'Language preference loaded: $languageCode',
|
||||||
|
name: 'DatabaseService',
|
||||||
|
);
|
||||||
return languageCode;
|
return languageCode;
|
||||||
}
|
}
|
||||||
developer.log('No language preference found', name: 'DatabaseService');
|
developer.log('No language preference found', name: 'DatabaseService');
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import 'location_service.dart';
|
|||||||
import '../app_state.dart';
|
import '../app_state.dart';
|
||||||
import '../models/chat_message.dart';
|
import '../models/chat_message.dart';
|
||||||
import '../models/job.dart';
|
import '../models/job.dart';
|
||||||
|
import '../models/queued_message.dart';
|
||||||
import 'dart_mq.dart';
|
import 'dart_mq.dart';
|
||||||
|
|
||||||
class WebSocketService {
|
class WebSocketService {
|
||||||
@@ -193,6 +194,73 @@ class WebSocketService {
|
|||||||
_reconnectTimer = null;
|
_reconnectTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Force a clean reconnect after the app resumes from standby.
|
||||||
|
/// Keeps buffered outbound messages intact and relies on saved credentials
|
||||||
|
/// for the subsequent auto-login inside [connect].
|
||||||
|
Future<void> reconnectForAppResume() async {
|
||||||
|
final credentials = await _databaseService.loadCredentials();
|
||||||
|
if (credentials == null) {
|
||||||
|
developer.log(
|
||||||
|
'Skipping reconnect after resume - no saved credentials',
|
||||||
|
name: 'WebSocketService',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isConnecting) {
|
||||||
|
developer.log(
|
||||||
|
'Skipping reconnect after resume - connection attempt already running',
|
||||||
|
name: 'WebSocketService',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
developer.log(
|
||||||
|
'Restarting WebSocket connection after app resume',
|
||||||
|
name: 'WebSocketService',
|
||||||
|
);
|
||||||
|
|
||||||
|
_stopReconnectTimer();
|
||||||
|
|
||||||
|
final existingSubscription = _wsSubscription;
|
||||||
|
final existingChannel = _wsChannel;
|
||||||
|
|
||||||
|
_wsSubscription = null;
|
||||||
|
_wsChannel = null;
|
||||||
|
_disconnectCompleter = null;
|
||||||
|
_isConnected = false;
|
||||||
|
_isConnecting = false;
|
||||||
|
_isAuthenticated = false;
|
||||||
|
_authToken = null;
|
||||||
|
_lastAuthResponse = null;
|
||||||
|
|
||||||
|
Future.microtask(() {
|
||||||
|
DartMQ().publish<bool>(MQTopics.connectionStatus, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await existingSubscription?.cancel();
|
||||||
|
} catch (e, st) {
|
||||||
|
developer.log(
|
||||||
|
'Error cancelling old WebSocket subscription on resume: $e',
|
||||||
|
name: 'WebSocketService',
|
||||||
|
);
|
||||||
|
developer.log('Stack: $st', name: 'WebSocketService');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await existingChannel?.sink.close(ws_status.goingAway);
|
||||||
|
} catch (e, st) {
|
||||||
|
developer.log(
|
||||||
|
'Error closing old WebSocket channel on resume: $e',
|
||||||
|
name: 'WebSocketService',
|
||||||
|
);
|
||||||
|
developer.log('Stack: $st', name: 'WebSocketService');
|
||||||
|
}
|
||||||
|
|
||||||
|
await connect();
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// WebSocket Send / Receive
|
// WebSocket Send / Receive
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -290,6 +358,8 @@ class WebSocketService {
|
|||||||
_handleJobDeletedMessage(data);
|
_handleJobDeletedMessage(data);
|
||||||
} else if (topic.endsWith('/job_created')) {
|
} else if (topic.endsWith('/job_created')) {
|
||||||
_handleJobCreatedMessage(data);
|
_handleJobCreatedMessage(data);
|
||||||
|
} else if (topic.endsWith('/message_ack')) {
|
||||||
|
await _handleChatMessageAck(data);
|
||||||
} else if (topic.endsWith('/message')) {
|
} else if (topic.endsWith('/message')) {
|
||||||
await _handleChatMessage(topic, data);
|
await _handleChatMessage(topic, data);
|
||||||
} else {
|
} else {
|
||||||
@@ -598,6 +668,20 @@ class WebSocketService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _handleChatMessageAck(Map<String, dynamic> data) async {
|
||||||
|
final clientMessageId = data['clientMessageId']?.toString().trim() ?? '';
|
||||||
|
if (clientMessageId.isEmpty) {
|
||||||
|
developer.log(
|
||||||
|
'Received message ACK without clientMessageId',
|
||||||
|
name: 'WebSocketService',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _databaseService.removeQueuedMessage(clientMessageId);
|
||||||
|
await ChatService().markOutgoingMessageSynced(clientMessageId);
|
||||||
|
}
|
||||||
|
|
||||||
void _handleOtherClientMessage(String topic, Map<String, dynamic> data) {
|
void _handleOtherClientMessage(String topic, Map<String, dynamic> data) {
|
||||||
final type = data['type'];
|
final type = data['type'];
|
||||||
if (topic.contains('/tasks/') || type == 'task') {
|
if (topic.contains('/tasks/') || type == 'task') {
|
||||||
@@ -731,6 +815,7 @@ class WebSocketService {
|
|||||||
/// Clears all local jobs and related data, then notifies the server.
|
/// Clears all local jobs and related data, then notifies the server.
|
||||||
Future<void> _flushMessageBuffer() async {
|
Future<void> _flushMessageBuffer() async {
|
||||||
final initialBufferSize = _messageBuffer.length;
|
final initialBufferSize = _messageBuffer.length;
|
||||||
|
final sentQueuedChatCount = await _flushQueuedChatMessages();
|
||||||
|
|
||||||
if (initialBufferSize > 0) {
|
if (initialBufferSize > 0) {
|
||||||
developer.log(
|
developer.log(
|
||||||
@@ -766,7 +851,8 @@ class WebSocketService {
|
|||||||
await _databaseService.clearAllJobsAndRelatedData();
|
await _databaseService.clearAllJobsAndRelatedData();
|
||||||
|
|
||||||
// Notify server that buffer flush is complete
|
// Notify server that buffer flush is complete
|
||||||
final sentCount = initialBufferSize - _messageBuffer.length;
|
final sentCount =
|
||||||
|
(initialBufferSize - _messageBuffer.length) + sentQueuedChatCount;
|
||||||
final bufferFlushedPayload = jsonEncode({
|
final bufferFlushedPayload = jsonEncode({
|
||||||
'timestamp': DateTime.now().toIso8601String(),
|
'timestamp': DateTime.now().toIso8601String(),
|
||||||
'messageCount': sentCount,
|
'messageCount': sentCount,
|
||||||
@@ -774,9 +860,51 @@ class WebSocketService {
|
|||||||
_sendWebSocket('/server/buffer_flushed', bufferFlushedPayload);
|
_sendWebSocket('/server/buffer_flushed', bufferFlushedPayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<int> _flushQueuedChatMessages() async {
|
||||||
|
final queuedMessages = await _databaseService.getQueuedMessages();
|
||||||
|
if (queuedMessages.isEmpty) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
developer.log(
|
||||||
|
'Flushing ${queuedMessages.length} queued chat messages',
|
||||||
|
name: 'WebSocketService',
|
||||||
|
);
|
||||||
|
|
||||||
|
var sentCount = 0;
|
||||||
|
for (final message in queuedMessages) {
|
||||||
|
final success = await _trySendQueuedChatMessage(
|
||||||
|
message,
|
||||||
|
incrementRetryOnFailure: true,
|
||||||
|
);
|
||||||
|
if (success) {
|
||||||
|
sentCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sentCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _trySendQueuedChatMessage(
|
||||||
|
QueuedMessage message, {
|
||||||
|
bool incrementRetryOnFailure = false,
|
||||||
|
}) async {
|
||||||
|
if (!_isConnected || !_isAuthenticated || _wsChannel == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final success = _sendWebSocket(message.topic, jsonEncode(message.payload));
|
||||||
|
if (!success && incrementRetryOnFailure) {
|
||||||
|
await _databaseService.updateMessageRetryCount(
|
||||||
|
message.id,
|
||||||
|
message.retryCount + 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
/// Publish a chat message according to the backend contract.
|
/// Publish a chat message according to the backend contract.
|
||||||
/// Returns the locally constructed message so callers can persist it locally.
|
/// The message is stored locally and remains queued until the server confirms it.
|
||||||
/// Messages are buffered if offline and sent automatically when reconnected.
|
|
||||||
Future<ChatMessage?> sendChatMessage({
|
Future<ChatMessage?> sendChatMessage({
|
||||||
required String sender,
|
required String sender,
|
||||||
required String receiver,
|
required String receiver,
|
||||||
@@ -790,6 +918,9 @@ class WebSocketService {
|
|||||||
final trimmedContent = content.trim();
|
final trimmedContent = content.trim();
|
||||||
final normalizedJobId = jobId?.trim();
|
final normalizedJobId = jobId?.trim();
|
||||||
final normalizedJobNumber = jobNumber?.trim();
|
final normalizedJobNumber = jobNumber?.trim();
|
||||||
|
final hasJobContext =
|
||||||
|
(normalizedJobId?.isNotEmpty ?? false) ||
|
||||||
|
(normalizedJobNumber?.isNotEmpty ?? false);
|
||||||
|
|
||||||
if (trimmedSender.isEmpty ||
|
if (trimmedSender.isEmpty ||
|
||||||
trimmedReceiver.isEmpty ||
|
trimmedReceiver.isEmpty ||
|
||||||
@@ -816,6 +947,9 @@ class WebSocketService {
|
|||||||
'receiver': trimmedReceiver,
|
'receiver': trimmedReceiver,
|
||||||
'content': trimmedContent,
|
'content': trimmedContent,
|
||||||
};
|
};
|
||||||
|
final now = DateTime.now();
|
||||||
|
final clientMessageId = 'local-${now.microsecondsSinceEpoch}';
|
||||||
|
payload['messageId'] = clientMessageId;
|
||||||
|
|
||||||
if (normalizedJobId != null && normalizedJobId.isNotEmpty) {
|
if (normalizedJobId != null && normalizedJobId.isNotEmpty) {
|
||||||
payload['jobId'] = normalizedJobId;
|
payload['jobId'] = normalizedJobId;
|
||||||
@@ -828,18 +962,13 @@ class WebSocketService {
|
|||||||
const topic = '/server/message';
|
const topic = '/server/message';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final jsonPayload = jsonEncode(payload);
|
|
||||||
// sendMessage buffers automatically if not connected/authenticated
|
|
||||||
sendMessage(topic, jsonPayload);
|
|
||||||
|
|
||||||
final now = DateTime.now();
|
|
||||||
final message = ChatMessage(
|
final message = ChatMessage(
|
||||||
id: 'local-${now.microsecondsSinceEpoch}',
|
id: clientMessageId,
|
||||||
content: trimmedContent,
|
content: trimmedContent,
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
direction: ChatDirection.outgoing,
|
direction: ChatDirection.outgoing,
|
||||||
messageType:
|
messageType:
|
||||||
normalizedJobId != null && normalizedJobId.isNotEmpty
|
hasJobContext
|
||||||
? ChatMessageType.jobRelated
|
? ChatMessageType.jobRelated
|
||||||
: ChatMessageType.general,
|
: ChatMessageType.general,
|
||||||
contentType: contentType,
|
contentType: contentType,
|
||||||
@@ -849,13 +978,26 @@ class WebSocketService {
|
|||||||
read: false,
|
read: false,
|
||||||
pendingSync: true,
|
pendingSync: true,
|
||||||
);
|
);
|
||||||
|
final queuedMessage = QueuedMessage(
|
||||||
|
id: clientMessageId,
|
||||||
|
topic: topic,
|
||||||
|
payload: payload,
|
||||||
|
createdAt: now,
|
||||||
|
);
|
||||||
|
|
||||||
|
await _databaseService.queueMessage(queuedMessage);
|
||||||
|
await ChatService().saveOutgoingMessage(message);
|
||||||
|
final sentImmediately = await _trySendQueuedChatMessage(queuedMessage);
|
||||||
|
if (!sentImmediately) {
|
||||||
|
developer.log(
|
||||||
|
'Chat message $clientMessageId queued for retry after reconnect',
|
||||||
|
name: 'WebSocketService',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
developer.log(
|
developer.log('Error sending chat message: $e', name: 'WebSocketService');
|
||||||
'Error encoding chat message payload: $e',
|
|
||||||
name: 'WebSocketService',
|
|
||||||
);
|
|
||||||
developer.log('Stack: $st', name: 'WebSocketService');
|
developer.log('Stack: $st', name: 'WebSocketService');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:image/image.dart' as img;
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'l10n/app_localizations.dart';
|
import 'l10n/app_localizations.dart';
|
||||||
|
import 'l10n/localization_helpers.dart';
|
||||||
import 'models/job.dart';
|
import 'models/job.dart';
|
||||||
import 'models/task.dart';
|
import 'models/task.dart';
|
||||||
import 'models/tasks/confirmation_task.dart';
|
import 'models/tasks/confirmation_task.dart';
|
||||||
@@ -721,8 +722,14 @@ class _TaskViewState extends State<TaskView> {
|
|||||||
decoration: isCompleted ? TextDecoration.lineThrough : null,
|
decoration: isCompleted ? TextDecoration.lineThrough : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
final displayName = task.displayName;
|
final displayName =
|
||||||
final description = task.description;
|
task.displayName != null
|
||||||
|
? localizeKnownText(context, task.displayName!)
|
||||||
|
: null;
|
||||||
|
final description =
|
||||||
|
task.description != null
|
||||||
|
? localizeKnownText(context, task.description!)
|
||||||
|
: null;
|
||||||
|
|
||||||
if (displayName?.isNotEmpty == true) {
|
if (displayName?.isNotEmpty == true) {
|
||||||
return Column(
|
return Column(
|
||||||
@@ -785,12 +792,10 @@ class _TaskViewState extends State<TaskView> {
|
|||||||
if (station.stationOrder == stationOrder) {
|
if (station.stationOrder == stationOrder) {
|
||||||
final suffix =
|
final suffix =
|
||||||
station.displayName.isNotEmpty ? station.displayName : station.city;
|
station.displayName.isNotEmpty ? station.displayName : station.city;
|
||||||
return suffix.isNotEmpty
|
return localizedStationLabel(context, stationOrder + 1, suffix: suffix);
|
||||||
? 'Station ${stationOrder + 1}: $suffix'
|
|
||||||
: 'Station ${stationOrder + 1}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Station ${stationOrder + 1}';
|
return AppLocalizations.of(context).stationNumber(stationOrder + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.9.12+1
|
version: 0.9.15+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.7.0
|
sdk: ^3.7.0
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>0.9.14</revision>
|
<revision>0.9.15</revision>
|
||||||
<java.version>21</java.version>
|
<java.version>21</java.version>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import org.bson.types.ObjectId;
|
|||||||
* Normalized payload for chat messages sent by mobile clients via WebSocket.
|
* Normalized payload for chat messages sent by mobile clients via WebSocket.
|
||||||
* receiver = AppUser ID (clientId) extracted from topic
|
* receiver = AppUser ID (clientId) extracted from topic
|
||||||
*/
|
*/
|
||||||
public record ChatMessageInboundPayload(String receiver, String content, MessageContentType contentType, ObjectId jobId,
|
public record ChatMessageInboundPayload(String receiver, String content, MessageContentType contentType,
|
||||||
String jobNumber) {
|
String messageId, ObjectId jobId, String jobNumber) {
|
||||||
|
|
||||||
public ChatMessageInboundPayload {
|
public ChatMessageInboundPayload {
|
||||||
contentType = contentType != null ? contentType : MessageContentType.TEXT;
|
contentType = contentType != null ? contentType : MessageContentType.TEXT;
|
||||||
@@ -23,10 +23,11 @@ public record ChatMessageInboundPayload(String receiver, String content, Message
|
|||||||
String receiver = extractRequiredString(payload, "receiver");
|
String receiver = extractRequiredString(payload, "receiver");
|
||||||
String content = extractRequiredString(payload, "content");
|
String content = extractRequiredString(payload, "content");
|
||||||
MessageContentType contentType = extractContentType(payload.get("contentType"));
|
MessageContentType contentType = extractContentType(payload.get("contentType"));
|
||||||
|
String messageId = extractOptionalString(payload.get("messageId"));
|
||||||
ObjectId jobId = extractObjectId(payload.get("jobId"), "jobId");
|
ObjectId jobId = extractObjectId(payload.get("jobId"), "jobId");
|
||||||
String jobNumber = extractOptionalString(payload.get("jobNumber"));
|
String jobNumber = extractOptionalString(payload.get("jobNumber"));
|
||||||
|
|
||||||
return new ChatMessageInboundPayload(receiver, content, contentType, jobId, jobNumber);
|
return new ChatMessageInboundPayload(receiver, content, contentType, messageId, jobId, jobNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasJobContext() {
|
public boolean hasJobContext() {
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ public class Message {
|
|||||||
@Field("receiver")
|
@Field("receiver")
|
||||||
private String receiver;
|
private String receiver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional stable client-side ID used for idempotent retries from the mobile
|
||||||
|
* app.
|
||||||
|
*/
|
||||||
|
@Field("client_message_id")
|
||||||
|
private String clientMessageId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp when the message was created
|
* Timestamp when the message was created
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import com.vaadin.flow.component.button.Button;
|
|||||||
import com.vaadin.flow.component.button.ButtonVariant;
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
import com.vaadin.flow.component.checkbox.Checkbox;
|
import com.vaadin.flow.component.checkbox.Checkbox;
|
||||||
import com.vaadin.flow.component.combobox.ComboBox;
|
import com.vaadin.flow.component.combobox.ComboBox;
|
||||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
|
||||||
import com.vaadin.flow.component.dialog.Dialog;
|
import com.vaadin.flow.component.dialog.Dialog;
|
||||||
import com.vaadin.flow.component.html.Div;
|
import com.vaadin.flow.component.html.Div;
|
||||||
import com.vaadin.flow.component.html.H3;
|
import com.vaadin.flow.component.html.H3;
|
||||||
@@ -441,25 +440,21 @@ public class DeliveryStationDialog extends Dialog {
|
|||||||
close();
|
close();
|
||||||
} else {
|
} else {
|
||||||
// Adresse nicht gefunden: Benutzer fragen
|
// Adresse nicht gefunden: Benutzer fragen
|
||||||
ConfirmDialog confirmDialog = new ConfirmDialog();
|
Dialog confirmDialog = DialogStylingHelper.createConfirmationDialog(
|
||||||
confirmDialog.setHeader(
|
translationHelper.getTranslation("addjob.validation.address.not.found.title"),
|
||||||
translationHelper.getTranslation("addjob.validation.address.not.found.title"));
|
translationHelper.getTranslation("addjob.validation.address.not.found.message"),
|
||||||
confirmDialog.setText(
|
"560px",
|
||||||
translationHelper.getTranslation("addjob.validation.address.not.found.message"));
|
translationHelper.getTranslation("addjob.validation.address.correct"),
|
||||||
confirmDialog.setConfirmText(
|
translationHelper.getTranslation("addjob.validation.address.save.anyway"),
|
||||||
translationHelper.getTranslation("addjob.validation.address.save.anyway"));
|
() -> {
|
||||||
confirmDialog.setConfirmButtonTheme("primary");
|
|
||||||
confirmDialog.setCancelable(true);
|
|
||||||
confirmDialog.setCancelText(
|
|
||||||
translationHelper.getTranslation("addjob.validation.address.correct"));
|
|
||||||
confirmDialog.addConfirmListener(ev -> {
|
|
||||||
data.setAddressValidatedByGoogle(false);
|
data.setAddressValidatedByGoogle(false);
|
||||||
data.setAddressValidationResult(validationResult);
|
data.setAddressValidationResult(validationResult);
|
||||||
if (saveListener != null) {
|
if (saveListener != null) {
|
||||||
saveListener.onSave(data);
|
saveListener.onSave(data);
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
});
|
},
|
||||||
|
ButtonVariant.LUMO_PRIMARY);
|
||||||
confirmDialog.open();
|
confirmDialog.open();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package de.assecutor.votianlt.pages.base.ui.component;
|
package de.assecutor.votianlt.pages.base.ui.component;
|
||||||
|
|
||||||
import com.vaadin.flow.component.Component;
|
import com.vaadin.flow.component.Component;
|
||||||
|
import com.vaadin.flow.component.button.Button;
|
||||||
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
import com.vaadin.flow.component.dialog.Dialog;
|
import com.vaadin.flow.component.dialog.Dialog;
|
||||||
import com.vaadin.flow.component.html.Div;
|
import com.vaadin.flow.component.html.Div;
|
||||||
|
import com.vaadin.flow.component.html.Span;
|
||||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
|
|
||||||
@@ -20,6 +23,33 @@ public final class DialogStylingHelper {
|
|||||||
return dialog;
|
return dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Dialog createConfirmationDialog(String title, String message, String width, String cancelText,
|
||||||
|
String confirmText, Runnable onConfirm, ButtonVariant... confirmVariants) {
|
||||||
|
Dialog dialog = createStyledDialog(title, width);
|
||||||
|
dialog.setCloseOnEsc(true);
|
||||||
|
dialog.setCloseOnOutsideClick(true);
|
||||||
|
|
||||||
|
VerticalLayout dialogContent = createContentLayout("320px");
|
||||||
|
if (message != null && !message.isBlank()) {
|
||||||
|
dialogContent.add(new Span(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
Button cancelButton = new Button(cancelText, event -> dialog.close());
|
||||||
|
cancelButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||||
|
|
||||||
|
Button confirmButton = new Button(confirmText, event -> {
|
||||||
|
dialog.close();
|
||||||
|
onConfirm.run();
|
||||||
|
});
|
||||||
|
if (confirmVariants != null && confirmVariants.length > 0) {
|
||||||
|
confirmButton.addThemeVariants(confirmVariants);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.add(wrapContent(dialogContent));
|
||||||
|
dialog.getFooter().add(cancelButton, confirmButton);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
public static void apply(Dialog dialog, String title, String width) {
|
public static void apply(Dialog dialog, String title, String width) {
|
||||||
if (title != null && !title.isBlank()) {
|
if (title != null && !title.isBlank()) {
|
||||||
dialog.setHeaderTitle(title);
|
dialog.setHeaderTitle(title);
|
||||||
@@ -32,16 +62,40 @@ public final class DialogStylingHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Component wrapContent(Component content) {
|
public static Component wrapContent(Component content) {
|
||||||
|
return wrapContent(content, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Component wrapContent(Component content, boolean fillHeight) {
|
||||||
Div frame = new Div();
|
Div frame = new Div();
|
||||||
frame.getStyle().set("border", "10px solid transparent");
|
frame.getStyle().set("border", "10px solid transparent");
|
||||||
frame.getStyle().set("border-radius", "0");
|
frame.getStyle().set("border-radius", "0");
|
||||||
frame.getStyle().set("box-sizing", "border-box");
|
frame.getStyle().set("box-sizing", "border-box");
|
||||||
|
if (fillHeight) {
|
||||||
|
frame.getStyle().set("display", "flex");
|
||||||
|
frame.getStyle().set("flex-direction", "column");
|
||||||
|
frame.getStyle().set("height", "100%");
|
||||||
|
frame.getStyle().set("min-height", "0");
|
||||||
|
frame.getStyle().set("flex", "1");
|
||||||
|
}
|
||||||
frame.setWidthFull();
|
frame.setWidthFull();
|
||||||
|
|
||||||
Div whiteCard = new Div();
|
Div whiteCard = new Div();
|
||||||
whiteCard.getStyle().set("background", "white");
|
whiteCard.getStyle().set("background", "white");
|
||||||
whiteCard.getStyle().set("border-radius", "24px");
|
whiteCard.getStyle().set("border-radius", "24px");
|
||||||
|
if (fillHeight) {
|
||||||
|
whiteCard.getStyle().set("display", "flex");
|
||||||
|
whiteCard.getStyle().set("flex-direction", "column");
|
||||||
|
whiteCard.getStyle().set("height", "100%");
|
||||||
|
whiteCard.getStyle().set("min-height", "0");
|
||||||
|
whiteCard.getStyle().set("flex", "1");
|
||||||
|
whiteCard.getStyle().set("overflow", "hidden");
|
||||||
|
content.getElement().getStyle().set("width", "100%");
|
||||||
|
content.getElement().getStyle().set("height", "100%");
|
||||||
|
content.getElement().getStyle().set("min-height", "0");
|
||||||
|
content.getElement().getStyle().set("flex", "1");
|
||||||
|
} else {
|
||||||
whiteCard.getStyle().set("overflow", "auto");
|
whiteCard.getStyle().set("overflow", "auto");
|
||||||
|
}
|
||||||
whiteCard.setWidthFull();
|
whiteCard.setWidthFull();
|
||||||
whiteCard.add(content);
|
whiteCard.add(content);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import com.vaadin.flow.component.textfield.NumberField;
|
|||||||
import com.vaadin.flow.component.textfield.TextField;
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
import com.vaadin.flow.component.timepicker.TimePicker;
|
import com.vaadin.flow.component.timepicker.TimePicker;
|
||||||
import com.vaadin.flow.component.UI;
|
import com.vaadin.flow.component.UI;
|
||||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
|
||||||
import com.vaadin.flow.component.notification.Notification;
|
import com.vaadin.flow.component.notification.Notification;
|
||||||
import com.vaadin.flow.component.progressbar.ProgressBar;
|
import com.vaadin.flow.component.progressbar.ProgressBar;
|
||||||
import de.assecutor.votianlt.model.AddressValidationResult;
|
import de.assecutor.votianlt.model.AddressValidationResult;
|
||||||
@@ -577,25 +576,21 @@ public class PickupStationDialog extends Dialog {
|
|||||||
close();
|
close();
|
||||||
} else {
|
} else {
|
||||||
// Adresse nicht gefunden: Benutzer fragen
|
// Adresse nicht gefunden: Benutzer fragen
|
||||||
ConfirmDialog confirmDialog = new ConfirmDialog();
|
Dialog confirmDialog = DialogStylingHelper.createConfirmationDialog(
|
||||||
confirmDialog.setHeader(
|
translationHelper.getTranslation("addjob.validation.address.not.found.title"),
|
||||||
translationHelper.getTranslation("addjob.validation.address.not.found.title"));
|
translationHelper.getTranslation("addjob.validation.address.not.found.message"),
|
||||||
confirmDialog.setText(
|
"560px",
|
||||||
translationHelper.getTranslation("addjob.validation.address.not.found.message"));
|
translationHelper.getTranslation("addjob.validation.address.correct"),
|
||||||
confirmDialog.setConfirmText(
|
translationHelper.getTranslation("addjob.validation.address.save.anyway"),
|
||||||
translationHelper.getTranslation("addjob.validation.address.save.anyway"));
|
() -> {
|
||||||
confirmDialog.setConfirmButtonTheme("primary");
|
|
||||||
confirmDialog.setCancelable(true);
|
|
||||||
confirmDialog.setCancelText(
|
|
||||||
translationHelper.getTranslation("addjob.validation.address.correct"));
|
|
||||||
confirmDialog.addConfirmListener(ev -> {
|
|
||||||
data.setAddressValidatedByGoogle(false);
|
data.setAddressValidatedByGoogle(false);
|
||||||
data.setAddressValidationResult(validationResult);
|
data.setAddressValidationResult(validationResult);
|
||||||
if (saveListener != null) {
|
if (saveListener != null) {
|
||||||
saveListener.onSave(data);
|
saveListener.onSave(data);
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
});
|
},
|
||||||
|
ButtonVariant.LUMO_PRIMARY);
|
||||||
confirmDialog.open();
|
confirmDialog.open();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import com.vaadin.flow.component.sidenav.SideNav;
|
|||||||
import com.vaadin.flow.component.sidenav.SideNavItem;
|
import com.vaadin.flow.component.sidenav.SideNavItem;
|
||||||
import com.vaadin.flow.router.Layout;
|
import com.vaadin.flow.router.Layout;
|
||||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.DialogStylingHelper;
|
||||||
import de.assecutor.votianlt.pages.view.EditProfileView;
|
import de.assecutor.votianlt.pages.view.EditProfileView;
|
||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
|
|
||||||
@@ -136,7 +137,7 @@ public final class AdminLayout extends AppLayout {
|
|||||||
// Profile display with navigation
|
// Profile display with navigation
|
||||||
userMenuItem.getSubMenu().addItem("Profil anzeigen", e -> UI.getCurrent().navigate(EditProfileView.class));
|
userMenuItem.getSubMenu().addItem("Profil anzeigen", e -> UI.getCurrent().navigate(EditProfileView.class));
|
||||||
userMenuItem.getSubMenu().addItem("Admin-Einstellungen");
|
userMenuItem.getSubMenu().addItem("Admin-Einstellungen");
|
||||||
userMenuItem.getSubMenu().addItem("Abmelden", e -> securityService.logout());
|
userMenuItem.getSubMenu().addItem("Abmelden", e -> openLogoutConfirmDialog());
|
||||||
|
|
||||||
// Update function for username and avatar
|
// Update function for username and avatar
|
||||||
Runnable updateUserInfo = () -> {
|
Runnable updateUserInfo = () -> {
|
||||||
@@ -151,4 +152,17 @@ public final class AdminLayout extends AppLayout {
|
|||||||
|
|
||||||
return userMenu;
|
return userMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openLogoutConfirmDialog() {
|
||||||
|
var dialog = DialogStylingHelper.createConfirmationDialog(
|
||||||
|
getTranslation("logout.confirm.title"),
|
||||||
|
getTranslation("logout.confirm.message"),
|
||||||
|
"460px",
|
||||||
|
getTranslation("button.cancel"),
|
||||||
|
getTranslation("nav.logout"),
|
||||||
|
securityService::logout,
|
||||||
|
com.vaadin.flow.component.button.ButtonVariant.LUMO_PRIMARY,
|
||||||
|
com.vaadin.flow.component.button.ButtonVariant.LUMO_ERROR);
|
||||||
|
dialog.open();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.vaadin.flow.server.auth.AnonymousAllowed;
|
|||||||
import com.vaadin.flow.shared.Registration;
|
import com.vaadin.flow.shared.Registration;
|
||||||
import de.assecutor.votianlt.model.User;
|
import de.assecutor.votianlt.model.User;
|
||||||
import de.assecutor.votianlt.model.Language;
|
import de.assecutor.votianlt.model.Language;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.DialogStylingHelper;
|
||||||
import de.assecutor.votianlt.pages.service.AppUserService;
|
import de.assecutor.votianlt.pages.service.AppUserService;
|
||||||
import de.assecutor.votianlt.pages.view.EditProfileView;
|
import de.assecutor.votianlt.pages.view.EditProfileView;
|
||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
@@ -327,8 +328,7 @@ public final class MainLayout extends AppLayout {
|
|||||||
// Profil anzeigen mit Navigation
|
// Profil anzeigen mit Navigation
|
||||||
userMenuItem.getSubMenu().addItem(getTranslation("nav.showprofile"),
|
userMenuItem.getSubMenu().addItem(getTranslation("nav.showprofile"),
|
||||||
e -> UI.getCurrent().navigate(EditProfileView.class));
|
e -> UI.getCurrent().navigate(EditProfileView.class));
|
||||||
userMenuItem.getSubMenu().addItem(getTranslation("nav.settings"));
|
userMenuItem.getSubMenu().addItem(getTranslation("nav.logout"), e -> openLogoutConfirmDialog());
|
||||||
userMenuItem.getSubMenu().addItem(getTranslation("nav.logout"), e -> securityService.logout());
|
|
||||||
|
|
||||||
// Update-Funktion für Benutzername und Avatar
|
// Update-Funktion für Benutzername und Avatar
|
||||||
Runnable updateUserInfo = () -> {
|
Runnable updateUserInfo = () -> {
|
||||||
@@ -344,6 +344,19 @@ public final class MainLayout extends AppLayout {
|
|||||||
return userMenu;
|
return userMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openLogoutConfirmDialog() {
|
||||||
|
var dialog = DialogStylingHelper.createConfirmationDialog(
|
||||||
|
getTranslation("logout.confirm.title"),
|
||||||
|
getTranslation("logout.confirm.message"),
|
||||||
|
"460px",
|
||||||
|
getTranslation("button.cancel"),
|
||||||
|
getTranslation("nav.logout"),
|
||||||
|
securityService::logout,
|
||||||
|
com.vaadin.flow.component.button.ButtonVariant.LUMO_PRIMARY,
|
||||||
|
com.vaadin.flow.component.button.ButtonVariant.LUMO_ERROR);
|
||||||
|
dialog.open();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onAttach(AttachEvent attachEvent) {
|
protected void onAttach(AttachEvent attachEvent) {
|
||||||
super.onAttach(attachEvent);
|
super.onAttach(attachEvent);
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ public class AddCustomerView extends Main implements HasDynamicTitle {
|
|||||||
String value = mail.getValue();
|
String value = mail.getValue();
|
||||||
if (value == null || value.trim().isEmpty()) {
|
if (value == null || value.trim().isEmpty()) {
|
||||||
mail.setInvalid(true);
|
mail.setInvalid(true);
|
||||||
mail.setErrorMessage(getTranslation("profile.email.required"));
|
mail.setErrorMessage(getTranslation("profile.validation.email.required"));
|
||||||
} else if (!value.contains("@")) {
|
} else if (!value.contains("@")) {
|
||||||
mail.setInvalid(true);
|
mail.setInvalid(true);
|
||||||
mail.setErrorMessage(getTranslation("profile.validation.email.invalid"));
|
mail.setErrorMessage(getTranslation("profile.validation.email.invalid"));
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import com.vaadin.flow.component.button.Button;
|
|||||||
import com.vaadin.flow.component.button.ButtonVariant;
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
import com.vaadin.flow.component.checkbox.Checkbox;
|
import com.vaadin.flow.component.checkbox.Checkbox;
|
||||||
import com.vaadin.flow.component.combobox.ComboBox;
|
import com.vaadin.flow.component.combobox.ComboBox;
|
||||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
|
||||||
import com.vaadin.flow.component.dialog.Dialog;
|
import com.vaadin.flow.component.dialog.Dialog;
|
||||||
import com.vaadin.flow.component.UI;
|
import com.vaadin.flow.component.UI;
|
||||||
|
|
||||||
@@ -756,12 +755,13 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
if (idx < 0)
|
if (idx < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ConfirmDialog dialog = new ConfirmDialog();
|
Dialog dialog = DialogStylingHelper.createConfirmationDialog(
|
||||||
dialog.setHeader(getTranslation("addjob.station.remove.confirm", idx + 1));
|
getTranslation("addjob.station.remove.confirm", idx + 1),
|
||||||
dialog.setCancelable(true);
|
null,
|
||||||
dialog.setCancelText(getTranslation("dialog.cancel"));
|
"460px",
|
||||||
dialog.setConfirmText(getTranslation("dialog.confirm"));
|
getTranslation("dialog.cancel"),
|
||||||
dialog.addConfirmListener(e -> {
|
getTranslation("dialog.confirm"),
|
||||||
|
() -> {
|
||||||
int removeIdx = deliveryStationTilesList.indexOf(tile);
|
int removeIdx = deliveryStationTilesList.indexOf(tile);
|
||||||
if (removeIdx < 0)
|
if (removeIdx < 0)
|
||||||
return;
|
return;
|
||||||
@@ -823,7 +823,8 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure "+" button is visible if under max
|
// Ensure "+" button is visible if under max
|
||||||
if (deliveryStationTilesList.size() < MAX_DELIVERY_STATIONS && addStationButtonSlot.getParent().isEmpty()) {
|
if (deliveryStationTilesList.size() < MAX_DELIVERY_STATIONS
|
||||||
|
&& addStationButtonSlot.getParent().isEmpty()) {
|
||||||
stationsGridContainer.add(addStationButtonSlot);
|
stationsGridContainer.add(addStationButtonSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -835,7 +836,8 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
updatePriceSummary();
|
updatePriceSummary();
|
||||||
triggerValidation();
|
triggerValidation();
|
||||||
updateTabLabels();
|
updateTabLabels();
|
||||||
});
|
},
|
||||||
|
ButtonVariant.LUMO_PRIMARY);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import de.assecutor.votianlt.model.Service;
|
|||||||
import de.assecutor.votianlt.model.User;
|
import de.assecutor.votianlt.model.User;
|
||||||
import de.assecutor.votianlt.model.InvoiceTemplate;
|
import de.assecutor.votianlt.model.InvoiceTemplate;
|
||||||
import de.assecutor.votianlt.model.invoices.CustomerInvoice;
|
import de.assecutor.votianlt.model.invoices.CustomerInvoice;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.DialogStylingHelper;
|
||||||
import de.assecutor.votianlt.pages.service.CustomerService;
|
import de.assecutor.votianlt.pages.service.CustomerService;
|
||||||
import de.assecutor.votianlt.pages.service.UserInvoiceDataService;
|
import de.assecutor.votianlt.pages.service.UserInvoiceDataService;
|
||||||
import de.assecutor.votianlt.repository.CustomerInvoiceRepository;
|
import de.assecutor.votianlt.repository.CustomerInvoiceRepository;
|
||||||
@@ -45,7 +46,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
|
||||||
import com.vaadin.flow.component.dialog.Dialog;
|
import com.vaadin.flow.component.dialog.Dialog;
|
||||||
import com.vaadin.flow.component.html.IFrame;
|
import com.vaadin.flow.component.html.IFrame;
|
||||||
|
|
||||||
@@ -659,9 +659,7 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
private void showPdfPreviewDialog(byte[] pdfBytes, String templateData, User user) {
|
private void showPdfPreviewDialog(byte[] pdfBytes, String templateData, User user) {
|
||||||
String title = getTranslation("createinvoice.preview.title");
|
String title = getTranslation("createinvoice.preview.title");
|
||||||
|
|
||||||
Dialog pdfDialog = new Dialog();
|
Dialog pdfDialog = DialogStylingHelper.createStyledDialog(title, "90vw");
|
||||||
pdfDialog.setHeaderTitle(title);
|
|
||||||
pdfDialog.setWidth("90vw");
|
|
||||||
pdfDialog.setHeight("90vh");
|
pdfDialog.setHeight("90vh");
|
||||||
|
|
||||||
IFrame pdfFrame = new IFrame();
|
IFrame pdfFrame = new IFrame();
|
||||||
@@ -676,19 +674,19 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||||
|
|
||||||
Button saveButton = new Button(getTranslation("createinvoice.button.save"), e -> {
|
Button saveButton = new Button(getTranslation("createinvoice.button.save"), e -> {
|
||||||
ConfirmDialog confirm = new ConfirmDialog();
|
Dialog confirm = DialogStylingHelper.createConfirmationDialog(
|
||||||
confirm.setHeader(getTranslation("createinvoice.confirm.save.title"));
|
getTranslation("createinvoice.confirm.save.title"),
|
||||||
confirm.setText(getTranslation("createinvoice.confirm.save.message"));
|
getTranslation("createinvoice.confirm.save.message"),
|
||||||
confirm.setConfirmText(getTranslation("createinvoice.confirm.save.confirm"));
|
"560px",
|
||||||
confirm.setConfirmButtonTheme("primary");
|
getTranslation("button.cancel"),
|
||||||
confirm.setCancelText(getTranslation("button.cancel"));
|
getTranslation("createinvoice.confirm.save.confirm"),
|
||||||
confirm.setCancelable(true);
|
() -> saveInvoice(templateData, user, pdfDialog),
|
||||||
confirm.addConfirmListener(ev -> saveInvoice(templateData, user, pdfDialog));
|
ButtonVariant.LUMO_PRIMARY);
|
||||||
confirm.open();
|
confirm.open();
|
||||||
});
|
});
|
||||||
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
|
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
|
||||||
|
|
||||||
pdfDialog.add(pdfFrame);
|
pdfDialog.add(DialogStylingHelper.wrapContent(pdfFrame, true));
|
||||||
pdfDialog.getFooter().add(closeButton, saveButton);
|
pdfDialog.getFooter().add(closeButton, saveButton);
|
||||||
pdfDialog.open();
|
pdfDialog.open();
|
||||||
}
|
}
|
||||||
@@ -696,9 +694,7 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
public static void showSavedInvoiceDialog(byte[] pdfBytes, String invoiceNumber,
|
public static void showSavedInvoiceDialog(byte[] pdfBytes, String invoiceNumber,
|
||||||
com.vaadin.flow.component.Component parent) {
|
com.vaadin.flow.component.Component parent) {
|
||||||
String title = "Rechnung " + invoiceNumber;
|
String title = "Rechnung " + invoiceNumber;
|
||||||
Dialog pdfDialog = new Dialog();
|
Dialog pdfDialog = DialogStylingHelper.createStyledDialog(title, "90vw");
|
||||||
pdfDialog.setHeaderTitle(title);
|
|
||||||
pdfDialog.setWidth("90vw");
|
|
||||||
pdfDialog.setHeight("90vh");
|
pdfDialog.setHeight("90vh");
|
||||||
|
|
||||||
IFrame pdfFrame = new IFrame();
|
IFrame pdfFrame = new IFrame();
|
||||||
@@ -720,7 +716,7 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
Button closeButton = new Button("Schließen", e -> pdfDialog.close());
|
Button closeButton = new Button("Schließen", e -> pdfDialog.close());
|
||||||
closeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
closeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
|
|
||||||
pdfDialog.add(pdfFrame);
|
pdfDialog.add(DialogStylingHelper.wrapContent(pdfFrame, true));
|
||||||
pdfDialog.getFooter().add(downloadButton, closeButton);
|
pdfDialog.getFooter().add(downloadButton, closeButton);
|
||||||
pdfDialog.open();
|
pdfDialog.open();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ public class EditProfileView extends HorizontalLayout implements HasDynamicTitle
|
|||||||
TextField faxField = new TextField(getTranslation("profile.fax"));
|
TextField faxField = new TextField(getTranslation("profile.fax"));
|
||||||
TextField mobileField = new TextField(getTranslation("profile.mobile"));
|
TextField mobileField = new TextField(getTranslation("profile.mobile"));
|
||||||
|
|
||||||
EmailField emailField = new EmailField(getTranslation("profile.email.required"));
|
EmailField emailField = new EmailField(getTranslation("profile.email"));
|
||||||
emailField.addBlurListener(e -> validateEmailField(emailField));
|
emailField.addBlurListener(e -> validateEmailField(emailField));
|
||||||
|
|
||||||
TextField streetField = new TextField(getTranslation("profile.street"));
|
TextField streetField = new TextField(getTranslation("profile.street"));
|
||||||
@@ -882,9 +882,7 @@ public class EditProfileView extends HorizontalLayout implements HasDynamicTitle
|
|||||||
String dataUrl = "data:application/pdf;base64," + base64Pdf;
|
String dataUrl = "data:application/pdf;base64," + base64Pdf;
|
||||||
|
|
||||||
// Create dialog
|
// Create dialog
|
||||||
Dialog pdfDialog = new Dialog();
|
Dialog pdfDialog = DialogStylingHelper.createStyledDialog(getTranslation("profile.invoice.pdf.preview"), "90vw");
|
||||||
pdfDialog.setHeaderTitle(getTranslation("profile.invoice.pdf.preview"));
|
|
||||||
pdfDialog.setWidth("90vw");
|
|
||||||
pdfDialog.setHeight("90vh");
|
pdfDialog.setHeight("90vh");
|
||||||
|
|
||||||
// Create a Div to hold the PDF viewer
|
// Create a Div to hold the PDF viewer
|
||||||
@@ -914,7 +912,7 @@ public class EditProfileView extends HorizontalLayout implements HasDynamicTitle
|
|||||||
});
|
});
|
||||||
downloadButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
downloadButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||||
|
|
||||||
pdfDialog.add(pdfContainer);
|
pdfDialog.add(DialogStylingHelper.wrapContent(pdfContainer, true));
|
||||||
pdfDialog.getFooter().add(downloadButton, closeButton);
|
pdfDialog.getFooter().add(downloadButton, closeButton);
|
||||||
pdfDialog.open();
|
pdfDialog.open();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package de.assecutor.votianlt.pages.view;
|
package de.assecutor.votianlt.pages.view;
|
||||||
|
|
||||||
|
import com.vaadin.flow.component.Component;
|
||||||
|
import com.vaadin.flow.component.html.Anchor;
|
||||||
import com.vaadin.flow.component.html.Div;
|
import com.vaadin.flow.component.html.Div;
|
||||||
|
import com.vaadin.flow.component.html.H5;
|
||||||
|
import com.vaadin.flow.component.html.Span;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import com.vaadin.flow.router.HasDynamicTitle;
|
import com.vaadin.flow.router.HasDynamicTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import jakarta.annotation.security.PermitAll;
|
import jakarta.annotation.security.PermitAll;
|
||||||
@@ -25,29 +28,82 @@ public class ImprintView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
ViewToolbar toolbar = new ViewToolbar(getTranslation("page.title.imprint"));
|
ViewToolbar toolbar = new ViewToolbar(getTranslation("page.title.imprint"));
|
||||||
content.add(toolbar);
|
content.add(toolbar);
|
||||||
|
|
||||||
try {
|
Div imprintCard = new Div();
|
||||||
// Load HTML content from resources
|
imprintCard.addClassNames("form-card", "form-shell");
|
||||||
ClassPathResource resource = new ClassPathResource("html/imprint.html");
|
imprintCard.getStyle().set("max-width", "800px").set("margin", "0 auto");
|
||||||
String htmlContent = new String(resource.getInputStream().readAllBytes());
|
|
||||||
|
|
||||||
// Create a Div to hold the HTML content
|
VerticalLayout imprintContent = new VerticalLayout();
|
||||||
Div imprintDiv = new Div();
|
imprintContent.setPadding(false);
|
||||||
imprintDiv.addClassNames("form-card", "form-shell");
|
imprintContent.setSpacing(false);
|
||||||
imprintDiv.getElement().setProperty("innerHTML", htmlContent);
|
imprintContent.getStyle().set("gap", "var(--lumo-space-l)");
|
||||||
|
|
||||||
content.add(imprintDiv);
|
imprintContent.add(
|
||||||
|
createSection("Assecutor Data Service GmbH",
|
||||||
|
createLinesBlock(createLine("Gerhart-Hauptmann-Weg 14"), createLine("21502 Geesthacht"),
|
||||||
|
createLine(getTranslation("imprint.country")),
|
||||||
|
createLine(getTranslation("imprint.phone") + ": +49 40 18 123 771 0"),
|
||||||
|
createEmailLine())),
|
||||||
|
createSection(getTranslation("imprint.management"),
|
||||||
|
createLinesBlock(createLine("Carsten Annacker"), createLine("Gunnar Timm"))),
|
||||||
|
createSection(getTranslation("imprint.registeredoffice"),
|
||||||
|
createLinesBlock(createLine("Gerhart-Hauptmann-Weg 14, 21502 Geesthacht"))),
|
||||||
|
createSection(getTranslation("imprint.commercialregister"),
|
||||||
|
createLinesBlock(createLine("HRB 8595 HL"))),
|
||||||
|
createSection(getTranslation("imprint.vatid"), createLinesBlock(createLine("DE261094748"))),
|
||||||
|
createSection(getTranslation("imprint.imagecredits"),
|
||||||
|
createLinesBlock(createSectionHeading(getTranslation("imprint.backgroundimage")),
|
||||||
|
createLine("MAN Financial Services (EURO-Leasing), flickr"),
|
||||||
|
createLine(
|
||||||
|
"(Creative Commons, Attribution-ShareAlike 2.0 Generic (CC BY-SA 2.0))"),
|
||||||
|
createExternalLink(
|
||||||
|
"https://www.flickr.com/photos/mbwa_pr/15969764443/in/album-72157632488355514/"))));
|
||||||
|
|
||||||
} catch (Exception e) {
|
imprintCard.add(imprintContent);
|
||||||
// Fallback content in case of error
|
content.add(imprintCard);
|
||||||
Div errorDiv = new Div();
|
|
||||||
errorDiv.addClassNames("form-card", "form-shell");
|
|
||||||
errorDiv.setText(getTranslation("imprint.error", e.getMessage()));
|
|
||||||
content.add(errorDiv);
|
|
||||||
}
|
|
||||||
|
|
||||||
add(content);
|
add(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Component createSection(String title, Component body) {
|
||||||
|
Div section = new Div();
|
||||||
|
section.getStyle().set("text-align", "left");
|
||||||
|
section.add(createSectionHeading(title), body);
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
private H5 createSectionHeading(String title) {
|
||||||
|
H5 heading = new H5(title);
|
||||||
|
heading.getStyle().set("margin", "0 0 var(--lumo-space-s) 0");
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Div createLinesBlock(Component... lines) {
|
||||||
|
Div block = new Div();
|
||||||
|
block.getStyle().set("display", "flex").set("flex-direction", "column").set("gap", "4px");
|
||||||
|
block.add(lines);
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Span createLine(String text) {
|
||||||
|
Span line = new Span(text);
|
||||||
|
line.getStyle().set("display", "block");
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Div createEmailLine() {
|
||||||
|
Div line = new Div();
|
||||||
|
line.getStyle().set("display", "block");
|
||||||
|
line.add(new Span(getTranslation("imprint.email") + ": "));
|
||||||
|
line.add(new Anchor("mailto:ahoi@assecutor.de", "ahoi@assecutor.de"));
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Anchor createExternalLink(String href) {
|
||||||
|
Anchor link = new Anchor(href, href);
|
||||||
|
link.setTarget("_blank");
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPageTitle() {
|
public String getPageTitle() {
|
||||||
return getTranslation("page.title.imprint");
|
return getTranslation("page.title.imprint");
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import com.vaadin.flow.component.textfield.TextField;
|
|||||||
import com.vaadin.flow.component.html.Input;
|
import com.vaadin.flow.component.html.Input;
|
||||||
import com.vaadin.flow.component.dialog.Dialog;
|
import com.vaadin.flow.component.dialog.Dialog;
|
||||||
import com.vaadin.flow.component.html.IFrame;
|
import com.vaadin.flow.component.html.IFrame;
|
||||||
import com.vaadin.flow.server.StreamResource;
|
|
||||||
import elemental.json.JsonValue;
|
import elemental.json.JsonValue;
|
||||||
import elemental.json.JsonType;
|
import elemental.json.JsonType;
|
||||||
import com.vaadin.flow.component.upload.Upload;
|
import com.vaadin.flow.component.upload.Upload;
|
||||||
@@ -306,15 +305,8 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showPdfInDialog(byte[] pdfBytes) {
|
private void showPdfInDialog(byte[] pdfBytes) {
|
||||||
// Create a stream resource for the PDF
|
Dialog pdfDialog = DialogStylingHelper.createStyledDialog(getTranslation("invoicegenerator.pdf.preview.title"),
|
||||||
StreamResource resource = new StreamResource("preview.pdf", () -> new java.io.ByteArrayInputStream(pdfBytes));
|
"90vw");
|
||||||
resource.setContentType("application/pdf");
|
|
||||||
resource.setCacheTime(0);
|
|
||||||
|
|
||||||
// Create dialog
|
|
||||||
Dialog pdfDialog = new Dialog();
|
|
||||||
pdfDialog.setHeaderTitle(getTranslation("invoicegenerator.pdf.preview.title"));
|
|
||||||
pdfDialog.setWidth("90vw");
|
|
||||||
pdfDialog.setHeight("90vh");
|
pdfDialog.setHeight("90vh");
|
||||||
|
|
||||||
// Create a Div to hold the PDF viewer
|
// Create a Div to hold the PDF viewer
|
||||||
@@ -347,7 +339,7 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
});
|
});
|
||||||
downloadButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
downloadButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||||
|
|
||||||
pdfDialog.add(pdfContainer);
|
pdfDialog.add(DialogStylingHelper.wrapContent(pdfContainer, true));
|
||||||
pdfDialog.getFooter().add(downloadButton, closeButton);
|
pdfDialog.getFooter().add(downloadButton, closeButton);
|
||||||
pdfDialog.open();
|
pdfDialog.open();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import de.assecutor.votianlt.model.JobHistoryType;
|
|||||||
import de.assecutor.votianlt.model.Barcode;
|
import de.assecutor.votianlt.model.Barcode;
|
||||||
import de.assecutor.votianlt.model.Photo;
|
import de.assecutor.votianlt.model.Photo;
|
||||||
import de.assecutor.votianlt.model.Signature;
|
import de.assecutor.votianlt.model.Signature;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.DialogStylingHelper;
|
||||||
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import de.assecutor.votianlt.repository.BarcodeRepository;
|
import de.assecutor.votianlt.repository.BarcodeRepository;
|
||||||
import de.assecutor.votianlt.repository.JobRepository;
|
import de.assecutor.votianlt.repository.JobRepository;
|
||||||
@@ -182,8 +183,7 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
Icon typeIcon = getTypeIcon(entry.getChangeType());
|
Icon typeIcon = getTypeIcon(entry.getChangeType());
|
||||||
typeIcon.getStyle().set("color", getTypeColor(entry.getChangeType()));
|
typeIcon.getStyle().set("color", getTypeColor(entry.getChangeType()));
|
||||||
|
|
||||||
Span reason = new Span(
|
Span reason = new Span(getLocalizedReason(entry));
|
||||||
entry.getReason() != null ? entry.getReason() : getTranslation("jobhistory.entry.unknown"));
|
|
||||||
reason.addClassName("timeline-reason");
|
reason.addClassName("timeline-reason");
|
||||||
|
|
||||||
Span timestamp = new Span(formatDateTime(entry.getTimestamp()));
|
Span timestamp = new Span(formatDateTime(entry.getTimestamp()));
|
||||||
@@ -202,8 +202,9 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
cardContent.add(headerRow);
|
cardContent.add(headerRow);
|
||||||
|
|
||||||
// Description
|
// Description
|
||||||
if (entry.getDescription() != null && !entry.getDescription().isBlank()) {
|
String localizedDescription = getLocalizedDescription(entry);
|
||||||
Span description = new Span(entry.getDescription());
|
if (localizedDescription != null && !localizedDescription.isBlank()) {
|
||||||
|
Span description = new Span(localizedDescription);
|
||||||
description.addClassName("timeline-description");
|
description.addClassName("timeline-description");
|
||||||
cardContent.add(description);
|
cardContent.add(description);
|
||||||
}
|
}
|
||||||
@@ -252,6 +253,37 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getLocalizedReason(JobHistory entry) {
|
||||||
|
if (entry == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (entry.getChangeType() == JobHistoryType.CREATE) {
|
||||||
|
return getTranslation("jobhistory.entry.create.reason");
|
||||||
|
}
|
||||||
|
return entry.getReason() != null ? entry.getReason() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getLocalizedDescription(JobHistory entry) {
|
||||||
|
if (entry == null || entry.getDescription() == null || entry.getDescription().isBlank()) {
|
||||||
|
return entry != null ? entry.getDescription() : "";
|
||||||
|
}
|
||||||
|
if (entry.getChangeType() == JobHistoryType.CREATE) {
|
||||||
|
return getTranslation("jobhistory.entry.create.description", extractDescriptionValue(entry.getDescription()));
|
||||||
|
}
|
||||||
|
return entry.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractDescriptionValue(String description) {
|
||||||
|
if (description == null || description.isBlank()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int separatorIndex = description.indexOf(':');
|
||||||
|
if (separatorIndex < 0 || separatorIndex == description.length() - 1) {
|
||||||
|
return description.trim();
|
||||||
|
}
|
||||||
|
return description.substring(separatorIndex + 1).trim();
|
||||||
|
}
|
||||||
|
|
||||||
private Icon getTypeIcon(JobHistoryType type) {
|
private Icon getTypeIcon(JobHistoryType type) {
|
||||||
if (type == null)
|
if (type == null)
|
||||||
return new Icon(VaadinIcon.INFO_CIRCLE);
|
return new Icon(VaadinIcon.INFO_CIRCLE);
|
||||||
@@ -300,15 +332,15 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
private String formatStatus(de.assecutor.votianlt.model.JobStatus status) {
|
private String formatStatus(de.assecutor.votianlt.model.JobStatus status) {
|
||||||
if (status == null)
|
if (status == null)
|
||||||
return getTranslation("jobhistory.entry.unknown");
|
return "";
|
||||||
|
|
||||||
return switch (status) {
|
return switch (status) {
|
||||||
case CREATED -> getTranslation("jobstatus.CREATED");
|
case CREATED -> getTranslation("jobstatus.CREATED");
|
||||||
case IN_PROGRESS -> getTranslation("jobstatus.IN_PROGRESS");
|
case IN_PROGRESS -> getTranslation("jobstatus.IN_PROGRESS");
|
||||||
case PICKUP_SCHEDULED -> "Abholung geplant";
|
case PICKUP_SCHEDULED -> getTranslation("jobhistory.status.pickupscheduled");
|
||||||
case PICKED_UP -> "Abgeholt";
|
case PICKED_UP -> getTranslation("jobhistory.status.pickedup");
|
||||||
case IN_TRANSIT -> "Unterwegs";
|
case IN_TRANSIT -> getTranslation("jobhistory.status.intransit");
|
||||||
case DELIVERED -> "Zugestellt";
|
case DELIVERED -> getTranslation("jobhistory.status.delivered");
|
||||||
case COMPLETED -> getTranslation("jobstatus.COMPLETED");
|
case COMPLETED -> getTranslation("jobstatus.COMPLETED");
|
||||||
case CANCELLED -> getTranslation("jobstatus.CANCELLED");
|
case CANCELLED -> getTranslation("jobstatus.CANCELLED");
|
||||||
};
|
};
|
||||||
@@ -393,8 +425,7 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showEnlargedPhoto(String base64Photo) {
|
private void showEnlargedPhoto(String base64Photo) {
|
||||||
Dialog photoDialog = new Dialog();
|
Dialog photoDialog = DialogStylingHelper.createStyledDialog(getTranslation("jobhistory.image.alt"), "80vw");
|
||||||
photoDialog.setWidth("80vw");
|
|
||||||
photoDialog.setHeight("80vh");
|
photoDialog.setHeight("80vh");
|
||||||
photoDialog.setModal(true);
|
photoDialog.setModal(true);
|
||||||
photoDialog.setCloseOnOutsideClick(true);
|
photoDialog.setCloseOnOutsideClick(true);
|
||||||
@@ -412,7 +443,7 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
dialogContent.setJustifyContentMode(VerticalLayout.JustifyContentMode.CENTER);
|
dialogContent.setJustifyContentMode(VerticalLayout.JustifyContentMode.CENTER);
|
||||||
dialogContent.setSizeFull();
|
dialogContent.setSizeFull();
|
||||||
|
|
||||||
photoDialog.add(dialogContent);
|
photoDialog.add(DialogStylingHelper.wrapContent(dialogContent, true));
|
||||||
photoDialog.open();
|
photoDialog.open();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -549,8 +580,7 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showEnlargedSignature(String svgContent) {
|
private void showEnlargedSignature(String svgContent) {
|
||||||
Dialog signatureDialog = new Dialog();
|
Dialog signatureDialog = DialogStylingHelper.createStyledDialog(getTranslation("tasktype.SIGNATURE"), "60vw");
|
||||||
signatureDialog.setWidth("60vw");
|
|
||||||
signatureDialog.setHeight("40vh");
|
signatureDialog.setHeight("40vh");
|
||||||
signatureDialog.setModal(true);
|
signatureDialog.setModal(true);
|
||||||
signatureDialog.setCloseOnOutsideClick(true);
|
signatureDialog.setCloseOnOutsideClick(true);
|
||||||
@@ -567,7 +597,7 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
dialogContent.setPadding(true);
|
dialogContent.setPadding(true);
|
||||||
|
|
||||||
dialogContent.add(enlargedSignature);
|
dialogContent.add(enlargedSignature);
|
||||||
signatureDialog.add(dialogContent);
|
signatureDialog.add(DialogStylingHelper.wrapContent(dialogContent, true));
|
||||||
signatureDialog.open();
|
signatureDialog.open();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ import de.assecutor.votianlt.service.LocationService;
|
|||||||
import de.assecutor.votianlt.service.MessageService;
|
import de.assecutor.votianlt.service.MessageService;
|
||||||
import de.assecutor.votianlt.service.TaskAssignmentService;
|
import de.assecutor.votianlt.service.TaskAssignmentService;
|
||||||
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
||||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@@ -135,8 +134,8 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
if (parameter == null || parameter.isBlank()) {
|
if (parameter == null || parameter.isBlank()) {
|
||||||
content.removeAll();
|
content.removeAll();
|
||||||
removeAll();
|
removeAll();
|
||||||
add(new ViewToolbar("Zusammenfassung"));
|
add(new ViewToolbar(getTranslation("jobsummary.title")));
|
||||||
content.add(new Span("Fehler: Keine Job-ID angegeben"));
|
content.add(new Span(getTranslation("jobsummary.error.noid")));
|
||||||
add(content);
|
add(content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -146,8 +145,8 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
content.removeAll();
|
content.removeAll();
|
||||||
removeAll();
|
removeAll();
|
||||||
add(new ViewToolbar("Zusammenfassung"));
|
add(new ViewToolbar(getTranslation("jobsummary.title")));
|
||||||
content.add(new Span("Fehler: Ungültige Job-ID Format: " + parameter));
|
content.add(new Span(getTranslation("jobsummary.error.invalidid", parameter)));
|
||||||
add(content);
|
add(content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -186,8 +185,8 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
Job job = jobRepository.findById(currentJobId).orElse(null);
|
Job job = jobRepository.findById(currentJobId).orElse(null);
|
||||||
if (job == null) {
|
if (job == null) {
|
||||||
add(new ViewToolbar("Zusammenfassung"));
|
add(new ViewToolbar(getTranslation("jobsummary.title")));
|
||||||
content.add(new Span("Fehler: Job mit ID " + currentJobId.toHexString() + " nicht gefunden"));
|
content.add(new Span(getTranslation("jobsummary.error.notfound", currentJobId.toHexString())));
|
||||||
add(content);
|
add(content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -221,7 +220,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add toolbar with both buttons in top right (Send Message button on the left)
|
// Add toolbar with both buttons in top right (Send Message button on the left)
|
||||||
add(new ViewToolbar("Zusammenfassung", sendMessageButton, jobHistoryButton));
|
add(new ViewToolbar(getTranslation("jobsummary.title"), sendMessageButton, jobHistoryButton));
|
||||||
|
|
||||||
List<CargoItem> cargo = cargoItemRepository.findByJobId(currentJobId);
|
List<CargoItem> cargo = cargoItemRepository.findByJobId(currentJobId);
|
||||||
List<BaseTask> tasks = taskAssignmentService.findTasksForJob(job);
|
List<BaseTask> tasks = taskAssignmentService.findTasksForJob(job);
|
||||||
@@ -325,14 +324,13 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
new Icon(VaadinIcon.CHECK_CIRCLE));
|
new Icon(VaadinIcon.CHECK_CIRCLE));
|
||||||
completeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
|
completeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
|
||||||
completeButton.addClickListener(e -> {
|
completeButton.addClickListener(e -> {
|
||||||
ConfirmDialog dialog = new ConfirmDialog();
|
Dialog dialog = DialogStylingHelper.createConfirmationDialog(
|
||||||
dialog.setHeader(getTranslation("jobsummary.dialog.complete.title"));
|
getTranslation("jobsummary.dialog.complete.title"),
|
||||||
dialog.setText(getTranslation("jobsummary.dialog.complete.text", job.getJobNumber()));
|
getTranslation("jobsummary.dialog.complete.text", job.getJobNumber()),
|
||||||
dialog.setCancelable(true);
|
"560px",
|
||||||
dialog.setCancelText(getTranslation("jobsummary.dialog.complete.cancel"));
|
getTranslation("jobsummary.dialog.complete.cancel"),
|
||||||
dialog.setConfirmText(getTranslation("jobsummary.dialog.complete.confirm"));
|
getTranslation("jobsummary.dialog.complete.confirm"),
|
||||||
dialog.setConfirmButtonTheme("primary");
|
() -> {
|
||||||
dialog.addConfirmListener(ev -> {
|
|
||||||
try {
|
try {
|
||||||
JobStatus oldStatus = job.getStatus();
|
JobStatus oldStatus = job.getStatus();
|
||||||
job.setStatus(JobStatus.COMPLETED);
|
job.setStatus(JobStatus.COMPLETED);
|
||||||
@@ -340,18 +338,19 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
jobRepository.save(job);
|
jobRepository.save(job);
|
||||||
jobHistoryService.logStatusChange(job, oldStatus, JobStatus.COMPLETED, "Manuell");
|
jobHistoryService.logStatusChange(job, oldStatus, JobStatus.COMPLETED, "Manuell");
|
||||||
Notification
|
Notification
|
||||||
.show(getTranslation("jobsummary.notification.completed", job.getJobNumber()), 3000,
|
.show(getTranslation("jobsummary.notification.completed", job.getJobNumber()),
|
||||||
Notification.Position.BOTTOM_END)
|
3000, Notification.Position.BOTTOM_END)
|
||||||
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
||||||
// Re-render the page
|
// Re-render the page
|
||||||
getUI().ifPresent(ui -> ui.navigate("job_summary/" + job.getId().toHexString()));
|
getUI().ifPresent(ui -> ui.navigate("job_summary/" + job.getId().toHexString()));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Notification
|
Notification
|
||||||
.show(getTranslation("jobsummary.notification.complete.error", ex.getMessage()), 5000,
|
.show(getTranslation("jobsummary.notification.complete.error",
|
||||||
Notification.Position.BOTTOM_END)
|
ex.getMessage()), 5000, Notification.Position.BOTTOM_END)
|
||||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
ButtonVariant.LUMO_PRIMARY);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -896,8 +895,9 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
// Gespeicherte Dauer formatieren
|
// Gespeicherte Dauer formatieren
|
||||||
int hours = savedDuration / 3600;
|
int hours = savedDuration / 3600;
|
||||||
int minutes = (savedDuration % 3600) / 60;
|
int minutes = (savedDuration % 3600) / 60;
|
||||||
String savedDurationText = hours > 0 ? String.format("%d Std. %d Min.", hours, minutes)
|
String savedDurationText = formatDurationShort(hours, minutes);
|
||||||
: String.format("%d Min.", minutes);
|
String plannedRouteLabel = escapeJs(getTranslation("jobsummary.route.planned"));
|
||||||
|
String durationLabel = escapeJs(getTranslation("createinvoice.route.duration"));
|
||||||
|
|
||||||
// Build waypoints JS array
|
// Build waypoints JS array
|
||||||
StringBuilder waypointsJs = new StringBuilder("[");
|
StringBuilder waypointsJs = new StringBuilder("[");
|
||||||
@@ -925,6 +925,8 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
var hasSavedRouteData = %s;
|
var hasSavedRouteData = %s;
|
||||||
var savedDistance = %s;
|
var savedDistance = %s;
|
||||||
var savedDurationText = '%s';
|
var savedDurationText = '%s';
|
||||||
|
var plannedRouteLabel = '%s';
|
||||||
|
var durationLabel = '%s';
|
||||||
var waypoints = %s;
|
var waypoints = %s;
|
||||||
|
|
||||||
var appUserMarker = null;
|
var appUserMarker = null;
|
||||||
@@ -968,7 +970,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
savedRouteDiv.style.backgroundColor = '#e3f2fd';
|
savedRouteDiv.style.backgroundColor = '#e3f2fd';
|
||||||
savedRouteDiv.style.borderRadius = '4px';
|
savedRouteDiv.style.borderRadius = '4px';
|
||||||
savedRouteDiv.style.fontWeight = 'bold';
|
savedRouteDiv.style.fontWeight = 'bold';
|
||||||
savedRouteDiv.textContent = '📍 Geplante Route: ' + savedDistance.toFixed(1) + ' km • Fahrtzeit: ' + savedDurationText;
|
savedRouteDiv.textContent = '📍 ' + plannedRouteLabel + ': ' + savedDistance.toFixed(1) + ' km • ' + durationLabel + ': ' + savedDurationText;
|
||||||
infoEl.appendChild(savedRouteDiv);
|
infoEl.appendChild(savedRouteDiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1075,7 +1077,16 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
.formatted(escapeJs(origin), escapeJs(destination), escapeJs(apiKey), lat, lng,
|
.formatted(escapeJs(origin), escapeJs(destination), escapeJs(apiKey), lat, lng,
|
||||||
Boolean.toString(hasPosition), escapeJs(appUserId), Boolean.toString(shouldUpdate),
|
Boolean.toString(hasPosition), escapeJs(appUserId), Boolean.toString(shouldUpdate),
|
||||||
Boolean.toString(hasSavedRouteData), savedDistanceStr, escapeJs(savedDurationText),
|
Boolean.toString(hasSavedRouteData), savedDistanceStr, escapeJs(savedDurationText),
|
||||||
waypointsJs.toString());
|
plannedRouteLabel, durationLabel, waypointsJs.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatDurationShort(int hours, int minutes) {
|
||||||
|
String hourUnit = getTranslation("duration.hours.short");
|
||||||
|
String minuteUnit = getTranslation("duration.minutes.short");
|
||||||
|
if (hours > 0) {
|
||||||
|
return String.format("%d %s %d %s", hours, hourUnit, minutes, minuteUnit);
|
||||||
|
}
|
||||||
|
return String.format("%d %s", minutes, minuteUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hilfsfunktion zum einfachen Escapen von JS-Zeichen in Strings
|
// Hilfsfunktion zum einfachen Escapen von JS-Zeichen in Strings
|
||||||
|
|||||||
@@ -693,7 +693,7 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
|
|
||||||
private HorizontalLayout createMessageInputArea() {
|
private HorizontalLayout createMessageInputArea() {
|
||||||
messageInput = new TextArea();
|
messageInput = new TextArea();
|
||||||
messageInput.setPlaceholder("Nachricht eingeben...");
|
messageInput.setPlaceholder(getTranslation("messagedetails.placeholder"));
|
||||||
messageInput.setWidthFull();
|
messageInput.setWidthFull();
|
||||||
messageInput.getStyle().set("min-height", "60px");
|
messageInput.getStyle().set("min-height", "60px");
|
||||||
messageInput.getStyle().set("max-height", "120px");
|
messageInput.getStyle().set("max-height", "120px");
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package de.assecutor.votianlt.pages.view;
|
|||||||
import com.vaadin.flow.component.button.Button;
|
import com.vaadin.flow.component.button.Button;
|
||||||
import com.vaadin.flow.component.button.ButtonVariant;
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
import com.vaadin.flow.component.combobox.ComboBox;
|
import com.vaadin.flow.component.combobox.ComboBox;
|
||||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
import com.vaadin.flow.component.dialog.Dialog;
|
||||||
import com.vaadin.flow.component.datepicker.DatePicker;
|
import com.vaadin.flow.component.datepicker.DatePicker;
|
||||||
import com.vaadin.flow.component.grid.Grid;
|
import com.vaadin.flow.component.grid.Grid;
|
||||||
import com.vaadin.flow.component.html.Anchor;
|
import com.vaadin.flow.component.html.Anchor;
|
||||||
@@ -21,6 +21,7 @@ import com.vaadin.flow.router.Route;
|
|||||||
import de.assecutor.votianlt.model.Job;
|
import de.assecutor.votianlt.model.Job;
|
||||||
import de.assecutor.votianlt.model.JobStatus;
|
import de.assecutor.votianlt.model.JobStatus;
|
||||||
import de.assecutor.votianlt.messaging.MessagingPublisher;
|
import de.assecutor.votianlt.messaging.MessagingPublisher;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.DialogStylingHelper;
|
||||||
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
||||||
import de.assecutor.votianlt.repository.CustomerInvoiceRepository;
|
import de.assecutor.votianlt.repository.CustomerInvoiceRepository;
|
||||||
@@ -214,14 +215,13 @@ public class ShowJobsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showCompleteJobDialog(Job job) {
|
private void showCompleteJobDialog(Job job) {
|
||||||
ConfirmDialog dialog = new ConfirmDialog();
|
Dialog dialog = DialogStylingHelper.createConfirmationDialog(
|
||||||
dialog.setHeader(getTranslation("jobs.dialog.complete.title"));
|
getTranslation("jobs.dialog.complete.title"),
|
||||||
dialog.setText(getTranslation("jobs.dialog.complete.text", job.getJobNumber()));
|
getTranslation("jobs.dialog.complete.text", job.getJobNumber()),
|
||||||
dialog.setCancelable(true);
|
"560px",
|
||||||
dialog.setCancelText(getTranslation("button.cancel"));
|
getTranslation("button.cancel"),
|
||||||
dialog.setConfirmText(getTranslation("jobs.dialog.complete.confirm"));
|
getTranslation("jobs.dialog.complete.confirm"),
|
||||||
dialog.setConfirmButtonTheme("primary");
|
() -> {
|
||||||
dialog.addConfirmListener(e -> {
|
|
||||||
try {
|
try {
|
||||||
JobStatus oldStatus = job.getStatus();
|
JobStatus oldStatus = job.getStatus();
|
||||||
job.setStatus(JobStatus.COMPLETED);
|
job.setStatus(JobStatus.COMPLETED);
|
||||||
@@ -235,19 +235,19 @@ public class ShowJobsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
Notification.show(getTranslation("jobs.notification.complete.error", ex.getMessage()), 5000,
|
Notification.show(getTranslation("jobs.notification.complete.error", ex.getMessage()), 5000,
|
||||||
Notification.Position.BOTTOM_END).addThemeVariants(NotificationVariant.LUMO_ERROR);
|
Notification.Position.BOTTOM_END).addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
ButtonVariant.LUMO_PRIMARY);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showDeleteJobDialog(Job job) {
|
private void showDeleteJobDialog(Job job) {
|
||||||
ConfirmDialog dialog = new ConfirmDialog();
|
Dialog dialog = DialogStylingHelper.createConfirmationDialog(
|
||||||
dialog.setHeader(getTranslation("jobs.dialog.delete.title"));
|
getTranslation("jobs.dialog.delete.title"),
|
||||||
dialog.setText(getTranslation("jobs.dialog.delete.text", job.getJobNumber()));
|
getTranslation("jobs.dialog.delete.text", job.getJobNumber()),
|
||||||
dialog.setCancelable(true);
|
"560px",
|
||||||
dialog.setCancelText(getTranslation("button.cancel"));
|
getTranslation("button.cancel"),
|
||||||
dialog.setConfirmText(getTranslation("button.delete"));
|
getTranslation("button.delete"),
|
||||||
dialog.setConfirmButtonTheme("error primary");
|
() -> {
|
||||||
dialog.addConfirmListener(e -> {
|
|
||||||
try {
|
try {
|
||||||
// Notify client before deleting if online
|
// Notify client before deleting if online
|
||||||
notifyClientJobDeleted(job);
|
notifyClientJobDeleted(job);
|
||||||
@@ -260,7 +260,9 @@ public class ShowJobsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
Notification.show(getTranslation("jobs.notification.delete.error", ex.getMessage()), 5000,
|
Notification.show(getTranslation("jobs.notification.delete.error", ex.getMessage()), 5000,
|
||||||
Notification.Position.BOTTOM_END).addThemeVariants(NotificationVariant.LUMO_ERROR);
|
Notification.Position.BOTTOM_END).addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
ButtonVariant.LUMO_PRIMARY,
|
||||||
|
ButtonVariant.LUMO_ERROR);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.vaadin.flow.router.BeforeEnterEvent;
|
|||||||
import com.vaadin.flow.router.BeforeEnterObserver;
|
import com.vaadin.flow.router.BeforeEnterObserver;
|
||||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||||
import de.assecutor.votianlt.model.Language;
|
import de.assecutor.votianlt.model.Language;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.DialogStylingHelper;
|
||||||
import de.assecutor.votianlt.security.SessionAuthenticationService;
|
import de.assecutor.votianlt.security.SessionAuthenticationService;
|
||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
import de.assecutor.votianlt.service.DemoModeService;
|
import de.assecutor.votianlt.service.DemoModeService;
|
||||||
@@ -241,17 +242,15 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
String currentUser = securityService.getCurrentUsername();
|
String currentUser = securityService.getCurrentUsername();
|
||||||
ComboBox<String> userCombo = new ComboBox<>();
|
ComboBox<String> userCombo = new ComboBox<>();
|
||||||
userCombo.setPlaceholder(currentUser);
|
userCombo.setPlaceholder(currentUser);
|
||||||
userCombo.setItems("Profil anzeigen", "Einstellungen", "Abmelden");
|
userCombo.setItems("Profil anzeigen", "Abmelden");
|
||||||
userCombo.addValueChangeListener(event -> {
|
userCombo.addValueChangeListener(event -> {
|
||||||
String value = event.getValue();
|
String value = event.getValue();
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case "Profil anzeigen":
|
case "Profil anzeigen":
|
||||||
break;
|
break;
|
||||||
case "Einstellungen":
|
|
||||||
break;
|
|
||||||
case "Abmelden":
|
case "Abmelden":
|
||||||
securityService.logout();
|
openLogoutConfirmDialog();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
userCombo.clear(); // Reset selection
|
userCombo.clear(); // Reset selection
|
||||||
@@ -450,6 +449,19 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
return footer;
|
return footer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openLogoutConfirmDialog() {
|
||||||
|
var dialog = DialogStylingHelper.createConfirmationDialog(
|
||||||
|
getTranslation("logout.confirm.title"),
|
||||||
|
getTranslation("logout.confirm.message"),
|
||||||
|
"460px",
|
||||||
|
getTranslation("button.cancel"),
|
||||||
|
getTranslation("nav.logout"),
|
||||||
|
securityService::logout,
|
||||||
|
ButtonVariant.LUMO_PRIMARY,
|
||||||
|
ButtonVariant.LUMO_ERROR);
|
||||||
|
dialog.open();
|
||||||
|
}
|
||||||
|
|
||||||
private void register() {
|
private void register() {
|
||||||
UI.getCurrent().navigate("register");
|
UI.getCurrent().navigate("register");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.springframework.data.mongodb.repository.MongoRepository;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface MessageRepository extends MongoRepository<Message, ObjectId> {
|
public interface MessageRepository extends MongoRepository<Message, ObjectId> {
|
||||||
@@ -66,6 +67,9 @@ public interface MessageRepository extends MongoRepository<Message, ObjectId> {
|
|||||||
List<Message> findByReceiverAndDeliveryStatusOrderByCreatedAtAsc(String receiver,
|
List<Message> findByReceiverAndDeliveryStatusOrderByCreatedAtAsc(String receiver,
|
||||||
MessageDeliveryStatus deliveryStatus);
|
MessageDeliveryStatus deliveryStatus);
|
||||||
|
|
||||||
|
Optional<Message> findFirstByReceiverAndOriginAndClientMessageId(String receiver, MessageOrigin origin,
|
||||||
|
String clientMessageId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all undelivered messages (NOTSEND status) for a receiver
|
* Find all undelivered messages (NOTSEND status) for a receiver
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -100,6 +101,16 @@ public class MessageService {
|
|||||||
* Inbound message payload where receiver = AppUser ID (clientId)
|
* Inbound message payload where receiver = AppUser ID (clientId)
|
||||||
*/
|
*/
|
||||||
public Message receiveMessageFromClient(ChatMessageInboundPayload payload) {
|
public Message receiveMessageFromClient(ChatMessageInboundPayload payload) {
|
||||||
|
String clientMessageId = payload.messageId();
|
||||||
|
if (clientMessageId != null && !clientMessageId.isBlank()) {
|
||||||
|
Optional<Message> existingMessage = messageRepository.findFirstByReceiverAndOriginAndClientMessageId(
|
||||||
|
payload.receiver(), MessageOrigin.CLIENT, clientMessageId);
|
||||||
|
if (existingMessage.isPresent()) {
|
||||||
|
sendClientMessageAck(payload.receiver(), clientMessageId, existingMessage.get());
|
||||||
|
return existingMessage.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Message message;
|
Message message;
|
||||||
MessageContentType contentType = payload.contentType();
|
MessageContentType contentType = payload.contentType();
|
||||||
if (payload.hasJobContext()) {
|
if (payload.hasJobContext()) {
|
||||||
@@ -109,11 +120,36 @@ public class MessageService {
|
|||||||
} else {
|
} else {
|
||||||
message = new Message(payload.content(), payload.receiver(), MessageOrigin.CLIENT, contentType);
|
message = new Message(payload.content(), payload.receiver(), MessageOrigin.CLIENT, contentType);
|
||||||
}
|
}
|
||||||
|
message.setClientMessageId(clientMessageId);
|
||||||
message = saveMessage(message);
|
message = saveMessage(message);
|
||||||
|
sendClientMessageAck(payload.receiver(), clientMessageId, message);
|
||||||
eventPublisher.publishEvent(new MessageReceivedEvent(this, message));
|
eventPublisher.publishEvent(new MessageReceivedEvent(this, message));
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendClientMessageAck(String receiver, String clientMessageId, Message message) {
|
||||||
|
if (receiver == null || receiver.isBlank() || clientMessageId == null || clientMessageId.isBlank()
|
||||||
|
|| message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
LinkedHashMap<String, Object> payload = new LinkedHashMap<>();
|
||||||
|
payload.put("clientMessageId", clientMessageId);
|
||||||
|
payload.put("messageId", message.getIdAsString());
|
||||||
|
payload.put("createdAt", message.getCreatedAt());
|
||||||
|
|
||||||
|
byte[] data = objectMapper.writeValueAsString(payload).getBytes(StandardCharsets.UTF_8);
|
||||||
|
webSocketService.sendToClient(receiver, "message_ack", data).exceptionally(ex -> {
|
||||||
|
log.debug("[Messaging] Failed to send ACK for client message {} to {}: {}", clientMessageId, receiver,
|
||||||
|
ex.getMessage());
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[Messaging] Error sending ACK for client message {}: {}", clientMessageId, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publish message to topic for the receiver. Only sends if client is connected,
|
* Publish message to topic for the receiver. Only sends if client is connected,
|
||||||
* otherwise keeps NOTSEND status.
|
* otherwise keeps NOTSEND status.
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ nav.users=Benutzer
|
|||||||
nav.showprofile=Profil anzeigen
|
nav.showprofile=Profil anzeigen
|
||||||
nav.settings=Einstellungen
|
nav.settings=Einstellungen
|
||||||
nav.logout=Abmelden
|
nav.logout=Abmelden
|
||||||
|
logout.confirm.title=Abmelden bestätigen
|
||||||
|
logout.confirm.message=Möchten Sie sich wirklich abmelden?
|
||||||
|
|
||||||
# Profile View
|
# Profile View
|
||||||
profile.title=Profil bearbeiten
|
profile.title=Profil bearbeiten
|
||||||
@@ -200,6 +202,10 @@ common.error=Fehler
|
|||||||
common.success=Erfolg
|
common.success=Erfolg
|
||||||
common.required=Pflichtfeld
|
common.required=Pflichtfeld
|
||||||
|
|
||||||
|
# Duration
|
||||||
|
duration.hours.short=Std.
|
||||||
|
duration.minutes.short=Min.
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validation.required=Feld ist erforderlich
|
validation.required=Feld ist erforderlich
|
||||||
validation.email=Ungültige E-Mail-Adresse
|
validation.email=Ungültige E-Mail-Adresse
|
||||||
@@ -603,6 +609,7 @@ jobsummary.task.photo.maxonly=Maximal {0} Fotos erlaubt
|
|||||||
jobsummary.task.photo.taken=Aufgenommene Fotos ({0})
|
jobsummary.task.photo.taken=Aufgenommene Fotos ({0})
|
||||||
jobsummary.task.button.text=Button-Text
|
jobsummary.task.button.text=Button-Text
|
||||||
jobsummary.button.schliessen=Schließen
|
jobsummary.button.schliessen=Schließen
|
||||||
|
jobsummary.route.planned=Geplante Route
|
||||||
|
|
||||||
# Jobs
|
# Jobs
|
||||||
jobs.title=Aufträge
|
jobs.title=Aufträge
|
||||||
@@ -730,6 +737,8 @@ statistics.loading=Berechne...
|
|||||||
# Job Status
|
# Job Status
|
||||||
jobstatus.IN_PROGRESS=In Bearbeitung
|
jobstatus.IN_PROGRESS=In Bearbeitung
|
||||||
jobstatus.COMPLETED=Abgeschlossen
|
jobstatus.COMPLETED=Abgeschlossen
|
||||||
|
jobstatus.CREATED=Erstellt
|
||||||
|
jobstatus.CANCELLED=Storniert
|
||||||
|
|
||||||
# Task Types
|
# Task Types
|
||||||
tasktype.CONFIRMATION=Bestätigung
|
tasktype.CONFIRMATION=Bestätigung
|
||||||
@@ -832,6 +841,15 @@ start.imprint.company=Assecutor Data Service GmbH
|
|||||||
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
||||||
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
||||||
start.imprint.email=E-Mail: ahoi@assecutor.de
|
start.imprint.email=E-Mail: ahoi@assecutor.de
|
||||||
|
imprint.phone=Telefon
|
||||||
|
imprint.email=E-Mail
|
||||||
|
imprint.country=Deutschland
|
||||||
|
imprint.management=Geschäftsführung
|
||||||
|
imprint.registeredoffice=Firmensitz
|
||||||
|
imprint.commercialregister=Handelsregister
|
||||||
|
imprint.vatid=Umsatzsteuer-ID
|
||||||
|
imprint.imagecredits=Quellenangaben für die verwendeten Bilder und Grafiken
|
||||||
|
imprint.backgroundimage=Hintergrundbild Startseite
|
||||||
start.cta.text=Registrieren Sie sich noch heute und nutzen den kostenfreien Probemonat, um das System auf Herz und Nieren zu testen.
|
start.cta.text=Registrieren Sie sich noch heute und nutzen den kostenfreien Probemonat, um das System auf Herz und Nieren zu testen.
|
||||||
start.slogan=Betreiben Sie Ihr Geschäft smart … mit votianLT!
|
start.slogan=Betreiben Sie Ihr Geschäft smart … mit votianLT!
|
||||||
start.version=Version
|
start.version=Version
|
||||||
@@ -914,6 +932,8 @@ jobhistory.info.createdat=Erstellt am: {0}
|
|||||||
jobhistory.info.status=Status: {0}
|
jobhistory.info.status=Status: {0}
|
||||||
jobhistory.count={0} Einträge in der Historie
|
jobhistory.count={0} Einträge in der Historie
|
||||||
jobhistory.changedby=Geändert von: {0}
|
jobhistory.changedby=Geändert von: {0}
|
||||||
|
jobhistory.entry.create.reason=Job erstellt
|
||||||
|
jobhistory.entry.create.description=Neuer Job wurde erstellt: {0}
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
version.label=Version
|
version.label=Version
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ nav.users=Kasutajad
|
|||||||
nav.showprofile=Kuva profiil
|
nav.showprofile=Kuva profiil
|
||||||
nav.settings=Seaded
|
nav.settings=Seaded
|
||||||
nav.logout=Logi v\u00e4lja
|
nav.logout=Logi v\u00e4lja
|
||||||
|
logout.confirm.title=Kinnita v\u00e4ljalogimine
|
||||||
|
logout.confirm.message=Kas soovite t\u00f5esti v\u00e4lja logida?
|
||||||
profile.title=Profiili muutmine
|
profile.title=Profiili muutmine
|
||||||
profile.language=Keel
|
profile.language=Keel
|
||||||
profile.company=Ettev\u00f5te
|
profile.company=Ettev\u00f5te
|
||||||
@@ -181,6 +183,11 @@ common.loading=Laadimine...
|
|||||||
common.error=Viga
|
common.error=Viga
|
||||||
common.success=Edukas
|
common.success=Edukas
|
||||||
common.required=Kohustuslik v\u00e4li
|
common.required=Kohustuslik v\u00e4li
|
||||||
|
|
||||||
|
# Duration
|
||||||
|
duration.hours.short=t
|
||||||
|
duration.minutes.short=min
|
||||||
|
|
||||||
validation.required=V\u00e4li on kohustuslik
|
validation.required=V\u00e4li on kohustuslik
|
||||||
validation.email=Vigane e-posti aadress
|
validation.email=Vigane e-posti aadress
|
||||||
validation.error=Valideerimise viga
|
validation.error=Valideerimise viga
|
||||||
@@ -549,6 +556,7 @@ jobsummary.task.photo.maxonly=Maksimaalselt {0} fotot lubatud
|
|||||||
jobsummary.task.photo.taken=Tehtud fotod ({0})
|
jobsummary.task.photo.taken=Tehtud fotod ({0})
|
||||||
jobsummary.task.button.text=Nupu tekst
|
jobsummary.task.button.text=Nupu tekst
|
||||||
jobsummary.button.schliessen=Sulge
|
jobsummary.button.schliessen=Sulge
|
||||||
|
jobsummary.route.planned=Planeeritud marsruut
|
||||||
jobs.title=Tellimused
|
jobs.title=Tellimused
|
||||||
jobs.filter.search=Otsi
|
jobs.filter.search=Otsi
|
||||||
jobs.filter.search.placeholder=Otsi tellimuse numbri j\u00e4rgi...
|
jobs.filter.search.placeholder=Otsi tellimuse numbri j\u00e4rgi...
|
||||||
@@ -662,6 +670,8 @@ statistics.data.fetched=Andmed on k\u00e4tte saadud
|
|||||||
statistics.loading=Arvutamine...
|
statistics.loading=Arvutamine...
|
||||||
jobstatus.IN_PROGRESS=T\u00f6\u00f6s
|
jobstatus.IN_PROGRESS=T\u00f6\u00f6s
|
||||||
jobstatus.COMPLETED=L\u00f5petatud
|
jobstatus.COMPLETED=L\u00f5petatud
|
||||||
|
jobstatus.CREATED=Loodud
|
||||||
|
jobstatus.CANCELLED=T\u00fchistatud
|
||||||
tasktype.CONFIRMATION=Kinnitus
|
tasktype.CONFIRMATION=Kinnitus
|
||||||
tasktype.SIGNATURE=Allkiri
|
tasktype.SIGNATURE=Allkiri
|
||||||
tasktype.TODOLIST=\u00dclesannete nimekiri
|
tasktype.TODOLIST=\u00dclesannete nimekiri
|
||||||
@@ -754,6 +764,15 @@ start.imprint.company=Assecutor Data Service GmbH
|
|||||||
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
||||||
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
||||||
start.imprint.email=E-post: ahoi@assecutor.de
|
start.imprint.email=E-post: ahoi@assecutor.de
|
||||||
|
imprint.phone=Telefon
|
||||||
|
imprint.email=E-post
|
||||||
|
imprint.country=Saksamaa
|
||||||
|
imprint.management=Juhtkond
|
||||||
|
imprint.registeredoffice=Registrijärgne asukoht
|
||||||
|
imprint.commercialregister=Äriregister
|
||||||
|
imprint.vatid=KMKR number
|
||||||
|
imprint.imagecredits=Kasutatud piltide ja graafika allikad
|
||||||
|
imprint.backgroundimage=Avalehe taustapilt
|
||||||
start.cta.text=Registreeruge juba t\u00e4na ja kasutage tasuta proovikuud, et s\u00fcsteemi p\u00f5hjalikult testida.
|
start.cta.text=Registreeruge juba t\u00e4na ja kasutage tasuta proovikuud, et s\u00fcsteemi p\u00f5hjalikult testida.
|
||||||
start.slogan=Ajage oma \u00e4ri targalt \u2026 votianLT-ga!
|
start.slogan=Ajage oma \u00e4ri targalt \u2026 votianLT-ga!
|
||||||
start.version=Versioon
|
start.version=Versioon
|
||||||
@@ -824,6 +843,8 @@ jobhistory.info.createdat=Loodud: {0}
|
|||||||
jobhistory.info.status=Olek: {0}
|
jobhistory.info.status=Olek: {0}
|
||||||
jobhistory.count={0} kirjet ajaloos
|
jobhistory.count={0} kirjet ajaloos
|
||||||
jobhistory.changedby=Muutnud: {0}
|
jobhistory.changedby=Muutnud: {0}
|
||||||
|
jobhistory.entry.create.reason=T\u00f6\u00f6 loodud
|
||||||
|
jobhistory.entry.create.description=Uus t\u00f6\u00f6 loodi: {0}
|
||||||
version.label=Versioon
|
version.label=Versioon
|
||||||
management.placeholder=Haldus
|
management.placeholder=Haldus
|
||||||
management.customers=Kliendid
|
management.customers=Kliendid
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ nav.users=Users
|
|||||||
nav.showprofile=Show Profile
|
nav.showprofile=Show Profile
|
||||||
nav.settings=Settings
|
nav.settings=Settings
|
||||||
nav.logout=Log Out
|
nav.logout=Log Out
|
||||||
|
logout.confirm.title=Confirm logout
|
||||||
|
logout.confirm.message=Do you really want to log out?
|
||||||
|
|
||||||
# Profile View
|
# Profile View
|
||||||
profile.title=Edit Profile
|
profile.title=Edit Profile
|
||||||
@@ -200,6 +202,10 @@ common.error=Error
|
|||||||
common.success=Success
|
common.success=Success
|
||||||
common.required=Required
|
common.required=Required
|
||||||
|
|
||||||
|
# Duration
|
||||||
|
duration.hours.short=hr
|
||||||
|
duration.minutes.short=min
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validation.required=Field is required
|
validation.required=Field is required
|
||||||
validation.email=Invalid email address
|
validation.email=Invalid email address
|
||||||
@@ -603,6 +609,7 @@ jobsummary.task.photo.maxonly=Maximum {0} photos allowed
|
|||||||
jobsummary.task.photo.taken=Photos taken ({0})
|
jobsummary.task.photo.taken=Photos taken ({0})
|
||||||
jobsummary.task.button.text=Button Text
|
jobsummary.task.button.text=Button Text
|
||||||
jobsummary.button.schliessen=Close
|
jobsummary.button.schliessen=Close
|
||||||
|
jobsummary.route.planned=Planned Route
|
||||||
|
|
||||||
# Jobs
|
# Jobs
|
||||||
jobs.title=Jobs
|
jobs.title=Jobs
|
||||||
@@ -730,6 +737,8 @@ statistics.loading=Calculating...
|
|||||||
# Job Status
|
# Job Status
|
||||||
jobstatus.IN_PROGRESS=In Progress
|
jobstatus.IN_PROGRESS=In Progress
|
||||||
jobstatus.COMPLETED=Completed
|
jobstatus.COMPLETED=Completed
|
||||||
|
jobstatus.CREATED=Created
|
||||||
|
jobstatus.CANCELLED=Cancelled
|
||||||
|
|
||||||
# Task Types
|
# Task Types
|
||||||
tasktype.CONFIRMATION=Confirmation
|
tasktype.CONFIRMATION=Confirmation
|
||||||
@@ -832,6 +841,15 @@ start.imprint.company=Assecutor Data Service GmbH
|
|||||||
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
||||||
start.imprint.phone=Phone: +49 40 18 123 771 0
|
start.imprint.phone=Phone: +49 40 18 123 771 0
|
||||||
start.imprint.email=Email: ahoi@assecutor.de
|
start.imprint.email=Email: ahoi@assecutor.de
|
||||||
|
imprint.phone=Phone
|
||||||
|
imprint.email=Email
|
||||||
|
imprint.country=Germany
|
||||||
|
imprint.management=Management
|
||||||
|
imprint.registeredoffice=Registered Office
|
||||||
|
imprint.commercialregister=Commercial Register
|
||||||
|
imprint.vatid=VAT ID
|
||||||
|
imprint.imagecredits=Image Credits for Pictures and Graphics Used
|
||||||
|
imprint.backgroundimage=Homepage Background Image
|
||||||
start.cta.text=Register today and use the free trial month to put the system through its paces.
|
start.cta.text=Register today and use the free trial month to put the system through its paces.
|
||||||
start.slogan=Run your business smart ... with votianLT!
|
start.slogan=Run your business smart ... with votianLT!
|
||||||
start.version=Version
|
start.version=Version
|
||||||
@@ -914,6 +932,8 @@ jobhistory.info.createdat=Created on: {0}
|
|||||||
jobhistory.info.status=Status: {0}
|
jobhistory.info.status=Status: {0}
|
||||||
jobhistory.count={0} entries in history
|
jobhistory.count={0} entries in history
|
||||||
jobhistory.changedby=Changed by: {0}
|
jobhistory.changedby=Changed by: {0}
|
||||||
|
jobhistory.entry.create.reason=Job Created
|
||||||
|
jobhistory.entry.create.description=New job was created: {0}
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
version.label=Version
|
version.label=Version
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ nav.users=Usuarios
|
|||||||
nav.showprofile=Ver perfil
|
nav.showprofile=Ver perfil
|
||||||
nav.settings=Configuraci\u00f3n
|
nav.settings=Configuraci\u00f3n
|
||||||
nav.logout=Cerrar sesi\u00f3n
|
nav.logout=Cerrar sesi\u00f3n
|
||||||
|
logout.confirm.title=Confirmar cierre de sesi\u00f3n
|
||||||
|
logout.confirm.message=\u00bfRealmente desea cerrar sesi\u00f3n?
|
||||||
|
|
||||||
# Profile View
|
# Profile View
|
||||||
profile.title=Editar perfil
|
profile.title=Editar perfil
|
||||||
@@ -200,6 +202,10 @@ common.error=Error
|
|||||||
common.success=\u00c9xito
|
common.success=\u00c9xito
|
||||||
common.required=Campo obligatorio
|
common.required=Campo obligatorio
|
||||||
|
|
||||||
|
# Duration
|
||||||
|
duration.hours.short=h
|
||||||
|
duration.minutes.short=min
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validation.required=El campo es obligatorio
|
validation.required=El campo es obligatorio
|
||||||
validation.email=Direcci\u00f3n de correo electr\u00f3nico no v\u00e1lida
|
validation.email=Direcci\u00f3n de correo electr\u00f3nico no v\u00e1lida
|
||||||
@@ -603,6 +609,7 @@ jobsummary.task.photo.maxonly=Se permiten como m\u00e1ximo {0} fotos
|
|||||||
jobsummary.task.photo.taken=Fotos tomadas ({0})
|
jobsummary.task.photo.taken=Fotos tomadas ({0})
|
||||||
jobsummary.task.button.text=Texto del bot\u00f3n
|
jobsummary.task.button.text=Texto del bot\u00f3n
|
||||||
jobsummary.button.schliessen=Cerrar
|
jobsummary.button.schliessen=Cerrar
|
||||||
|
jobsummary.route.planned=Ruta planificada
|
||||||
|
|
||||||
# Jobs
|
# Jobs
|
||||||
jobs.title=Pedidos
|
jobs.title=Pedidos
|
||||||
@@ -730,6 +737,8 @@ statistics.loading=Calculando...
|
|||||||
# Job Status
|
# Job Status
|
||||||
jobstatus.IN_PROGRESS=En proceso
|
jobstatus.IN_PROGRESS=En proceso
|
||||||
jobstatus.COMPLETED=Completado
|
jobstatus.COMPLETED=Completado
|
||||||
|
jobstatus.CREATED=Creado
|
||||||
|
jobstatus.CANCELLED=Cancelado
|
||||||
|
|
||||||
# Task Types
|
# Task Types
|
||||||
tasktype.CONFIRMATION=Confirmaci\u00f3n
|
tasktype.CONFIRMATION=Confirmaci\u00f3n
|
||||||
@@ -832,6 +841,15 @@ start.imprint.company=Assecutor Data Service GmbH
|
|||||||
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
||||||
start.imprint.phone=Tel\u00e9fono: +49 40 18 123 771 0
|
start.imprint.phone=Tel\u00e9fono: +49 40 18 123 771 0
|
||||||
start.imprint.email=Correo electr\u00f3nico: ahoi@assecutor.de
|
start.imprint.email=Correo electr\u00f3nico: ahoi@assecutor.de
|
||||||
|
imprint.phone=Tel\u00e9fono
|
||||||
|
imprint.email=Correo electr\u00f3nico
|
||||||
|
imprint.country=Alemania
|
||||||
|
imprint.management=Direcci\u00f3n
|
||||||
|
imprint.registeredoffice=Domicilio social
|
||||||
|
imprint.commercialregister=Registro mercantil
|
||||||
|
imprint.vatid=ID de IVA
|
||||||
|
imprint.imagecredits=Fuentes de las im\u00e1genes y gr\u00e1ficos utilizados
|
||||||
|
imprint.backgroundimage=Imagen de fondo de la p\u00e1gina de inicio
|
||||||
start.cta.text=Reg\u00edstrese hoy mismo y utilice el mes de prueba gratuito para probar el sistema a fondo.
|
start.cta.text=Reg\u00edstrese hoy mismo y utilice el mes de prueba gratuito para probar el sistema a fondo.
|
||||||
start.slogan=\u00a1Gestione su negocio de forma inteligente... con votianLT!
|
start.slogan=\u00a1Gestione su negocio de forma inteligente... con votianLT!
|
||||||
start.version=Versi\u00f3n
|
start.version=Versi\u00f3n
|
||||||
@@ -914,6 +932,8 @@ jobhistory.info.createdat=Creado el: {0}
|
|||||||
jobhistory.info.status=Estado: {0}
|
jobhistory.info.status=Estado: {0}
|
||||||
jobhistory.count={0} entradas en el historial
|
jobhistory.count={0} entradas en el historial
|
||||||
jobhistory.changedby=Modificado por: {0}
|
jobhistory.changedby=Modificado por: {0}
|
||||||
|
jobhistory.entry.create.reason=Pedido creado
|
||||||
|
jobhistory.entry.create.description=Se ha creado un nuevo pedido: {0}
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
version.label=Versi\u00f3n
|
version.label=Versi\u00f3n
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ nav.users=Utilisateurs
|
|||||||
nav.showprofile=Afficher le profil
|
nav.showprofile=Afficher le profil
|
||||||
nav.settings=Param\u00e8tres
|
nav.settings=Param\u00e8tres
|
||||||
nav.logout=D\u00e9connexion
|
nav.logout=D\u00e9connexion
|
||||||
|
logout.confirm.title=Confirmer la d\u00e9connexion
|
||||||
|
logout.confirm.message=Voulez-vous vraiment vous d\u00e9connecter ?
|
||||||
|
|
||||||
# Profile View
|
# Profile View
|
||||||
profile.title=Modifier le profil
|
profile.title=Modifier le profil
|
||||||
@@ -200,6 +202,10 @@ common.error=Erreur
|
|||||||
common.success=Succ\u00e8s
|
common.success=Succ\u00e8s
|
||||||
common.required=Champ obligatoire
|
common.required=Champ obligatoire
|
||||||
|
|
||||||
|
# Duration
|
||||||
|
duration.hours.short=h
|
||||||
|
duration.minutes.short=min
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validation.required=Le champ est obligatoire
|
validation.required=Le champ est obligatoire
|
||||||
validation.email=Adresse e-mail invalide
|
validation.email=Adresse e-mail invalide
|
||||||
@@ -603,6 +609,7 @@ jobsummary.task.photo.maxonly=Maximum {0} photos autoris\u00e9es
|
|||||||
jobsummary.task.photo.taken=Photos prises ({0})
|
jobsummary.task.photo.taken=Photos prises ({0})
|
||||||
jobsummary.task.button.text=Texte du bouton
|
jobsummary.task.button.text=Texte du bouton
|
||||||
jobsummary.button.schliessen=Fermer
|
jobsummary.button.schliessen=Fermer
|
||||||
|
jobsummary.route.planned=Itin\u00e9raire pr\u00e9vu
|
||||||
|
|
||||||
# Jobs
|
# Jobs
|
||||||
jobs.title=Missions
|
jobs.title=Missions
|
||||||
@@ -730,6 +737,8 @@ statistics.loading=Calcul en cours...
|
|||||||
# Job Status
|
# Job Status
|
||||||
jobstatus.IN_PROGRESS=En cours
|
jobstatus.IN_PROGRESS=En cours
|
||||||
jobstatus.COMPLETED=Termin\u00e9e
|
jobstatus.COMPLETED=Termin\u00e9e
|
||||||
|
jobstatus.CREATED=Cr\u00e9\u00e9e
|
||||||
|
jobstatus.CANCELLED=Annul\u00e9e
|
||||||
|
|
||||||
# Task Types
|
# Task Types
|
||||||
tasktype.CONFIRMATION=Confirmation
|
tasktype.CONFIRMATION=Confirmation
|
||||||
@@ -832,6 +841,15 @@ start.imprint.company=Assecutor Data Service GmbH
|
|||||||
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
||||||
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
||||||
start.imprint.email=E-Mail: ahoi@assecutor.de
|
start.imprint.email=E-Mail: ahoi@assecutor.de
|
||||||
|
imprint.phone=T\u00e9l\u00e9phone
|
||||||
|
imprint.email=E-mail
|
||||||
|
imprint.country=Allemagne
|
||||||
|
imprint.management=Direction
|
||||||
|
imprint.registeredoffice=Si\u00e8ge social
|
||||||
|
imprint.commercialregister=Registre du commerce
|
||||||
|
imprint.vatid=ID TVA
|
||||||
|
imprint.imagecredits=Cr\u00e9dits des images et graphiques utilis\u00e9s
|
||||||
|
imprint.backgroundimage=Image d'arri\u00e8re-plan de la page d'accueil
|
||||||
start.cta.text=Inscrivez-vous d\u00e8s aujourd'hui et profitez du mois d'essai gratuit pour tester le syst\u00e8me en profondeur.
|
start.cta.text=Inscrivez-vous d\u00e8s aujourd'hui et profitez du mois d'essai gratuit pour tester le syst\u00e8me en profondeur.
|
||||||
start.slogan=G\u00e9rez votre activit\u00e9 intelligemment ... avec votianLT !
|
start.slogan=G\u00e9rez votre activit\u00e9 intelligemment ... avec votianLT !
|
||||||
start.version=Version
|
start.version=Version
|
||||||
@@ -914,6 +932,8 @@ jobhistory.info.createdat=Cr\u00e9\u00e9 le : {0}
|
|||||||
jobhistory.info.status=Statut : {0}
|
jobhistory.info.status=Statut : {0}
|
||||||
jobhistory.count={0} entr\u00e9es dans l'historique
|
jobhistory.count={0} entr\u00e9es dans l'historique
|
||||||
jobhistory.changedby=Modifi\u00e9 par : {0}
|
jobhistory.changedby=Modifi\u00e9 par : {0}
|
||||||
|
jobhistory.entry.create.reason=Mission cr\u00e9\u00e9e
|
||||||
|
jobhistory.entry.create.description=Nouvelle mission cr\u00e9\u00e9e : {0}
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
version.label=Version
|
version.label=Version
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ nav.users=Naudotojai
|
|||||||
nav.showprofile=Rodyti profilį
|
nav.showprofile=Rodyti profilį
|
||||||
nav.settings=Nustatymai
|
nav.settings=Nustatymai
|
||||||
nav.logout=Atsijungti
|
nav.logout=Atsijungti
|
||||||
|
logout.confirm.title=Patvirtinti atsijungimą
|
||||||
|
logout.confirm.message=Ar tikrai norite atsijungti?
|
||||||
|
|
||||||
# Profile View
|
# Profile View
|
||||||
profile.title=Redaguoti profilį
|
profile.title=Redaguoti profilį
|
||||||
@@ -200,6 +202,10 @@ common.error=Klaida
|
|||||||
common.success=Sėkmė
|
common.success=Sėkmė
|
||||||
common.required=Privalomas laukas
|
common.required=Privalomas laukas
|
||||||
|
|
||||||
|
# Duration
|
||||||
|
duration.hours.short=val.
|
||||||
|
duration.minutes.short=min.
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validation.required=Laukas yra privalomas
|
validation.required=Laukas yra privalomas
|
||||||
validation.email=Neteisingas el. pašto adresas
|
validation.email=Neteisingas el. pašto adresas
|
||||||
@@ -603,6 +609,7 @@ jobsummary.task.photo.maxonly=Daugiausiai {0} nuotraukų
|
|||||||
jobsummary.task.photo.taken=Padarytos nuotraukos ({0})
|
jobsummary.task.photo.taken=Padarytos nuotraukos ({0})
|
||||||
jobsummary.task.button.text=Mygtuko tekstas
|
jobsummary.task.button.text=Mygtuko tekstas
|
||||||
jobsummary.button.schliessen=Uždaryti
|
jobsummary.button.schliessen=Uždaryti
|
||||||
|
jobsummary.route.planned=Planuotas maršrutas
|
||||||
|
|
||||||
# Jobs
|
# Jobs
|
||||||
jobs.title=Užsakymai
|
jobs.title=Užsakymai
|
||||||
@@ -730,6 +737,8 @@ statistics.loading=Skaičiuojama...
|
|||||||
# Job Status
|
# Job Status
|
||||||
jobstatus.IN_PROGRESS=Vykdomas
|
jobstatus.IN_PROGRESS=Vykdomas
|
||||||
jobstatus.COMPLETED=Užbaigtas
|
jobstatus.COMPLETED=Užbaigtas
|
||||||
|
jobstatus.CREATED=Sukurtas
|
||||||
|
jobstatus.CANCELLED=Atšauktas
|
||||||
|
|
||||||
# Task Types
|
# Task Types
|
||||||
tasktype.CONFIRMATION=Patvirtinimas
|
tasktype.CONFIRMATION=Patvirtinimas
|
||||||
@@ -832,6 +841,15 @@ start.imprint.company=Assecutor Data Service GmbH
|
|||||||
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
||||||
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
||||||
start.imprint.email=E-Mail: ahoi@assecutor.de
|
start.imprint.email=E-Mail: ahoi@assecutor.de
|
||||||
|
imprint.phone=Telefonas
|
||||||
|
imprint.email=El. paštas
|
||||||
|
imprint.country=Vokietija
|
||||||
|
imprint.management=Vadovybė
|
||||||
|
imprint.registeredoffice=Buveinė
|
||||||
|
imprint.commercialregister=Komercinis registras
|
||||||
|
imprint.vatid=PVM kodas
|
||||||
|
imprint.imagecredits=Naudotų vaizdų ir grafikos šaltiniai
|
||||||
|
imprint.backgroundimage=Pradžios puslapio fono paveikslėlis
|
||||||
start.cta.text=Užsiregistruokite šiandien ir pasinaudokite nemokamu bandomuoju mėnesiu, kad galėtumėte išbandyti sistemą.
|
start.cta.text=Užsiregistruokite šiandien ir pasinaudokite nemokamu bandomuoju mėnesiu, kad galėtumėte išbandyti sistemą.
|
||||||
start.slogan=Valdykite savo verslą protingai … su votianLT!
|
start.slogan=Valdykite savo verslą protingai … su votianLT!
|
||||||
start.version=Versija
|
start.version=Versija
|
||||||
@@ -914,6 +932,8 @@ jobhistory.info.createdat=Sukurta: {0}
|
|||||||
jobhistory.info.status=Būsena: {0}
|
jobhistory.info.status=Būsena: {0}
|
||||||
jobhistory.count={0} įrašų istorijoje
|
jobhistory.count={0} įrašų istorijoje
|
||||||
jobhistory.changedby=Pakeitė: {0}
|
jobhistory.changedby=Pakeitė: {0}
|
||||||
|
jobhistory.entry.create.reason=Užsakymas sukurtas
|
||||||
|
jobhistory.entry.create.description=Sukurtas naujas užsakymas: {0}
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
version.label=Versija
|
version.label=Versija
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ nav.users=Lietotāji
|
|||||||
nav.showprofile=Rādīt profilu
|
nav.showprofile=Rādīt profilu
|
||||||
nav.settings=Iestatījumi
|
nav.settings=Iestatījumi
|
||||||
nav.logout=Izrakstīties
|
nav.logout=Izrakstīties
|
||||||
|
logout.confirm.title=Apstiprināt izrakstīšanos
|
||||||
|
logout.confirm.message=Vai tiešām vēlaties izrakstīties?
|
||||||
|
|
||||||
# Profile View
|
# Profile View
|
||||||
profile.title=Rediģēt profilu
|
profile.title=Rediģēt profilu
|
||||||
@@ -200,6 +202,10 @@ common.error=Kļūda
|
|||||||
common.success=Veiksmīgi
|
common.success=Veiksmīgi
|
||||||
common.required=Obligāts lauks
|
common.required=Obligāts lauks
|
||||||
|
|
||||||
|
# Duration
|
||||||
|
duration.hours.short=st.
|
||||||
|
duration.minutes.short=min.
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validation.required=Lauks ir obligāts
|
validation.required=Lauks ir obligāts
|
||||||
validation.email=Nederīga e-pasta adrese
|
validation.email=Nederīga e-pasta adrese
|
||||||
@@ -603,6 +609,7 @@ jobsummary.task.photo.maxonly=Atļautas ne vairāk kā {0} fotogrāfijas
|
|||||||
jobsummary.task.photo.taken=Uzņemtās fotogrāfijas ({0})
|
jobsummary.task.photo.taken=Uzņemtās fotogrāfijas ({0})
|
||||||
jobsummary.task.button.text=Pogas teksts
|
jobsummary.task.button.text=Pogas teksts
|
||||||
jobsummary.button.schliessen=Aizvērt
|
jobsummary.button.schliessen=Aizvērt
|
||||||
|
jobsummary.route.planned=Plānotais maršruts
|
||||||
|
|
||||||
# Jobs
|
# Jobs
|
||||||
jobs.title=Uzdevumi
|
jobs.title=Uzdevumi
|
||||||
@@ -730,6 +737,8 @@ statistics.loading=Aprēķina...
|
|||||||
# Job Status
|
# Job Status
|
||||||
jobstatus.IN_PROGRESS=Izpildē
|
jobstatus.IN_PROGRESS=Izpildē
|
||||||
jobstatus.COMPLETED=Pabeigts
|
jobstatus.COMPLETED=Pabeigts
|
||||||
|
jobstatus.CREATED=Izveidots
|
||||||
|
jobstatus.CANCELLED=Atcelts
|
||||||
|
|
||||||
# Task Types
|
# Task Types
|
||||||
tasktype.CONFIRMATION=Apstiprinājums
|
tasktype.CONFIRMATION=Apstiprinājums
|
||||||
@@ -832,6 +841,15 @@ start.imprint.company=Assecutor Data Service GmbH
|
|||||||
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
||||||
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
||||||
start.imprint.email=E-Mail: ahoi@assecutor.de
|
start.imprint.email=E-Mail: ahoi@assecutor.de
|
||||||
|
imprint.phone=Tālrunis
|
||||||
|
imprint.email=E-pasts
|
||||||
|
imprint.country=Vācija
|
||||||
|
imprint.management=Vadība
|
||||||
|
imprint.registeredoffice=Juridiskā adrese
|
||||||
|
imprint.commercialregister=Komercreģistrs
|
||||||
|
imprint.vatid=PVN ID
|
||||||
|
imprint.imagecredits=Izmantoto attēlu un grafiku avoti
|
||||||
|
imprint.backgroundimage=Sākumlapas fona attēls
|
||||||
start.cta.text=Reģistrējieties jau šodien un izmantojiet bezmaksas izmēģinājuma mēnesi, lai rūpīgi pārbaudītu sistēmu.
|
start.cta.text=Reģistrējieties jau šodien un izmantojiet bezmaksas izmēģinājuma mēnesi, lai rūpīgi pārbaudītu sistēmu.
|
||||||
start.slogan=Vadiet savu biznesu gudri ... ar votianLT!
|
start.slogan=Vadiet savu biznesu gudri ... ar votianLT!
|
||||||
start.version=Versija
|
start.version=Versija
|
||||||
@@ -914,6 +932,8 @@ jobhistory.info.createdat=Izveidots: {0}
|
|||||||
jobhistory.info.status=Statuss: {0}
|
jobhistory.info.status=Statuss: {0}
|
||||||
jobhistory.count={0} ieraksti vēsturē
|
jobhistory.count={0} ieraksti vēsturē
|
||||||
jobhistory.changedby=Mainīja: {0}
|
jobhistory.changedby=Mainīja: {0}
|
||||||
|
jobhistory.entry.create.reason=Uzdevums izveidots
|
||||||
|
jobhistory.entry.create.description=Izveidots jauns uzdevums: {0}
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
version.label=Versija
|
version.label=Versija
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ nav.users=U\u017cytkownicy
|
|||||||
nav.showprofile=Poka\u017c profil
|
nav.showprofile=Poka\u017c profil
|
||||||
nav.settings=Ustawienia
|
nav.settings=Ustawienia
|
||||||
nav.logout=Wyloguj si\u0119
|
nav.logout=Wyloguj si\u0119
|
||||||
|
logout.confirm.title=Potwierd\u017a wylogowanie
|
||||||
|
logout.confirm.message=Czy na pewno chcesz si\u0119 wylogowa\u0107?
|
||||||
|
|
||||||
# Profile View
|
# Profile View
|
||||||
profile.title=Edytuj profil
|
profile.title=Edytuj profil
|
||||||
@@ -200,6 +202,10 @@ common.error=B\u0142\u0105d
|
|||||||
common.success=Sukces
|
common.success=Sukces
|
||||||
common.required=Pole wymagane
|
common.required=Pole wymagane
|
||||||
|
|
||||||
|
# Duration
|
||||||
|
duration.hours.short=godz.
|
||||||
|
duration.minutes.short=min.
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validation.required=Pole jest wymagane
|
validation.required=Pole jest wymagane
|
||||||
validation.email=Nieprawid\u0142owy adres e-mail
|
validation.email=Nieprawid\u0142owy adres e-mail
|
||||||
@@ -603,6 +609,7 @@ jobsummary.task.photo.maxonly=Dozwolone maksymalnie {0} zdj\u0119\u0107
|
|||||||
jobsummary.task.photo.taken=Wykonane zdj\u0119cia ({0})
|
jobsummary.task.photo.taken=Wykonane zdj\u0119cia ({0})
|
||||||
jobsummary.task.button.text=Tekst przycisku
|
jobsummary.task.button.text=Tekst przycisku
|
||||||
jobsummary.button.schliessen=Zamknij
|
jobsummary.button.schliessen=Zamknij
|
||||||
|
jobsummary.route.planned=Planowana trasa
|
||||||
|
|
||||||
# Jobs
|
# Jobs
|
||||||
jobs.title=Zlecenia
|
jobs.title=Zlecenia
|
||||||
@@ -730,6 +737,8 @@ statistics.loading=Obliczanie...
|
|||||||
# Job Status
|
# Job Status
|
||||||
jobstatus.IN_PROGRESS=W trakcie realizacji
|
jobstatus.IN_PROGRESS=W trakcie realizacji
|
||||||
jobstatus.COMPLETED=Zako\u0144czone
|
jobstatus.COMPLETED=Zako\u0144czone
|
||||||
|
jobstatus.CREATED=Utworzone
|
||||||
|
jobstatus.CANCELLED=Anulowane
|
||||||
|
|
||||||
# Task Types
|
# Task Types
|
||||||
tasktype.CONFIRMATION=Potwierdzenie
|
tasktype.CONFIRMATION=Potwierdzenie
|
||||||
@@ -832,6 +841,15 @@ start.imprint.company=Assecutor Data Service GmbH
|
|||||||
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
||||||
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
||||||
start.imprint.email=E-Mail: ahoi@assecutor.de
|
start.imprint.email=E-Mail: ahoi@assecutor.de
|
||||||
|
imprint.phone=Telefon
|
||||||
|
imprint.email=E-mail
|
||||||
|
imprint.country=Niemcy
|
||||||
|
imprint.management=Zarz\u0105d
|
||||||
|
imprint.registeredoffice=Siedziba firmy
|
||||||
|
imprint.commercialregister=Rejestr handlowy
|
||||||
|
imprint.vatid=Numer VAT
|
||||||
|
imprint.imagecredits=\u0179r\u00f3d\u0142a wykorzystanych zdj\u0119\u0107 i grafik
|
||||||
|
imprint.backgroundimage=Obraz t\u0142a strony startowej
|
||||||
start.cta.text=Zarejestruj si\u0119 ju\u017c dzi\u015b i skorzystaj z bezp\u0142atnego miesi\u0105ca pr\u00f3bnego, aby dok\u0142adnie przetestowa\u0107 system.
|
start.cta.text=Zarejestruj si\u0119 ju\u017c dzi\u015b i skorzystaj z bezp\u0142atnego miesi\u0105ca pr\u00f3bnego, aby dok\u0142adnie przetestowa\u0107 system.
|
||||||
start.slogan=Prowad\u017a sw\u00f3j biznes m\u0105drze \u2026 z votianLT!
|
start.slogan=Prowad\u017a sw\u00f3j biznes m\u0105drze \u2026 z votianLT!
|
||||||
start.version=Wersja
|
start.version=Wersja
|
||||||
@@ -914,6 +932,8 @@ jobhistory.info.createdat=Utworzono dnia: {0}
|
|||||||
jobhistory.info.status=Status: {0}
|
jobhistory.info.status=Status: {0}
|
||||||
jobhistory.count={0} wpis\u00f3w w historii
|
jobhistory.count={0} wpis\u00f3w w historii
|
||||||
jobhistory.changedby=Zmienione przez: {0}
|
jobhistory.changedby=Zmienione przez: {0}
|
||||||
|
jobhistory.entry.create.reason=Zlecenie utworzone
|
||||||
|
jobhistory.entry.create.description=Utworzono nowe zlecenie: {0}
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
version.label=Wersja
|
version.label=Wersja
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ nav.users=Пользователи
|
|||||||
nav.showprofile=Показать профиль
|
nav.showprofile=Показать профиль
|
||||||
nav.settings=Настройки
|
nav.settings=Настройки
|
||||||
nav.logout=Выйти
|
nav.logout=Выйти
|
||||||
|
logout.confirm.title=Подтвердите выход
|
||||||
|
logout.confirm.message=Вы действительно хотите выйти?
|
||||||
|
|
||||||
# Profile View
|
# Profile View
|
||||||
profile.title=Редактирование профиля
|
profile.title=Редактирование профиля
|
||||||
@@ -200,6 +202,10 @@ common.error=Ошибка
|
|||||||
common.success=Успех
|
common.success=Успех
|
||||||
common.required=Обязательное поле
|
common.required=Обязательное поле
|
||||||
|
|
||||||
|
# Duration
|
||||||
|
duration.hours.short=ч
|
||||||
|
duration.minutes.short=мин.
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validation.required=Поле обязательно для заполнения
|
validation.required=Поле обязательно для заполнения
|
||||||
validation.email=Недействительный адрес электронной почты
|
validation.email=Недействительный адрес электронной почты
|
||||||
@@ -603,6 +609,7 @@ jobsummary.task.photo.maxonly=Максимум {0} фото разрешено
|
|||||||
jobsummary.task.photo.taken=Сделанные фотографии ({0})
|
jobsummary.task.photo.taken=Сделанные фотографии ({0})
|
||||||
jobsummary.task.button.text=Текст кнопки
|
jobsummary.task.button.text=Текст кнопки
|
||||||
jobsummary.button.schliessen=Закрыть
|
jobsummary.button.schliessen=Закрыть
|
||||||
|
jobsummary.route.planned=Запланированный маршрут
|
||||||
|
|
||||||
# Jobs
|
# Jobs
|
||||||
jobs.title=Заказы
|
jobs.title=Заказы
|
||||||
@@ -730,6 +737,8 @@ statistics.loading=Вычисление...
|
|||||||
# Job Status
|
# Job Status
|
||||||
jobstatus.IN_PROGRESS=В обработке
|
jobstatus.IN_PROGRESS=В обработке
|
||||||
jobstatus.COMPLETED=Завершено
|
jobstatus.COMPLETED=Завершено
|
||||||
|
jobstatus.CREATED=Создано
|
||||||
|
jobstatus.CANCELLED=Отменено
|
||||||
|
|
||||||
# Task Types
|
# Task Types
|
||||||
tasktype.CONFIRMATION=Подтверждение
|
tasktype.CONFIRMATION=Подтверждение
|
||||||
@@ -832,6 +841,15 @@ start.imprint.company=Assecutor Data Service GmbH
|
|||||||
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
||||||
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
||||||
start.imprint.email=E-Mail: ahoi@assecutor.de
|
start.imprint.email=E-Mail: ahoi@assecutor.de
|
||||||
|
imprint.phone=Телефон
|
||||||
|
imprint.email=Эл. почта
|
||||||
|
imprint.country=Германия
|
||||||
|
imprint.management=Руководство
|
||||||
|
imprint.registeredoffice=Юридический адрес
|
||||||
|
imprint.commercialregister=Торговый реестр
|
||||||
|
imprint.vatid=Номер НДС
|
||||||
|
imprint.imagecredits=Источники использованных изображений и графики
|
||||||
|
imprint.backgroundimage=Фоновое изображение главной страницы
|
||||||
start.cta.text=Зарегистрируйтесь сегодня и воспользуйтесь бесплатным пробным месяцем, чтобы тщательно протестировать систему.
|
start.cta.text=Зарегистрируйтесь сегодня и воспользуйтесь бесплатным пробным месяцем, чтобы тщательно протестировать систему.
|
||||||
start.slogan=Ведите свой бизнес умно ... с votianLT!
|
start.slogan=Ведите свой бизнес умно ... с votianLT!
|
||||||
start.version=Версия
|
start.version=Версия
|
||||||
@@ -914,6 +932,8 @@ jobhistory.info.createdat=Создано: {0}
|
|||||||
jobhistory.info.status=Статус: {0}
|
jobhistory.info.status=Статус: {0}
|
||||||
jobhistory.count={0} записей в истории
|
jobhistory.count={0} записей в истории
|
||||||
jobhistory.changedby=Изменено: {0}
|
jobhistory.changedby=Изменено: {0}
|
||||||
|
jobhistory.entry.create.reason=Заказ создан
|
||||||
|
jobhistory.entry.create.description=Создан новый заказ: {0}
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
version.label=Версия
|
version.label=Версия
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ nav.users=Kullan\u0131c\u0131lar
|
|||||||
nav.showprofile=Profili G\u00f6ster
|
nav.showprofile=Profili G\u00f6ster
|
||||||
nav.settings=Ayarlar
|
nav.settings=Ayarlar
|
||||||
nav.logout=\u00c7\u0131k\u0131\u015f
|
nav.logout=\u00c7\u0131k\u0131\u015f
|
||||||
|
logout.confirm.title=\u00c7\u0131k\u0131\u015f\u0131 onayla
|
||||||
|
logout.confirm.message=Ger\u00e7ekten \u00e7\u0131k\u0131\u015f yapmak istiyor musunuz?
|
||||||
|
|
||||||
# Profile View
|
# Profile View
|
||||||
profile.title=Profili D\u00fczenle
|
profile.title=Profili D\u00fczenle
|
||||||
@@ -200,6 +202,10 @@ common.error=Hata
|
|||||||
common.success=Ba\u015far\u0131l\u0131
|
common.success=Ba\u015far\u0131l\u0131
|
||||||
common.required=Zorunlu Alan
|
common.required=Zorunlu Alan
|
||||||
|
|
||||||
|
# Duration
|
||||||
|
duration.hours.short=sa.
|
||||||
|
duration.minutes.short=dk.
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validation.required=Alan gereklidir
|
validation.required=Alan gereklidir
|
||||||
validation.email=Ge\u00e7ersiz e-posta adresi
|
validation.email=Ge\u00e7ersiz e-posta adresi
|
||||||
@@ -603,6 +609,7 @@ jobsummary.task.photo.maxonly=En fazla {0} foto\u011fraf izin verilir
|
|||||||
jobsummary.task.photo.taken=\u00c7ekilen Foto\u011fraflar ({0})
|
jobsummary.task.photo.taken=\u00c7ekilen Foto\u011fraflar ({0})
|
||||||
jobsummary.task.button.text=Buton Metni
|
jobsummary.task.button.text=Buton Metni
|
||||||
jobsummary.button.schliessen=Kapat
|
jobsummary.button.schliessen=Kapat
|
||||||
|
jobsummary.route.planned=Planlanan Rota
|
||||||
|
|
||||||
# Jobs
|
# Jobs
|
||||||
jobs.title=\u0130\u015fler
|
jobs.title=\u0130\u015fler
|
||||||
@@ -730,6 +737,8 @@ statistics.loading=Hesaplan\u0131yor...
|
|||||||
# Job Status
|
# Job Status
|
||||||
jobstatus.IN_PROGRESS=Devam Ediyor
|
jobstatus.IN_PROGRESS=Devam Ediyor
|
||||||
jobstatus.COMPLETED=Tamamland\u0131
|
jobstatus.COMPLETED=Tamamland\u0131
|
||||||
|
jobstatus.CREATED=Olu\u015fturuldu
|
||||||
|
jobstatus.CANCELLED=\u0130ptal Edildi
|
||||||
|
|
||||||
# Task Types
|
# Task Types
|
||||||
tasktype.CONFIRMATION=Onay
|
tasktype.CONFIRMATION=Onay
|
||||||
@@ -832,6 +841,15 @@ start.imprint.company=Assecutor Data Service GmbH
|
|||||||
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
start.imprint.address=Ottensener Str. 8, 22525 Hamburg
|
||||||
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
start.imprint.phone=Telefon: +49 40 18 123 771 0
|
||||||
start.imprint.email=E-Mail: ahoi@assecutor.de
|
start.imprint.email=E-Mail: ahoi@assecutor.de
|
||||||
|
imprint.phone=Telefon
|
||||||
|
imprint.email=E-posta
|
||||||
|
imprint.country=Almanya
|
||||||
|
imprint.management=Y\u00f6netim
|
||||||
|
imprint.registeredoffice=\u015eirket merkezi
|
||||||
|
imprint.commercialregister=Ticaret sicili
|
||||||
|
imprint.vatid=KDV kimli\u011fi
|
||||||
|
imprint.imagecredits=Kullan\u0131lan g\u00f6rsel ve grafik kaynaklar\u0131
|
||||||
|
imprint.backgroundimage=Ana sayfa arka plan g\u00f6rseli
|
||||||
start.cta.text=Bug\u00fcn kay\u0131t olun ve sistemi ba\u015ftan sona test etmek i\u00e7in \u00fccretsiz deneme ay\u0131n\u0131 kullan\u0131n.
|
start.cta.text=Bug\u00fcn kay\u0131t olun ve sistemi ba\u015ftan sona test etmek i\u00e7in \u00fccretsiz deneme ay\u0131n\u0131 kullan\u0131n.
|
||||||
start.slogan=\u0130\u015finizi ak\u0131ll\u0131ca y\u00f6netin ... votianLT ile!
|
start.slogan=\u0130\u015finizi ak\u0131ll\u0131ca y\u00f6netin ... votianLT ile!
|
||||||
start.version=S\u00fcr\u00fcm
|
start.version=S\u00fcr\u00fcm
|
||||||
@@ -914,6 +932,8 @@ jobhistory.info.createdat=Olu\u015fturulma Tarihi: {0}
|
|||||||
jobhistory.info.status=Durum: {0}
|
jobhistory.info.status=Durum: {0}
|
||||||
jobhistory.count=Ge\u00e7mi\u015fte {0} kay\u0131t
|
jobhistory.count=Ge\u00e7mi\u015fte {0} kay\u0131t
|
||||||
jobhistory.changedby=De\u011fi\u015ftiren: {0}
|
jobhistory.changedby=De\u011fi\u015ftiren: {0}
|
||||||
|
jobhistory.entry.create.reason=\u0130\u015f olu\u015fturuldu
|
||||||
|
jobhistory.entry.create.description=Yeni i\u015f olu\u015fturuldu: {0}
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
version.label=S\u00fcr\u00fcm
|
version.label=S\u00fcr\u00fcm
|
||||||
|
|||||||
16
upload.sh
Executable file
16
upload.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
find . \
|
||||||
|
-type f \
|
||||||
|
\( -name "*.java" -o -name "*.dart" -o -name "*.md" \
|
||||||
|
-o -name "*.yaml" -o -name "*.yml" -o -name "*.properties" \
|
||||||
|
-o -name "*.xml" -o -name "*.gradle" \) \
|
||||||
|
! -path "*/build/*" \
|
||||||
|
! -path "*/.gradle/*" \
|
||||||
|
! -path "*/.dart_tool/*" \
|
||||||
|
! -path "*/node_modules/*" \
|
||||||
|
! -path "*/.git/*" \
|
||||||
|
| while read file; do
|
||||||
|
echo "Upload: $file"
|
||||||
|
curl -s -X POST "http://192.168.180.16:3001/api/v1/document/upload/votianlt" \
|
||||||
|
-H "Authorization: Bearer W25V4A8-5E24XSH-KKNJBX3-WN07B7P" \
|
||||||
|
-F "file=@$file"
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user