ticket form funzionante! devo ancora provare a salvare però
This commit is contained in:
@@ -6,7 +6,7 @@ import 'package:flux/core/data/core_repository.dart';
|
|||||||
import 'package:flux/core/layout/app_shell.dart';
|
import 'package:flux/core/layout/app_shell.dart';
|
||||||
import 'package:flux/core/utils/extensions.dart';
|
import 'package:flux/core/utils/extensions.dart';
|
||||||
import 'package:flux/core/widgets/set_password_screen.dart';
|
import 'package:flux/core/widgets/set_password_screen.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/shared_mobile_upload_screen.dart';
|
import 'package:flux/core/widgets/shared_forms/mobile_upload_screen.dart';
|
||||||
import 'package:flux/features/auth/ui/auth_screen.dart';
|
import 'package:flux/features/auth/ui/auth_screen.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';
|
||||||
@@ -27,7 +27,10 @@ import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
|||||||
import 'package:flux/features/operations/models/operation_model.dart';
|
import 'package:flux/features/operations/models/operation_model.dart';
|
||||||
import 'package:flux/features/operations/ui/operation_form_screen.dart';
|
import 'package:flux/features/operations/ui/operation_form_screen.dart';
|
||||||
import 'package:flux/features/operations/ui/operations_screen.dart';
|
import 'package:flux/features/operations/ui/operations_screen.dart';
|
||||||
|
import 'package:flux/features/tickets/blocs/ticket_form_cubit.dart';
|
||||||
import 'package:flux/features/tickets/blocs/ticket_list_cubit.dart';
|
import 'package:flux/features/tickets/blocs/ticket_list_cubit.dart';
|
||||||
|
import 'package:flux/features/tickets/models/ticket_model.dart';
|
||||||
|
import 'package:flux/features/tickets/ui/ticket_form_screen.dart';
|
||||||
import 'package:flux/features/tickets/ui/ticket_list_screen.dart';
|
import 'package:flux/features/tickets/ui/ticket_list_screen.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@@ -157,6 +160,32 @@ class AppRouter {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// --- DETTAGLI E OPERATIVITÀ (FUORI DALLA SHELL - TUTTO SCHERMO) ---
|
// --- DETTAGLI E OPERATIVITÀ (FUORI DALLA SHELL - TUTTO SCHERMO) ---
|
||||||
|
GoRoute(
|
||||||
|
// Il path sarà es. /tickets/form/123 oppure /tickets/form/new
|
||||||
|
path: '/tickets/form/:id',
|
||||||
|
builder: (context, state) {
|
||||||
|
// 1. Leggiamo l'ID dall'URL
|
||||||
|
final String pathId = state.pathParameters['id'] ?? 'new';
|
||||||
|
|
||||||
|
// 2. Leggiamo l'oggetto dalla RAM (se arriviamo da un tap interno all'app)
|
||||||
|
final TicketModel? ticketFromExtra = state.extra as TicketModel?;
|
||||||
|
|
||||||
|
// 3. Capiamo se è un nuovo ticket o una modifica
|
||||||
|
final String? realTicketId = pathId == 'new' ? null : pathId;
|
||||||
|
context.read<StaffCubit>().loadStaffForStore(
|
||||||
|
GetIt.I.get<SessionCubit>().state.currentStore!.id!,
|
||||||
|
);
|
||||||
|
context.read<CustomersCubit>().loadCustomers();
|
||||||
|
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => TicketFormCubit(),
|
||||||
|
child: TicketFormScreen(
|
||||||
|
ticketId: realTicketId,
|
||||||
|
existingTicket: ticketFromExtra,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/customer/:id',
|
path: '/customer/:id',
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class StaffSection extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
// Se staffId è nullo, proviamo a preselezionare l'utente loggato
|
||||||
final selectedStaffId =
|
final selectedStaffId =
|
||||||
staffId ?? GetIt.I.get<SessionCubit>().state.currentStaffMember?.id;
|
staffId ?? GetIt.I.get<SessionCubit>().state.currentStaffMember?.id;
|
||||||
|
|
||||||
@@ -31,7 +32,8 @@ class StaffSection extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 12.0),
|
padding: const EdgeInsets.only(bottom: 12.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Operatore',
|
label ??
|
||||||
|
'Operatore', // <-- FIX: Ora usa l'etichetta passata dal form!
|
||||||
style: theme.textTheme.titleLarge?.copyWith(
|
style: theme.textTheme.titleLarge?.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
@@ -39,8 +41,28 @@ class StaffSection extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
BlocBuilder<StaffCubit, StaffState>(
|
BlocBuilder<StaffCubit, StaffState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
// Dati finti per farti vedere la UI, piallali quando attacchi il BlocBuilder!
|
// FIX: Aggiunto un controllo se sta caricando
|
||||||
|
if (state.status == StaffStatus.loading) {
|
||||||
|
return const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final staffMembers = state.storeStaff;
|
final staffMembers = state.storeStaff;
|
||||||
|
|
||||||
|
// FIX: Feedback visivo se la lista è vuota
|
||||||
|
if (staffMembers.isEmpty) {
|
||||||
|
return const Text(
|
||||||
|
'Nessun operatore caricato. Controlla il Cubit!',
|
||||||
|
style: TextStyle(color: Colors.red),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final currentLoggedStaffMember = GetIt.I
|
final currentLoggedStaffMember = GetIt.I
|
||||||
.get<SessionCubit>()
|
.get<SessionCubit>()
|
||||||
.state
|
.state
|
||||||
@@ -55,11 +77,6 @@ class StaffSection extends StatelessWidget {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
onStaffSelected(staff);
|
onStaffSelected(staff);
|
||||||
|
|
||||||
/* context.read<OperationsCubit>().updateOperationFields(
|
|
||||||
staffId: staff.id,
|
|
||||||
staffDisplayName: staff.name,
|
|
||||||
); */
|
|
||||||
},
|
},
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
@@ -196,7 +196,7 @@ class HomeScreen extends StatelessWidget {
|
|||||||
color: Colors.redAccent,
|
color: Colors.redAccent,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// Andiamo alla lista! (Da lì poi aggiungeremo il tasto "+" per il form)
|
// Andiamo alla lista! (Da lì poi aggiungeremo il tasto "+" per il form)
|
||||||
context.push('/tickets');
|
context.push('/tickets/form/new');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import 'package:flux/core/blocs/session/session_cubit.dart';
|
|||||||
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
||||||
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
||||||
import 'package:flux/features/operations/models/operation_model.dart';
|
import 'package:flux/features/operations/models/operation_model.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/shared_customer_section.dart';
|
import 'package:flux/core/widgets/shared_forms/customer_section.dart';
|
||||||
import 'package:flux/features/operations/ui/widgets/details_section.dart';
|
import 'package:flux/features/operations/ui/widgets/details_section.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/shared_attachments_section.dart';
|
import 'package:flux/core/widgets/shared_forms/attachments_section.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/shared_staff_section.dart';
|
import 'package:flux/core/widgets/shared_forms/staff_section.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
|
|
||||||
class OperationFormScreen extends StatefulWidget {
|
class OperationFormScreen extends StatefulWidget {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/shared_model_section.dart';
|
import 'package:flux/core/widgets/shared_forms/model_section.dart';
|
||||||
import 'package:flux/features/master_data/providers/blocs/provider_cubit.dart';
|
import 'package:flux/features/master_data/providers/blocs/provider_cubit.dart';
|
||||||
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
||||||
import 'package:flux/features/operations/models/operation_model.dart';
|
import 'package:flux/features/operations/models/operation_model.dart';
|
||||||
|
|||||||
@@ -17,12 +17,36 @@ class TicketFormCubit extends Cubit<TicketFormState> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// 1. INIZIALIZZAZIONE (Se stiamo modificando un ticket esistente)
|
/// 1. INIZIALIZZAZIONE (Se stiamo modificando un ticket esistente)
|
||||||
void initForm(TicketModel? existingTicket) {
|
Future<void> initForm({String? id, TicketModel? existingTicket}) async {
|
||||||
if (existingTicket != null) {
|
if (existingTicket != null) {
|
||||||
|
// SCENARIO 1 (App Native / Navigazione interna Web):
|
||||||
|
// Abbiamo l'oggetto intero passato via 'extra'. Lo mostriamo all'istante!
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(ticket: existingTicket, status: TicketFormStatus.ready),
|
state.copyWith(ticket: existingTicket, status: TicketFormStatus.ready),
|
||||||
);
|
);
|
||||||
|
} else if (id != null) {
|
||||||
|
// SCENARIO 2 (Web Refresh o Link condiviso):
|
||||||
|
// L'utente ha premuto F5 su /tickets/form/123. L'extra è andato perso, ma abbiamo l'ID!
|
||||||
|
emit(
|
||||||
|
state.copyWith(status: TicketFormStatus.loading),
|
||||||
|
); // Mostriamo uno spinner
|
||||||
|
try {
|
||||||
|
final fetchedTicket = await _repository.getTicketById(
|
||||||
|
id,
|
||||||
|
); // Lo scarichiamo!
|
||||||
|
emit(
|
||||||
|
state.copyWith(ticket: fetchedTicket, status: TicketFormStatus.ready),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
status: TicketFormStatus.failure,
|
||||||
|
errorMessage: 'Ticket non trovato',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// SCENARIO 3 (Nuovo Ticket):
|
||||||
// È un nuovo ticket! Inseriamo i default base (Azienda, Negozio, Creatore)
|
// È un nuovo ticket! Inseriamo i default base (Azienda, Negozio, Creatore)
|
||||||
final currentUser = _sessionCubit.state.currentStaffMember;
|
final currentUser = _sessionCubit.state.currentStaffMember;
|
||||||
final currentStore = _sessionCubit.state.currentStore;
|
final currentStore = _sessionCubit.state.currentStore;
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ class TicketRepository {
|
|||||||
|
|
||||||
/// Recupera un ticket specifico CON TUTTE LE RELAZIONI espanse (Cliente e Modelli)
|
/// Recupera un ticket specifico CON TUTTE LE RELAZIONI espanse (Cliente e Modelli)
|
||||||
/// Questa è la vera magia di Supabase!
|
/// Questa è la vera magia di Supabase!
|
||||||
Future<TicketModel> getTicketWithDetails(String ticketId) async {
|
Future<TicketModel> getTicketById(String ticketId) async {
|
||||||
try {
|
try {
|
||||||
// Usiamo i nomi esatti delle Foreign Key che hai definito nell'SQL!
|
// Usiamo i nomi esatti delle Foreign Key che hai definito nell'SQL!
|
||||||
final response = await _supabase
|
final response = await _supabase
|
||||||
|
|||||||
@@ -3,15 +3,16 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:flux/features/tickets/blocs/ticket_form_cubit.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/blocs/ticket_form_state.dart';
|
||||||
import 'package:flux/features/tickets/models/ticket_model.dart';
|
import 'package:flux/features/tickets/models/ticket_model.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/shared_customer_section.dart';
|
import 'package:flux/core/widgets/shared_forms/customer_section.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/shared_model_section.dart';
|
import 'package:flux/core/widgets/shared_forms/model_section.dart';
|
||||||
import 'package:flux/core/widgets/shared_forms/shared_staff_section.dart';
|
import 'package:flux/core/widgets/shared_forms/staff_section.dart';
|
||||||
import 'package:flux/features/tickets/models/ticket_status_extension.dart'; // Il tuo widget agnostico dello staff
|
import 'package:flux/features/tickets/models/ticket_status_extension.dart'; // Il tuo widget agnostico dello staff
|
||||||
|
|
||||||
class TicketFormScreen extends StatefulWidget {
|
class TicketFormScreen extends StatefulWidget {
|
||||||
final TicketModel? existingTicket;
|
final TicketModel? existingTicket;
|
||||||
|
final String? ticketId;
|
||||||
|
|
||||||
const TicketFormScreen({super.key, this.existingTicket});
|
const TicketFormScreen({super.key, this.existingTicket, this.ticketId});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TicketFormScreen> createState() => _TicketFormScreenState();
|
State<TicketFormScreen> createState() => _TicketFormScreenState();
|
||||||
@@ -36,7 +37,10 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// Inizializziamo il Cubit
|
// Inizializziamo il Cubit
|
||||||
context.read<TicketFormCubit>().initForm(widget.existingTicket);
|
context.read<TicketFormCubit>().initForm(
|
||||||
|
id: widget.ticketId,
|
||||||
|
existingTicket: widget.existingTicket,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -150,7 +154,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
padding: const EdgeInsets.only(right: 16.0),
|
padding: const EdgeInsets.only(right: 16.0),
|
||||||
child: Chip(
|
child: Chip(
|
||||||
label: Text(
|
label: Text(
|
||||||
ticket.ticketStatus!.name.toUpperCase(),
|
ticket.ticketStatus.name.toUpperCase(),
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 10),
|
style: const TextStyle(color: Colors.white, fontSize: 10),
|
||||||
),
|
),
|
||||||
backgroundColor: ticket.ticketStatus.color,
|
backgroundColor: ticket.ticketStatus.color,
|
||||||
@@ -239,7 +243,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DropdownButtonFormField<TicketType>(
|
child: DropdownButtonFormField<TicketType>(
|
||||||
value: ticket.ticketType,
|
initialValue: ticket.ticketType,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Tipo Lavorazione',
|
labelText: 'Tipo Lavorazione',
|
||||||
),
|
),
|
||||||
@@ -261,7 +265,7 @@ class _TicketFormScreenState extends State<TicketFormScreen> {
|
|||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DropdownButtonFormField<TicketStatus>(
|
child: DropdownButtonFormField<TicketStatus>(
|
||||||
value: ticket.ticketStatus,
|
initialValue: ticket.ticketStatus,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Stato Attuale',
|
labelText: 'Stato Attuale',
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user