ticket refinements

This commit is contained in:
2026-06-02 11:52:31 +02:00
parent 7fad6ee02b
commit a51ac8fe7f
10 changed files with 155 additions and 66 deletions

View File

@@ -367,4 +367,22 @@ class TicketFormCubit extends Cubit<TicketFormState> {
);
}
}
Future<void> deleteTicket() async {
final currentTicket = state.ticket;
if (currentTicket.id == null || currentTicket.id!.isEmpty) return;
try {
await _repository.deleteTicket(currentTicket.id!);
emit(state.copyWith(status: TicketFormStatus.deleted));
} catch (e) {
emit(
state.copyWith(
status: TicketFormStatus.failure,
errorMessage: 'Errore durante l\'eliminazione: $e',
),
);
}
}
}

View File

@@ -2,7 +2,16 @@ import 'package:equatable/equatable.dart';
import 'package:flux/features/tickets/models/ticket_model.dart';
// Adatta gli import al tuo progetto!
enum TicketFormStatus { initial, ready, loading, saving, success, pop, failure }
enum TicketFormStatus {
initial,
ready,
loading,
saving,
success,
pop,
failure,
deleted,
}
class TicketFormState extends Equatable {
final TicketModel ticket;

View File

@@ -158,4 +158,19 @@ class TicketListCubit extends Cubit<TicketListState> {
// Opzionale: Se vuoi comunque riallinearti al server in modo silenzioso dopo l'animazione
// loadTickets(refresh: true);
}
Future<void> deleteTickets(List<TicketModel> tickets) async {
try {
for (final ticket in tickets) {
await _repository.deleteTicket(ticket.id!);
}
// Rimuoviamo i ticket localmente senza ricaricare tutto
final remainingTickets = state.tickets
.where((t) => !tickets.any((toDelete) => toDelete.id == t.id))
.toList();
emit(state.copyWith(tickets: remainingTickets, selectedTickets: {}));
} catch (e) {
emit(state.copyWith(errorMessage: e.toString()));
}
}
}

View File

@@ -348,6 +348,32 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
trackingCubit.loadTrackings(ticketId, TrackingParentType.ticket);
}
void _deleteTicket(TicketModel ticket, {Color color = Colors.red}) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Conferma Cancellazione'),
content: Text(
'Sei sicuro di voler cancellare il ticket "${ticket.referenceId}"? Questa azione è irreversibile.',
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Annulla'),
),
ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: color),
onPressed: () {
context.read<TicketFormCubit>().deleteTicket();
Navigator.of(context).pop(); // Chiude il dialog
},
child: const Text('Cancella Ticket'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
@@ -359,6 +385,10 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
_syncTextControllers(state.ticket);
}
if (state.status == TicketFormStatus.deleted) {
Navigator.of(context).pop();
}
if (state.status == TicketFormStatus.success) {
context.read<TicketListCubit>().loadTickets(refresh: true);
_showSuccessActions(
@@ -388,66 +418,61 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
: 'Modifica Ticket - Operatore: ${state.ticket.createdByName}',
),
actions: [
BlocBuilder<TicketFormCubit, TicketFormState>(
builder: (context, state) {
final ticket = state.ticket;
// Se il ticket non è ancora salvato, niente azioni rapide
if (ticket.id == null || ticket.id!.isEmpty) {
return const SizedBox.shrink();
}
// CONDIZIONE A: Da iniziare
if (ticket.ticketStatus == TicketStatus.open ||
ticket.ticketStatus == TicketStatus.waitingForParts) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: FilledButton.icon(
style: FilledButton.styleFrom(
backgroundColor:
Colors.amber.shade700, // Colore Action
),
onPressed: () async {
StaffMemberModel? takenBy = await getStaffMember(
context,
);
if (takenBy == null || !context.mounted) return;
context.read<TicketFormCubit>().takeInCharge(
staffId: takenBy.id!,
staffName: takenBy.name,
);
_navigateToWorkspace(ticket.id!);
},
icon: const Icon(Icons.play_arrow, color: Colors.white),
label: const Text(
'Prendi in Carico',
style: TextStyle(color: Colors.white),
),
),
);
}
// CONDIZIONE B: Già in lavorazione
else if (ticket.ticketStatus == TicketStatus.inProgress) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: FilledButton.icon(
onPressed: () => _navigateToWorkspace(ticket.id!),
icon: const Icon(Icons.handyman),
label: const Text('Vai a Lavorazione'),
),
);
}
// Se è chiuso o in altri stati strani, nascondiamo il bottone
return const SizedBox.shrink();
},
),
if (ticket.id != null) ...[
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: FilledButton.icon(
onPressed: () => _deleteTicket(ticket, color: Colors.red),
icon: const Icon(Icons.delete),
label: const Text('Cancella Ticket'),
),
),
],
if (ticket.ticketStatus == TicketStatus.open ||
ticket.ticketStatus == TicketStatus.waitingForParts) ...[
// CONDIZIONE A: Da iniziare
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: FilledButton.icon(
style: FilledButton.styleFrom(
backgroundColor: Colors.amber.shade700, // Colore Action
),
onPressed: () async {
StaffMemberModel? takenBy = await getStaffMember(context);
if (takenBy == null || !context.mounted) return;
context.read<TicketFormCubit>().takeInCharge(
staffId: takenBy.id!,
staffName: takenBy.name,
);
_navigateToWorkspace(ticket.id!);
},
icon: const Icon(Icons.play_arrow, color: Colors.white),
label: const Text(
'Prendi in Carico',
style: TextStyle(color: Colors.white),
),
),
),
],
if (ticket.ticketStatus == TicketStatus.inProgress) ...[
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: FilledButton.icon(
onPressed: () => _navigateToWorkspace(ticket.id!),
icon: const Icon(Icons.handyman),
label: const Text('Vai a Lavorazione'),
),
),
],
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Chip(

View File

@@ -78,6 +78,12 @@ class TicketList extends StatelessWidget {
}
}
void _deleteTickets(BuildContext context) {
context.read<TicketListCubit>().deleteTickets(
state.selectedTickets.toList(),
);
}
@override
Widget build(BuildContext context) {
return Stack(
@@ -178,6 +184,11 @@ class TicketList extends StatelessWidget {
runSpacing: 8.0,
alignment: WrapAlignment.end,
children: [
IconButton.filled(
tooltip: 'Elimina',
onPressed: () => _deleteTickets(context),
icon: const Icon(Icons.delete),
),
IconButton.filled(
tooltip: 'Riconsegna',
onPressed: () => _setStatusClosed(context),