sistemati ticket
This commit is contained in:
@@ -1,24 +1,27 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flux/core/routes/routes.dart';
|
||||||
import 'package:flux/features/customers/blocs/customers_cubit.dart';
|
import 'package:flux/features/customers/blocs/customers_cubit.dart';
|
||||||
import 'package:flux/features/customers/models/customer_model.dart';
|
import 'package:flux/features/customers/models/customer_model.dart';
|
||||||
import 'package:flux/features/customers/ui/quick_customer_dialog.dart';
|
import 'package:flux/features/customers/ui/quick_customer_dialog.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class SharedCustomerSection extends StatelessWidget {
|
class SharedCustomerSection extends StatelessWidget {
|
||||||
final String? customerId;
|
final CustomerModel? customer;
|
||||||
final String? customerName;
|
|
||||||
final ValueChanged<CustomerModel> onCustomerSelected;
|
final ValueChanged<CustomerModel> onCustomerSelected;
|
||||||
|
|
||||||
const SharedCustomerSection({
|
const SharedCustomerSection({
|
||||||
super.key,
|
super.key,
|
||||||
this.customerId,
|
this.customer,
|
||||||
this.customerName,
|
|
||||||
required this.onCustomerSelected,
|
required this.onCustomerSelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final hasCustomer = customerId != null && customerId!.isNotEmpty;
|
final hasCustomer = customer != null && customer!.id!.isNotEmpty;
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -47,7 +50,7 @@ class SharedCustomerSection extends StatelessWidget {
|
|||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
hasCustomer ? customerName! : 'Seleziona Cliente *',
|
hasCustomer ? customer!.name : 'Seleziona Cliente *',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: hasCustomer
|
fontWeight: hasCustomer
|
||||||
? FontWeight.bold
|
? FontWeight.bold
|
||||||
@@ -57,10 +60,145 @@ class SharedCustomerSection extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Icon(Icons.search),
|
const Icon(Icons.search),
|
||||||
|
|
||||||
|
if (hasCustomer) ...[
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => context.pushNamed(
|
||||||
|
Routes.customerForm,
|
||||||
|
pathParameters: {'id': customer!.id!},
|
||||||
|
extra: customer,
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (hasCustomer &&
|
||||||
|
(customer!.phoneNumber.isNotEmpty ||
|
||||||
|
customer!.email.isNotEmpty)) ...[
|
||||||
|
const SizedBox(height: 12), // Un po' più di respiro dal box sopra
|
||||||
|
// Mettiamo i contatti in un Container con un po' di stile per farli sembrare una "Contact Card" integrata
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.withValues(alpha: 0.05), // Sfondo leggerissimo
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: Colors.grey.withValues(alpha: 0.2)),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// --- RIGA TELEFONO ---
|
||||||
|
if (customer!.phoneNumber.isNotEmpty)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
// Usiamo i pulsanti "Small" per non occupare troppo spazio verticale
|
||||||
|
IconButton(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
onPressed: () => launchUrl(
|
||||||
|
Uri.parse('https://wa.me/39${customer!.phoneNumber}'),
|
||||||
|
),
|
||||||
|
icon: const FaIcon(
|
||||||
|
FontAwesomeIcons.whatsapp,
|
||||||
|
color: Colors.green,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
tooltip: 'Invia WhatsApp',
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
// Expanded evita l'overflow se il numero è assurdamente lungo
|
||||||
|
child: SelectableText(
|
||||||
|
customer!.phoneNumber,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
onPressed: () {
|
||||||
|
Clipboard.setData(
|
||||||
|
ClipboardData(text: customer!.phoneNumber),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Telefono copiato!'),
|
||||||
|
duration: Duration(seconds: 2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.copy,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
tooltip: 'Copia',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// Sezione divisoria se ci sono entrambi
|
||||||
|
if (customer!.phoneNumber.isNotEmpty &&
|
||||||
|
customer!.email.isNotEmpty)
|
||||||
|
const Divider(height: 8, thickness: 0.5),
|
||||||
|
|
||||||
|
// --- RIGA EMAIL ---
|
||||||
|
if (customer!.email.isNotEmpty)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
onPressed: () => launchUrl(
|
||||||
|
Uri.parse('mailto:${customer!.email}'),
|
||||||
|
), // Rimosso il // dopo mailto:, è più sicuro
|
||||||
|
icon: const FaIcon(
|
||||||
|
FontAwesomeIcons.envelope,
|
||||||
|
color: Colors.blue,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
tooltip: 'Invia Email',
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
// L'Expanded è vitale per le email che possono essere lunghissime
|
||||||
|
child: SelectableText(
|
||||||
|
customer!.email,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
onPressed: () {
|
||||||
|
Clipboard.setData(
|
||||||
|
ClipboardData(text: customer!.email),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Email copiata!'),
|
||||||
|
duration: Duration(seconds: 2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.copy,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
tooltip: 'Copia',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ class SharedModelSection extends StatelessWidget {
|
|||||||
final String? modelId;
|
final String? modelId;
|
||||||
final String? modelName;
|
final String? modelName;
|
||||||
final String label;
|
final String label;
|
||||||
|
final Color? backgroundColor;
|
||||||
|
final Color? borderColor;
|
||||||
|
|
||||||
// Usiamo una callback che passa direttamente ID e Nome
|
// Usiamo una callback che passa direttamente ID e Nome
|
||||||
// così non dobbiamo preoccuparci di importare la classe esatta del modello ovunque
|
// così non dobbiamo preoccuparci di importare la classe esatta del modello ovunque
|
||||||
@@ -18,6 +20,8 @@ class SharedModelSection extends StatelessWidget {
|
|||||||
required this.modelName,
|
required this.modelName,
|
||||||
required this.onModelSelected,
|
required this.onModelSelected,
|
||||||
this.label = 'Seleziona Modello',
|
this.label = 'Seleziona Modello',
|
||||||
|
this.backgroundColor,
|
||||||
|
this.borderColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -26,6 +30,7 @@ class SharedModelSection extends StatelessWidget {
|
|||||||
final hasModel = modelId != null && modelId!.isNotEmpty;
|
final hasModel = modelId != null && modelId!.isNotEmpty;
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
|
tileColor: backgroundColor,
|
||||||
title: Text(label),
|
title: Text(label),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
hasModel ? modelName! : 'Nessun modello selezionato',
|
hasModel ? modelName! : 'Nessun modello selezionato',
|
||||||
@@ -36,7 +41,7 @@ class SharedModelSection extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
trailing: const Icon(Icons.arrow_drop_down),
|
trailing: const Icon(Icons.arrow_drop_down),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
side: BorderSide(color: theme.dividerColor),
|
side: BorderSide(color: borderColor ?? theme.dividerColor),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
onTap: () => _showModelModal(context),
|
onTap: () => _showModelModal(context),
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ class _LatestOperationsCardContent extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 5,
|
flex: 5,
|
||||||
child: Text(
|
child: Text(
|
||||||
operation.customerDisplayName ??
|
operation.customer?.name ??
|
||||||
'Cliente sconosciuto',
|
'Cliente sconosciuto',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||||
|
import 'package:flux/features/customers/models/customer_model.dart';
|
||||||
import 'package:flux/features/operations/data/operations_repository.dart';
|
import 'package:flux/features/operations/data/operations_repository.dart';
|
||||||
import 'package:flux/features/operations/models/operation_model.dart';
|
import 'package:flux/features/operations/models/operation_model.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
@@ -90,7 +91,7 @@ class OperationFormCubit extends Cubit<OperationFormState> {
|
|||||||
storeDisplayName: current.storeDisplayName,
|
storeDisplayName: current.storeDisplayName,
|
||||||
batchUuid: current.batchUuid, // MANTIENE IL COLLEGAMENTO
|
batchUuid: current.batchUuid, // MANTIENE IL COLLEGAMENTO
|
||||||
customerId: current.customerId, // MANTIENE IL CLIENTE
|
customerId: current.customerId, // MANTIENE IL CLIENTE
|
||||||
customerDisplayName: current.customerDisplayName,
|
customer: current.customer,
|
||||||
status: OperationStatus.draft,
|
status: OperationStatus.draft,
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
),
|
),
|
||||||
@@ -178,8 +179,7 @@ class OperationFormCubit extends Cubit<OperationFormState> {
|
|||||||
// --- GESTIONE DEI CAMPI IN TEMPO REALE ---
|
// --- GESTIONE DEI CAMPI IN TEMPO REALE ---
|
||||||
|
|
||||||
void updateFields({
|
void updateFields({
|
||||||
String? customerId,
|
CustomerModel? customer,
|
||||||
String? customerDisplayName,
|
|
||||||
String? reference,
|
String? reference,
|
||||||
String? note,
|
String? note,
|
||||||
String? type,
|
String? type,
|
||||||
@@ -211,10 +211,8 @@ class OperationFormCubit extends Cubit<OperationFormState> {
|
|||||||
if (quantity != null && quantity > 0) newQuantity = quantity;
|
if (quantity != null && quantity > 0) newQuantity = quantity;
|
||||||
|
|
||||||
final updated = current.copyWith(
|
final updated = current.copyWith(
|
||||||
customerId:
|
customer: customer ?? current.customer,
|
||||||
customerId ??
|
customerId: customer?.id ?? current.customerId,
|
||||||
current.customerId, // Se non passo customerId, tengo il vecchio
|
|
||||||
customerDisplayName: customerDisplayName ?? current.customerDisplayName,
|
|
||||||
reference: reference ?? current.reference,
|
reference: reference ?? current.reference,
|
||||||
note: note ?? current.note,
|
note: note ?? current.note,
|
||||||
providerId: clearProvider ? null : (providerId ?? current.providerId),
|
providerId: clearProvider ? null : (providerId ?? current.providerId),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flux/core/utils/extensions.dart';
|
import 'package:flux/core/utils/extensions.dart';
|
||||||
import 'package:flux/features/attachments/models/attachment_model.dart';
|
import 'package:flux/features/attachments/models/attachment_model.dart';
|
||||||
|
import 'package:flux/features/customers/models/customer_model.dart';
|
||||||
|
|
||||||
enum OperationStatus {
|
enum OperationStatus {
|
||||||
success('success', 'OK'),
|
success('success', 'OK'),
|
||||||
@@ -45,7 +46,7 @@ class OperationModel extends Equatable {
|
|||||||
final String? lastCampaignId;
|
final String? lastCampaignId;
|
||||||
final OperationStatus status;
|
final OperationStatus status;
|
||||||
final String? customerId;
|
final String? customerId;
|
||||||
final String? customerDisplayName;
|
final CustomerModel? customer;
|
||||||
final String reference;
|
final String reference;
|
||||||
|
|
||||||
// ALLEGATI (Aggiunto)
|
// ALLEGATI (Aggiunto)
|
||||||
@@ -74,7 +75,7 @@ class OperationModel extends Equatable {
|
|||||||
this.lastCampaignId,
|
this.lastCampaignId,
|
||||||
this.status = OperationStatus.draft,
|
this.status = OperationStatus.draft,
|
||||||
this.customerId,
|
this.customerId,
|
||||||
this.customerDisplayName,
|
this.customer,
|
||||||
this.reference = '',
|
this.reference = '',
|
||||||
this.attachments = const [],
|
this.attachments = const [],
|
||||||
});
|
});
|
||||||
@@ -102,7 +103,7 @@ class OperationModel extends Equatable {
|
|||||||
String? lastCampaignId,
|
String? lastCampaignId,
|
||||||
OperationStatus? status,
|
OperationStatus? status,
|
||||||
String? customerId,
|
String? customerId,
|
||||||
String? customerDisplayName,
|
CustomerModel? customer,
|
||||||
String? reference,
|
String? reference,
|
||||||
List<AttachmentModel>? attachments,
|
List<AttachmentModel>? attachments,
|
||||||
}) => OperationModel(
|
}) => OperationModel(
|
||||||
@@ -128,7 +129,7 @@ class OperationModel extends Equatable {
|
|||||||
lastCampaignId: lastCampaignId ?? this.lastCampaignId,
|
lastCampaignId: lastCampaignId ?? this.lastCampaignId,
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
customerId: customerId ?? this.customerId,
|
customerId: customerId ?? this.customerId,
|
||||||
customerDisplayName: customerDisplayName ?? this.customerDisplayName,
|
customer: customer ?? this.customer,
|
||||||
reference: reference ?? this.reference,
|
reference: reference ?? this.reference,
|
||||||
attachments: attachments ?? this.attachments,
|
attachments: attachments ?? this.attachments,
|
||||||
);
|
);
|
||||||
@@ -157,7 +158,7 @@ class OperationModel extends Equatable {
|
|||||||
lastCampaignId,
|
lastCampaignId,
|
||||||
status,
|
status,
|
||||||
customerId,
|
customerId,
|
||||||
customerDisplayName,
|
customer,
|
||||||
reference,
|
reference,
|
||||||
attachments,
|
attachments,
|
||||||
];
|
];
|
||||||
@@ -207,9 +208,11 @@ class OperationModel extends Equatable {
|
|||||||
|
|
||||||
lastCampaignId: map['last_campaign_id'] as String?,
|
lastCampaignId: map['last_campaign_id'] as String?,
|
||||||
status: OperationStatus.fromString(map['status'] ?? 'draft'),
|
status: OperationStatus.fromString(map['status'] ?? 'draft'),
|
||||||
|
|
||||||
customerId: map['customer_id'] as String?,
|
customerId: map['customer_id'] as String?,
|
||||||
customerDisplayName: (map['customer']?['name'] as String?)?.myFormat(),
|
|
||||||
|
customer: map['customer'] != null
|
||||||
|
? CustomerModel.fromMap(map['customer'] as Map<String, dynamic>)
|
||||||
|
: null,
|
||||||
|
|
||||||
attachments:
|
attachments:
|
||||||
(map['attachment'] as List?)
|
(map['attachment'] as List?)
|
||||||
|
|||||||
@@ -265,23 +265,6 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
|||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
|
||||||
flex: 1,
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: state.status == OperationFormStatus.saving
|
|
||||||
? null
|
|
||||||
: () => _saveOperation(
|
|
||||||
keepAdding: true,
|
|
||||||
targetStatus:
|
|
||||||
displayStatus, // <-- Usiamo lo stato selezionato nel form!
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Salva e Aggiungi Altro',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
@@ -317,6 +300,24 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
|||||||
: const Text('Salva ed Esci'),
|
: const Text('Salva ed Esci'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: state.status == OperationFormStatus.saving
|
||||||
|
? null
|
||||||
|
: () => _saveOperation(
|
||||||
|
keepAdding: true,
|
||||||
|
targetStatus:
|
||||||
|
displayStatus, // <-- Usiamo lo stato selezionato nel form!
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'Salva e Aggiungi Altro',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -466,13 +467,9 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
|||||||
|
|
||||||
Widget _buildCustomerSection(OperationFormState state) {
|
Widget _buildCustomerSection(OperationFormState state) {
|
||||||
return SharedCustomerSection(
|
return SharedCustomerSection(
|
||||||
customerId: state.operation.customerId,
|
customer: state.operation.customer,
|
||||||
customerName: state.operation.customerDisplayName,
|
|
||||||
onCustomerSelected: (customer) {
|
onCustomerSelected: (customer) {
|
||||||
context.read<OperationFormCubit>().updateFields(
|
context.read<OperationFormCubit>().updateFields(customer: customer);
|
||||||
customerId: customer.id,
|
|
||||||
customerDisplayName: customer.name,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -559,7 +556,7 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
|||||||
return SharedAttachmentsSection(
|
return SharedAttachmentsSection(
|
||||||
parentType: AttachmentParentType.operation,
|
parentType: AttachmentParentType.operation,
|
||||||
parentId: state.operation.id,
|
parentId: state.operation.id,
|
||||||
titleForUpload: state.operation.customerDisplayName ?? 'Nuova pratica',
|
titleForUpload: state.operation.customer?.name ?? 'Nuova pratica',
|
||||||
onGenerateIdForQr: _generateIdForQr,
|
onGenerateIdForQr: _generateIdForQr,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ class _OperationListScreenState extends State<OperationListScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
operation.customerDisplayName ?? "Cliente sconosciuto",
|
operation.customer?.name ?? "Cliente sconosciuto",
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ class TicketFormCubit extends Cubit<TicketFormState> {
|
|||||||
state.copyWith(
|
state.copyWith(
|
||||||
ticket: state.ticket.copyWith(
|
ticket: state.ticket.copyWith(
|
||||||
customerId: customer.id,
|
customerId: customer.id,
|
||||||
customerName: customer.name,
|
customer: customer,
|
||||||
alternativePhoneNumber: customer.phoneNumber,
|
alternativePhoneNumber:
|
||||||
customerEmail: customer.email,
|
state.ticket.alternativePhoneNumber ?? customer.phoneNumber,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -92,6 +92,17 @@ class TicketFormCubit extends Cubit<TicketFormState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateSourceModel({required String modelId, required String modelName}) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
ticket: state.ticket.copyWith(
|
||||||
|
sourceModelId: modelId,
|
||||||
|
sourceModelName: modelName,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void updateCreator({required String staffId, required String staffName}) {
|
void updateCreator({required String staffId, required String staffName}) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
@@ -109,6 +120,7 @@ class TicketFormCubit extends Cubit<TicketFormState> {
|
|||||||
TicketStatus? status,
|
TicketStatus? status,
|
||||||
String? request,
|
String? request,
|
||||||
String? targetSn,
|
String? targetSn,
|
||||||
|
String? sourceSn,
|
||||||
String? alternativePhoneNumber,
|
String? alternativePhoneNumber,
|
||||||
bool? hasCourtesyDevice,
|
bool? hasCourtesyDevice,
|
||||||
String? includedAccessories,
|
String? includedAccessories,
|
||||||
@@ -126,6 +138,7 @@ class TicketFormCubit extends Cubit<TicketFormState> {
|
|||||||
ticketStatus: status ?? state.ticket.ticketStatus,
|
ticketStatus: status ?? state.ticket.ticketStatus,
|
||||||
request: request ?? state.ticket.request,
|
request: request ?? state.ticket.request,
|
||||||
targetSn: targetSn ?? state.ticket.targetSn,
|
targetSn: targetSn ?? state.ticket.targetSn,
|
||||||
|
sourceSn: sourceSn ?? state.ticket.sourceSn,
|
||||||
alternativePhoneNumber:
|
alternativePhoneNumber:
|
||||||
alternativePhoneNumber ?? state.ticket.alternativePhoneNumber,
|
alternativePhoneNumber ?? state.ticket.alternativePhoneNumber,
|
||||||
hasCourtesyDevice:
|
hasCourtesyDevice:
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flux/core/utils/extensions.dart';
|
import 'package:flux/core/utils/extensions.dart';
|
||||||
|
import 'package:flux/features/customers/models/customer_model.dart';
|
||||||
|
|
||||||
/// Enum per il tipo di ticket
|
/// Enum per il tipo di ticket
|
||||||
enum TicketType {
|
enum TicketType {
|
||||||
repair('repair', 'Riparazione'),
|
repair('repair', 'Riparazione'),
|
||||||
softwareSetup('software_setup', 'Setup software'),
|
softwareSetup('software_setup', 'Impost. software'),
|
||||||
dataTransfer('data_transfer', 'Trasferimento dati'),
|
dataTransfer('data_transfer', 'Trasf. dati'),
|
||||||
operationTicket('operation_ticket', 'Ticket di operazione'),
|
operationTicket('operation_ticket', 'Ticket operazione'),
|
||||||
other('other', 'Altro');
|
other('other', 'Altro');
|
||||||
|
|
||||||
final String value;
|
final String value;
|
||||||
@@ -106,8 +107,7 @@ class TicketModel extends Equatable {
|
|||||||
final DateTime? estimatedDeliveryAt;
|
final DateTime? estimatedDeliveryAt;
|
||||||
final TicketResult? ticketResult;
|
final TicketResult? ticketResult;
|
||||||
final String? resolutionNotes;
|
final String? resolutionNotes;
|
||||||
final String? customerName;
|
final CustomerModel? customer;
|
||||||
final String? customerEmail;
|
|
||||||
final String? targetModelName;
|
final String? targetModelName;
|
||||||
final String? sourceModelName;
|
final String? sourceModelName;
|
||||||
final String? createdById;
|
final String? createdById;
|
||||||
@@ -142,8 +142,7 @@ class TicketModel extends Equatable {
|
|||||||
this.estimatedDeliveryAt,
|
this.estimatedDeliveryAt,
|
||||||
this.ticketResult,
|
this.ticketResult,
|
||||||
this.resolutionNotes,
|
this.resolutionNotes,
|
||||||
this.customerName,
|
this.customer,
|
||||||
this.customerEmail,
|
|
||||||
this.targetModelName,
|
this.targetModelName,
|
||||||
this.sourceModelName,
|
this.sourceModelName,
|
||||||
this.createdById,
|
this.createdById,
|
||||||
@@ -193,8 +192,7 @@ class TicketModel extends Equatable {
|
|||||||
DateTime? estimatedDeliveryAt,
|
DateTime? estimatedDeliveryAt,
|
||||||
TicketResult? ticketResult,
|
TicketResult? ticketResult,
|
||||||
String? resolutionNotes,
|
String? resolutionNotes,
|
||||||
String? customerName,
|
CustomerModel? customer,
|
||||||
String? customerEmail,
|
|
||||||
String? targetModelName,
|
String? targetModelName,
|
||||||
String? sourceModelName,
|
String? sourceModelName,
|
||||||
String? createdById,
|
String? createdById,
|
||||||
@@ -230,8 +228,7 @@ class TicketModel extends Equatable {
|
|||||||
estimatedDeliveryAt: estimatedDeliveryAt ?? this.estimatedDeliveryAt,
|
estimatedDeliveryAt: estimatedDeliveryAt ?? this.estimatedDeliveryAt,
|
||||||
ticketResult: ticketResult ?? this.ticketResult,
|
ticketResult: ticketResult ?? this.ticketResult,
|
||||||
resolutionNotes: resolutionNotes ?? this.resolutionNotes,
|
resolutionNotes: resolutionNotes ?? this.resolutionNotes,
|
||||||
customerName: customerName ?? this.customerName,
|
customer: customer ?? this.customer,
|
||||||
customerEmail: customerEmail ?? this.customerEmail,
|
|
||||||
targetModelName: targetModelName ?? this.targetModelName,
|
targetModelName: targetModelName ?? this.targetModelName,
|
||||||
sourceModelName: sourceModelName ?? this.sourceModelName,
|
sourceModelName: sourceModelName ?? this.sourceModelName,
|
||||||
createdById: createdById ?? this.createdById,
|
createdById: createdById ?? this.createdById,
|
||||||
@@ -279,8 +276,9 @@ class TicketModel extends Equatable {
|
|||||||
: null,
|
: null,
|
||||||
ticketResult: TicketResult.fromString(map['ticket_result'] as String?),
|
ticketResult: TicketResult.fromString(map['ticket_result'] as String?),
|
||||||
resolutionNotes: map['resolution_notes'] as String?,
|
resolutionNotes: map['resolution_notes'] as String?,
|
||||||
customerName: (map['customer']?['name'] as String?).myFormat(),
|
customer: map['customer'] != null
|
||||||
customerEmail: (map['customer']?['email'] as String?).myFormat(),
|
? CustomerModel.fromMap(map['customer'] as Map<String, dynamic>)
|
||||||
|
: null,
|
||||||
targetModelName: (map['target_model']?['name_with_brand'] as String?)
|
targetModelName: (map['target_model']?['name_with_brand'] as String?)
|
||||||
?.myFormat(),
|
?.myFormat(),
|
||||||
sourceModelName: (map['source_model']?['name_with_brand'] as String?)
|
sourceModelName: (map['source_model']?['name_with_brand'] as String?)
|
||||||
@@ -354,8 +352,7 @@ class TicketModel extends Equatable {
|
|||||||
ticketResult,
|
ticketResult,
|
||||||
resolutionNotes,
|
resolutionNotes,
|
||||||
includedAccessories,
|
includedAccessories,
|
||||||
customerName,
|
customer,
|
||||||
customerEmail,
|
|
||||||
targetModelName,
|
targetModelName,
|
||||||
sourceModelName,
|
sourceModelName,
|
||||||
createdById,
|
createdById,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import 'package:flux/core/widgets/shared_forms/staff_section.dart';
|
|||||||
import 'package:flux/features/tickets/models/ticket_status_extension.dart';
|
import 'package:flux/features/tickets/models/ticket_status_extension.dart';
|
||||||
import 'package:flux/features/tickets/utils/ticket_pdf_service.dart';
|
import 'package:flux/features/tickets/utils/ticket_pdf_service.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:pdf/pdf.dart';
|
|
||||||
import 'package:printing/printing.dart';
|
import 'package:printing/printing.dart';
|
||||||
|
|
||||||
class TicketFormScreen extends StatefulWidget {
|
class TicketFormScreen extends StatefulWidget {
|
||||||
@@ -32,7 +31,8 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
final _altPhoneCtrl = TextEditingController();
|
final _altPhoneCtrl = TextEditingController();
|
||||||
final _serialCtrl = TextEditingController();
|
final _targetSerialCtrl = TextEditingController();
|
||||||
|
final _sourceSerialCtrl = TextEditingController();
|
||||||
final _requestCtrl = TextEditingController();
|
final _requestCtrl = TextEditingController();
|
||||||
final _accessoriesCtrl = TextEditingController();
|
final _accessoriesCtrl = TextEditingController();
|
||||||
final _publicNotesCtrl = TextEditingController();
|
final _publicNotesCtrl = TextEditingController();
|
||||||
@@ -54,7 +54,8 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_altPhoneCtrl.dispose();
|
_altPhoneCtrl.dispose();
|
||||||
_serialCtrl.dispose();
|
_targetSerialCtrl.dispose();
|
||||||
|
_sourceSerialCtrl.dispose();
|
||||||
_requestCtrl.dispose();
|
_requestCtrl.dispose();
|
||||||
_accessoriesCtrl.dispose();
|
_accessoriesCtrl.dispose();
|
||||||
_publicNotesCtrl.dispose();
|
_publicNotesCtrl.dispose();
|
||||||
@@ -68,7 +69,12 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
if (_altPhoneCtrl.text.isEmpty) {
|
if (_altPhoneCtrl.text.isEmpty) {
|
||||||
_altPhoneCtrl.text = model.alternativePhoneNumber ?? '';
|
_altPhoneCtrl.text = model.alternativePhoneNumber ?? '';
|
||||||
}
|
}
|
||||||
if (_serialCtrl.text.isEmpty) _serialCtrl.text = model.targetSn ?? '';
|
if (_targetSerialCtrl.text.isEmpty) {
|
||||||
|
_targetSerialCtrl.text = model.targetSn ?? '';
|
||||||
|
}
|
||||||
|
if (_sourceSerialCtrl.text.isEmpty) {
|
||||||
|
_sourceSerialCtrl.text = model.sourceSn ?? '';
|
||||||
|
}
|
||||||
if (_requestCtrl.text.isEmpty) _requestCtrl.text = model.request;
|
if (_requestCtrl.text.isEmpty) _requestCtrl.text = model.request;
|
||||||
if (_accessoriesCtrl.text.isEmpty) {
|
if (_accessoriesCtrl.text.isEmpty) {
|
||||||
_accessoriesCtrl.text = model.includedAccessories ?? '';
|
_accessoriesCtrl.text = model.includedAccessories ?? '';
|
||||||
@@ -91,7 +97,8 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
void _flushControllersToCubit() {
|
void _flushControllersToCubit() {
|
||||||
context.read<TicketFormCubit>().updateFields(
|
context.read<TicketFormCubit>().updateFields(
|
||||||
alternativePhoneNumber: _altPhoneCtrl.text,
|
alternativePhoneNumber: _altPhoneCtrl.text,
|
||||||
targetSn: _serialCtrl.text,
|
targetSn: _targetSerialCtrl.text,
|
||||||
|
sourceSn: _sourceSerialCtrl.text,
|
||||||
request: _requestCtrl.text,
|
request: _requestCtrl.text,
|
||||||
includedAccessories: _accessoriesCtrl.text,
|
includedAccessories: _accessoriesCtrl.text,
|
||||||
publicNotes: _publicNotesCtrl.text,
|
publicNotes: _publicNotesCtrl.text,
|
||||||
@@ -231,7 +238,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
_ActionButton(
|
_ActionButton(
|
||||||
icon: Icons.email,
|
icon: Icons.email,
|
||||||
label: "Invia Email",
|
label: "Invia Email",
|
||||||
onTap: ticket.customerEmail != null ? () {} : null,
|
onTap: ticket.customer!.email.isNotEmpty ? () {} : null,
|
||||||
),
|
),
|
||||||
_ActionButton(
|
_ActionButton(
|
||||||
icon: Icons.close,
|
icon: Icons.close,
|
||||||
@@ -362,6 +369,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
child: const Text('Ricevuta'),
|
child: const Text('Ricevuta'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
@@ -401,17 +409,17 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
Expanded(child: Column(children: [_cardAnagrafica(ticket)])),
|
||||||
|
const SizedBox(width: 24),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [_cardAnagrafica(ticket), _cardDispositivo(ticket)],
|
children: [_cardDettagli(ticket), _cardCosti(ticket)],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 24),
|
const SizedBox(width: 24),
|
||||||
Expanded(child: Column(children: [_cardDettagli(ticket)])),
|
|
||||||
const SizedBox(width: 24),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [_cardCosti(ticket), _cardAssegnazione(ticket)],
|
children: [_cardDispositivi(ticket), _cardAssegnazione(ticket)],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -425,7 +433,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_cardAnagrafica(ticket),
|
_cardAnagrafica(ticket),
|
||||||
_cardDispositivo(ticket),
|
_cardDispositivi(ticket),
|
||||||
_cardAssegnazione(ticket),
|
_cardAssegnazione(ticket),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -444,7 +452,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
_cardAnagrafica(ticket),
|
_cardAnagrafica(ticket),
|
||||||
_cardDispositivo(ticket),
|
_cardDispositivi(ticket),
|
||||||
_cardDettagli(ticket),
|
_cardDettagli(ticket),
|
||||||
_cardCosti(ticket),
|
_cardCosti(ticket),
|
||||||
_cardAssegnazione(ticket),
|
_cardAssegnazione(ticket),
|
||||||
@@ -471,8 +479,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
),
|
),
|
||||||
const Divider(height: 32),
|
const Divider(height: 32),
|
||||||
SharedCustomerSection(
|
SharedCustomerSection(
|
||||||
customerId: ticket.customerId,
|
customer: ticket.customer,
|
||||||
customerName: ticket.customerName,
|
|
||||||
onCustomerSelected: (customer) =>
|
onCustomerSelected: (customer) =>
|
||||||
context.read<TicketFormCubit>().updateCustomer(customer),
|
context.read<TicketFormCubit>().updateCustomer(customer),
|
||||||
),
|
),
|
||||||
@@ -488,14 +495,19 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _cardDispositivo(TicketModel ticket) {
|
Widget _cardDispositivi(TicketModel ticket) {
|
||||||
|
final bool isDataTransfer = ticket.ticketType == TicketType.dataTransfer;
|
||||||
|
|
||||||
return _buildCard(
|
return _buildCard(
|
||||||
title: 'Dispositivo',
|
title: isDataTransfer ? 'Dispositivi' : 'Dispositivo',
|
||||||
icon: Icons.devices,
|
icon: Icons.devices,
|
||||||
themeColor: Colors.deepOrange,
|
themeColor: Colors.deepOrange,
|
||||||
children: [
|
children: [
|
||||||
|
// --- DISPOSITIVO TARGET (Nuovo/Ricevente) ---
|
||||||
SharedModelSection(
|
SharedModelSection(
|
||||||
label: 'Modello da Riparare',
|
label: isDataTransfer
|
||||||
|
? 'Dispositivo Target (Nuovo/Ricevente)'
|
||||||
|
: 'Modello da Riparare',
|
||||||
modelId: ticket.targetModelId,
|
modelId: ticket.targetModelId,
|
||||||
modelName: ticket.targetModelName,
|
modelName: ticket.targetModelName,
|
||||||
onModelSelected: (id, name) => context
|
onModelSelected: (id, name) => context
|
||||||
@@ -504,12 +516,108 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _serialCtrl,
|
controller: _targetSerialCtrl, // Controller per il seriale TARGET
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Seriale / IMEI',
|
labelText: 'Seriale / IMEI',
|
||||||
prefixIcon: Icon(Icons.qr_code),
|
prefixIcon: Icon(Icons.qr_code),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// --- DISPOSITIVO SORGENTE (Animato per Passaggio Dati) ---
|
||||||
|
AnimatedSize(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
child: isDataTransfer
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 24.0),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
// Bordo trasparente e delicato per definire la card
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.orange.shade300.withValues(alpha: 0.2),
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
// SFONDO RIMOSSO: vedi direttamente il tema scuro sotto!
|
||||||
|
// color: Colors.transparent, // opzionale
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.devices_fold,
|
||||||
|
color: Colors.orange.shade700,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
'Dispositivo Sorgente',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.orange.shade900,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// LA SHARED SECTION "SOFT"
|
||||||
|
SharedModelSection(
|
||||||
|
label: 'Modello Sorgente (Da cui copiare)',
|
||||||
|
modelId: ticket.sourceModelId,
|
||||||
|
modelName: ticket.sourceModelName,
|
||||||
|
// Sfondo quasi trasparente per non appesantire
|
||||||
|
backgroundColor: Colors.white.withValues(alpha: 0.1),
|
||||||
|
// Bordo delicato
|
||||||
|
borderColor: Colors.orange.shade300.withValues(
|
||||||
|
alpha: 0.2,
|
||||||
|
),
|
||||||
|
onModelSelected: (id, name) => context
|
||||||
|
.read<TicketFormCubit>()
|
||||||
|
.updateSourceModel(modelId: id, modelName: name),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: _sourceSerialCtrl,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Seriale / IMEI Sorgente',
|
||||||
|
prefixIcon: Icon(
|
||||||
|
Icons.qr_code,
|
||||||
|
color: Colors.orange.shade700.withValues(
|
||||||
|
alpha: 0.7,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Usiamo lo stesso riempimento tenue per coerenza
|
||||||
|
fillColor: Colors.white.withValues(alpha: 0.1),
|
||||||
|
filled: true,
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.orange.shade300.withValues(
|
||||||
|
alpha: 0.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.orange.shade500.withValues(
|
||||||
|
alpha: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -529,11 +637,16 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
labelText: 'Tipo Lavorazione',
|
labelText: 'Tipo Lavorazione',
|
||||||
),
|
),
|
||||||
items: TicketType.values
|
items: TicketType.values
|
||||||
.map((t) => DropdownMenuItem(value: t, child: Text(t.name)))
|
.map(
|
||||||
|
(t) => DropdownMenuItem(
|
||||||
|
value: t,
|
||||||
|
child: Text(t.displayValue),
|
||||||
|
),
|
||||||
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
onChanged: (val) => context
|
onChanged: (val) {
|
||||||
.read<TicketFormCubit>()
|
context.read<TicketFormCubit>().updateFields(ticketType: val);
|
||||||
.updateFields(ticketType: val),
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
@@ -657,7 +770,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
const Divider(height: 32),
|
const Divider(height: 32),
|
||||||
// ECCO LA MAGIA:
|
// ECCO LA MAGIA:
|
||||||
SharedFilesSection(
|
SharedFilesSection(
|
||||||
titleNameForUpload: ticket.customerName ?? 'Nuovo Ticket',
|
titleNameForUpload: ticket.customer?.name ?? 'Nuovo Ticket',
|
||||||
onGenerateIdForQr: _generateIdForQr,
|
onGenerateIdForQr: _generateIdForQr,
|
||||||
),
|
),
|
||||||
/* SharedAttachmentsSection(
|
/* SharedAttachmentsSection(
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ class _TicketCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
ticket.customerName ?? 'Cliente Sconosciuto',
|
ticket.customer?.name ?? 'Cliente Sconosciuto',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:pdf/pdf.dart';
|
import 'package:pdf/pdf.dart';
|
||||||
@@ -152,7 +150,7 @@ class TicketPdfService {
|
|||||||
pw.Expanded(
|
pw.Expanded(
|
||||||
child: _infoBlock(
|
child: _infoBlock(
|
||||||
"CLIENTE",
|
"CLIENTE",
|
||||||
ticket.customerName ?? 'Cliente Sconosciuto',
|
ticket.customer?.name ?? 'Cliente Sconosciuto',
|
||||||
font,
|
font,
|
||||||
boldFont,
|
boldFont,
|
||||||
),
|
),
|
||||||
@@ -317,7 +315,7 @@ class TicketPdfService {
|
|||||||
style: pw.TextStyle(font: boldFont, fontSize: 10),
|
style: pw.TextStyle(font: boldFont, fontSize: 10),
|
||||||
),
|
),
|
||||||
pw.Text(
|
pw.Text(
|
||||||
ticket.customerName ?? 'Cliente sconosciuto',
|
ticket.customer?.name ?? 'Cliente sconosciuto',
|
||||||
style: pw.TextStyle(font: font, fontSize: 9),
|
style: pw.TextStyle(font: font, fontSize: 9),
|
||||||
),
|
),
|
||||||
pw.Text(
|
pw.Text(
|
||||||
|
|||||||
10
pubspec.lock
10
pubspec.lock
@@ -309,6 +309,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
font_awesome_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: font_awesome_flutter
|
||||||
|
sha256: "09dcde8ab90ffae1a7d65ff2ef96fc62a17ad9d0ce7c127b317ded676b0d5935"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.0.0"
|
||||||
functions_client:
|
functions_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1043,7 +1051,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
|
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ dependencies:
|
|||||||
uuid: ^4.5.3
|
uuid: ^4.5.3
|
||||||
pdf: ^3.12.0
|
pdf: ^3.12.0
|
||||||
universal_io: ^2.3.1
|
universal_io: ^2.3.1
|
||||||
printing: ^5.13.1
|
url_launcher: ^6.3.2
|
||||||
|
printing: ^5.14.3
|
||||||
|
font_awesome_flutter: ^11.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user