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

@@ -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/utils/ticket_pdf_service.dart';
import 'package:get_it/get_it.dart';
import 'package:pdf/pdf.dart';
import 'package:printing/printing.dart';
class TicketFormScreen extends StatefulWidget {
@@ -32,7 +31,8 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
final _formKey = GlobalKey<FormState>();
final _altPhoneCtrl = TextEditingController();
final _serialCtrl = TextEditingController();
final _targetSerialCtrl = TextEditingController();
final _sourceSerialCtrl = TextEditingController();
final _requestCtrl = TextEditingController();
final _accessoriesCtrl = TextEditingController();
final _publicNotesCtrl = TextEditingController();
@@ -54,7 +54,8 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
@override
void dispose() {
_altPhoneCtrl.dispose();
_serialCtrl.dispose();
_targetSerialCtrl.dispose();
_sourceSerialCtrl.dispose();
_requestCtrl.dispose();
_accessoriesCtrl.dispose();
_publicNotesCtrl.dispose();
@@ -68,7 +69,12 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
if (_altPhoneCtrl.text.isEmpty) {
_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 (_accessoriesCtrl.text.isEmpty) {
_accessoriesCtrl.text = model.includedAccessories ?? '';
@@ -91,7 +97,8 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
void _flushControllersToCubit() {
context.read<TicketFormCubit>().updateFields(
alternativePhoneNumber: _altPhoneCtrl.text,
targetSn: _serialCtrl.text,
targetSn: _targetSerialCtrl.text,
sourceSn: _sourceSerialCtrl.text,
request: _requestCtrl.text,
includedAccessories: _accessoriesCtrl.text,
publicNotes: _publicNotesCtrl.text,
@@ -231,7 +238,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
_ActionButton(
icon: Icons.email,
label: "Invia Email",
onTap: ticket.customerEmail != null ? () {} : null,
onTap: ticket.customer!.email.isNotEmpty ? () {} : null,
),
_ActionButton(
icon: Icons.close,
@@ -362,6 +369,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
child: const Text('Ricevuta'),
),
),
const SizedBox(width: 12),
Expanded(
flex: 1,
child: ElevatedButton(
@@ -401,17 +409,17 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: Column(children: [_cardAnagrafica(ticket)])),
const SizedBox(width: 24),
Expanded(
child: Column(
children: [_cardAnagrafica(ticket), _cardDispositivo(ticket)],
children: [_cardDettagli(ticket), _cardCosti(ticket)],
),
),
const SizedBox(width: 24),
Expanded(child: Column(children: [_cardDettagli(ticket)])),
const SizedBox(width: 24),
Expanded(
child: Column(
children: [_cardCosti(ticket), _cardAssegnazione(ticket)],
children: [_cardDispositivi(ticket), _cardAssegnazione(ticket)],
),
),
],
@@ -425,7 +433,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
child: Column(
children: [
_cardAnagrafica(ticket),
_cardDispositivo(ticket),
_cardDispositivi(ticket),
_cardAssegnazione(ticket),
],
),
@@ -444,7 +452,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_cardAnagrafica(ticket),
_cardDispositivo(ticket),
_cardDispositivi(ticket),
_cardDettagli(ticket),
_cardCosti(ticket),
_cardAssegnazione(ticket),
@@ -471,8 +479,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
),
const Divider(height: 32),
SharedCustomerSection(
customerId: ticket.customerId,
customerName: ticket.customerName,
customer: ticket.customer,
onCustomerSelected: (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(
title: 'Dispositivo',
title: isDataTransfer ? 'Dispositivi' : 'Dispositivo',
icon: Icons.devices,
themeColor: Colors.deepOrange,
children: [
// --- DISPOSITIVO TARGET (Nuovo/Ricevente) ---
SharedModelSection(
label: 'Modello da Riparare',
label: isDataTransfer
? 'Dispositivo Target (Nuovo/Ricevente)'
: 'Modello da Riparare',
modelId: ticket.targetModelId,
modelName: ticket.targetModelName,
onModelSelected: (id, name) => context
@@ -504,12 +516,108 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
),
const SizedBox(height: 16),
TextFormField(
controller: _serialCtrl,
controller: _targetSerialCtrl, // Controller per il seriale TARGET
decoration: const InputDecoration(
labelText: 'Seriale / IMEI',
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',
),
items: TicketType.values
.map((t) => DropdownMenuItem(value: t, child: Text(t.name)))
.map(
(t) => DropdownMenuItem(
value: t,
child: Text(t.displayValue),
),
)
.toList(),
onChanged: (val) => context
.read<TicketFormCubit>()
.updateFields(ticketType: val),
onChanged: (val) {
context.read<TicketFormCubit>().updateFields(ticketType: val);
},
),
),
const SizedBox(width: 16),
@@ -657,7 +770,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
const Divider(height: 32),
// ECCO LA MAGIA:
SharedFilesSection(
titleNameForUpload: ticket.customerName ?? 'Nuovo Ticket',
titleNameForUpload: ticket.customer?.name ?? 'Nuovo Ticket',
onGenerateIdForQr: _generateIdForQr,
),
/* SharedAttachmentsSection(