sistemati ticket

This commit is contained in:
2026-05-12 11:14:48 +02:00
parent 57061da20d
commit 2aab70aec5
14 changed files with 367 additions and 95 deletions

View File

@@ -1,24 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.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/models/customer_model.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 {
final String? customerId;
final String? customerName;
final CustomerModel? customer;
final ValueChanged<CustomerModel> onCustomerSelected;
const SharedCustomerSection({
super.key,
this.customerId,
this.customerName,
this.customer,
required this.onCustomerSelected,
});
@override
Widget build(BuildContext context) {
final hasCustomer = customerId != null && customerId!.isNotEmpty;
final hasCustomer = customer != null && customer!.id!.isNotEmpty;
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -47,7 +50,7 @@ class SharedCustomerSection extends StatelessWidget {
const SizedBox(width: 12),
Expanded(
child: Text(
hasCustomer ? customerName! : 'Seleziona Cliente *',
hasCustomer ? customer!.name : 'Seleziona Cliente *',
style: TextStyle(
fontWeight: hasCustomer
? FontWeight.bold
@@ -57,10 +60,145 @@ class SharedCustomerSection extends StatelessWidget {
),
),
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',
),
],
),
],
),
),
],
],
);
}

View File

@@ -7,6 +7,8 @@ class SharedModelSection extends StatelessWidget {
final String? modelId;
final String? modelName;
final String label;
final Color? backgroundColor;
final Color? borderColor;
// Usiamo una callback che passa direttamente ID e Nome
// 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.onModelSelected,
this.label = 'Seleziona Modello',
this.backgroundColor,
this.borderColor,
});
@override
@@ -26,6 +30,7 @@ class SharedModelSection extends StatelessWidget {
final hasModel = modelId != null && modelId!.isNotEmpty;
return ListTile(
tileColor: backgroundColor,
title: Text(label),
subtitle: Text(
hasModel ? modelName! : 'Nessun modello selezionato',
@@ -36,7 +41,7 @@ class SharedModelSection extends StatelessWidget {
),
trailing: const Icon(Icons.arrow_drop_down),
shape: RoundedRectangleBorder(
side: BorderSide(color: theme.dividerColor),
side: BorderSide(color: borderColor ?? theme.dividerColor),
borderRadius: BorderRadius.circular(8),
),
onTap: () => _showModelModal(context),