From b91442872d8da294b55e15fe87e10a5dc84179e8 Mon Sep 17 00:00:00 2001 From: mark-cachy Date: Sun, 10 May 2026 15:36:26 +0200 Subject: [PATCH] a --- lib/features/tickets/models/ticket_model.dart | 5 + .../tickets/ui/ticket_form_screen.dart | 159 +++++++++++++++++- 2 files changed, 163 insertions(+), 1 deletion(-) diff --git a/lib/features/tickets/models/ticket_model.dart b/lib/features/tickets/models/ticket_model.dart index 3f307c8..08a5a78 100644 --- a/lib/features/tickets/models/ticket_model.dart +++ b/lib/features/tickets/models/ticket_model.dart @@ -107,6 +107,7 @@ class TicketModel extends Equatable { final TicketResult? ticketResult; final String? resolutionNotes; final String? customerName; + final String? customerEmail; final String? targetModelName; final String? sourceModelName; final String? createdById; @@ -142,6 +143,7 @@ class TicketModel extends Equatable { this.ticketResult, this.resolutionNotes, this.customerName, + this.customerEmail, this.targetModelName, this.sourceModelName, this.createdById, @@ -192,6 +194,7 @@ class TicketModel extends Equatable { TicketResult? ticketResult, String? resolutionNotes, String? customerName, + String? customerEmail, String? targetModelName, String? sourceModelName, String? createdById, @@ -228,6 +231,7 @@ class TicketModel extends Equatable { ticketResult: ticketResult ?? this.ticketResult, resolutionNotes: resolutionNotes ?? this.resolutionNotes, customerName: customerName ?? this.customerName, + customerEmail: customerEmail ?? this.customerEmail, targetModelName: targetModelName ?? this.targetModelName, sourceModelName: sourceModelName ?? this.sourceModelName, createdById: createdById ?? this.createdById, @@ -276,6 +280,7 @@ class TicketModel extends Equatable { ticketResult: TicketResult.fromString(map['ticket_result'] as String?), resolutionNotes: map['resolution_notes'] as String?, customerName: (map['customer']?['name'] as String?).myFormat(), + customerEmail: (map['customer']?['email'] as String?).myFormat(), targetModelName: (map['target_model']?['name_with_brand'] as String?) ?.myFormat(), sourceModelName: (map['source_model']?['name_with_brand'] as String?) diff --git a/lib/features/tickets/ui/ticket_form_screen.dart b/lib/features/tickets/ui/ticket_form_screen.dart index 75f34e3..dc1dba1 100644 --- a/lib/features/tickets/ui/ticket_form_screen.dart +++ b/lib/features/tickets/ui/ticket_form_screen.dart @@ -1,14 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/core/blocs/session/session_cubit.dart'; import 'package:flux/core/widgets/shared_forms/customer_section.dart'; import 'package:flux/core/widgets/shared_forms/model_section.dart'; import 'package:flux/core/widgets/shared_forms/shared_files_section.dart'; import 'package:flux/features/attachments/blocs/attachments_bloc.dart'; +import 'package:flux/features/company/models/company_model.dart'; import 'package:flux/features/tickets/blocs/ticket_form_cubit.dart'; import 'package:flux/features/tickets/blocs/ticket_form_state.dart'; import 'package:flux/features/tickets/models/ticket_model.dart'; 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/utils/ticket_pdf_service.dart'; +import 'package:get_it/get_it.dart'; +import 'package:printing/printing.dart'; class TicketFormScreen extends StatefulWidget { final TicketModel? existingTicket; @@ -121,6 +126,102 @@ class _TicketFormScreenState extends State { return newId; } + void _showSuccessActions( + BuildContext context, + TicketModel ticket, + CompanyModel company, + ) { + showModalBottomSheet( + context: context, + isDismissible: false, // Costringiamo l'operatore a una scelta conscia + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(24)), + ), + builder: (context) => Container( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + ), + const SizedBox(height: 24), + const Icon(Icons.check_circle, color: Colors.green, size: 64), + const SizedBox(height: 16), + Text( + "Ticket Salvato!", + style: Theme.of( + context, + ).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold), + ), + Text( + "Rif: ${ticket.referenceId}", + style: TextStyle(color: Colors.grey[600], fontSize: 18), + ), + const SizedBox(height: 32), + + // Griglia delle Azioni + GridView.count( + crossAxisCount: 2, + shrinkWrap: true, + mainAxisSpacing: 16, + crossAxisSpacing: 16, + childAspectRatio: 1.5, + children: [ + _ActionButton( + icon: Icons.print, + label: "Ricevuta A4", + onTap: () async { + final pdf = await TicketPdfService().generateTicketReceipt( + ticket, + company, + ); + await Printing.layoutPdf(onLayout: (format) => pdf); + }, + ), + if (company.labelFormat != LabelFormat.none) + _ActionButton( + icon: Icons.label, + label: "Etichetta", + onTap: () async { + final pdf = await TicketPdfService().generateLabelPdf( + ticket, + company, + ); + await Printing.layoutPdf(onLayout: (format) => pdf); + }, + ), + _ActionButton( + icon: Icons.email, + label: "Invia Email", + onTap: ticket.customerEmail != null + ? () => _sendEmail(ticket) + : null, + ), + _ActionButton( + icon: Icons.close, + label: "Chiudi", + color: Colors.blue[900], + textColor: Colors.white, + onTap: () => Navigator.of(context).pop(), // Torna alla lista + ), + ], + ), + ], + ), + ), + ); + } + + Future _sendEmail(TicketModel ticket) async { + //TODO send ticket receipt via email + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -133,7 +234,11 @@ class _TicketFormScreenState extends State { } if (state.status == TicketFormStatus.success) { - Navigator.of(context).pop(); + _showSuccessActions( + context, + state.ticket, + GetIt.I.get().state.company!, + ); } else if (state.status == TicketFormStatus.successAndAddAnother) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -597,3 +702,55 @@ class _TicketFormScreenState extends State { ); } } + +// Widget helper per i bottoni dell'Action Hub +class _ActionButton extends StatelessWidget { + final IconData icon; + final String label; + final VoidCallback? onTap; + final Color? color; + final Color? textColor; + + const _ActionButton({ + required this.icon, + required this.label, + this.onTap, + this.color, + this.textColor, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(16), + child: Container( + decoration: BoxDecoration( + color: onTap == null ? Colors.grey[100] : (color ?? Colors.grey[200]), + borderRadius: BorderRadius.circular(16), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + icon, + color: onTap == null + ? Colors.grey + : (textColor ?? Colors.blue[900]), + ), + const SizedBox(height: 8), + Text( + label, + style: TextStyle( + fontWeight: FontWeight.bold, + color: onTap == null + ? Colors.grey + : (textColor ?? Colors.blue[900]), + ), + ), + ], + ), + ), + ); + } +}