2026-04-22 11:06:02 +02:00
|
|
|
import 'dart:async';
|
2026-04-09 18:17:48 +02:00
|
|
|
import 'package:flutter/material.dart';
|
2026-04-22 11:06:02 +02:00
|
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
|
import 'package:flux/core/blocs/session/session_cubit.dart';
|
|
|
|
|
import 'package:flux/core/data/core_repository.dart';
|
2026-04-29 11:40:17 +02:00
|
|
|
import 'package:flux/core/layout/app_shell.dart';
|
2026-05-09 20:42:42 +02:00
|
|
|
import 'package:flux/core/routes/routes.dart';
|
2026-05-09 17:30:51 +02:00
|
|
|
import 'package:flux/core/widgets/image_upload/blocs/image_upload_cubit.dart';
|
2026-05-09 16:00:06 +02:00
|
|
|
import 'package:flux/core/widgets/image_upload/ui/image_upload_screen.dart';
|
2026-04-28 10:12:15 +02:00
|
|
|
import 'package:flux/core/widgets/set_password_screen.dart';
|
2026-05-09 16:00:06 +02:00
|
|
|
import 'package:flux/core/widgets/image_upload/ui/upload_success_screen.dart';
|
2026-04-09 18:17:48 +02:00
|
|
|
import 'package:flux/features/auth/ui/auth_screen.dart';
|
2026-05-08 18:51:28 +02:00
|
|
|
import 'package:flux/features/company/bloc/company_settings_cubit.dart';
|
2026-05-08 12:28:14 +02:00
|
|
|
import 'package:flux/features/company/ui/company_settings_screen.dart';
|
2026-05-19 10:32:01 +02:00
|
|
|
import 'package:flux/features/customers/blocs/customer_form_cubit.dart';
|
|
|
|
|
import 'package:flux/features/customers/blocs/customers_list_cubit.dart';
|
2026-04-11 12:40:03 +02:00
|
|
|
import 'package:flux/features/customers/models/customer_model.dart';
|
|
|
|
|
import 'package:flux/features/customers/ui/customer_detail_screen.dart';
|
2026-05-19 10:32:01 +02:00
|
|
|
import 'package:flux/features/customers/ui/customer_form_screen.dart';
|
2026-05-18 19:20:38 +02:00
|
|
|
import 'package:flux/features/customers/ui/customers_list_screen.dart';
|
2026-04-13 10:00:07 +02:00
|
|
|
import 'package:flux/features/home/ui/home_screen.dart';
|
2026-04-29 11:40:17 +02:00
|
|
|
import 'package:flux/features/master_data/master_data_hub_content.dart';
|
2026-05-04 15:36:42 +02:00
|
|
|
import 'package:flux/features/master_data/products/blocs/product_cubit.dart';
|
2026-04-14 11:29:49 +02:00
|
|
|
import 'package:flux/features/master_data/products/ui/products_screen.dart';
|
2026-05-15 10:12:05 +02:00
|
|
|
import 'package:flux/features/master_data/providers/blocs/provider_form_cubit.dart';
|
|
|
|
|
import 'package:flux/features/master_data/providers/blocs/provider_list_cubit.dart';
|
|
|
|
|
import 'package:flux/features/master_data/providers/models/provider_model.dart';
|
|
|
|
|
import 'package:flux/features/master_data/providers/ui/provider_form_screen.dart';
|
|
|
|
|
import 'package:flux/features/master_data/providers/ui/provider_list_screen.dart';
|
2026-05-13 12:41:07 +02:00
|
|
|
import 'package:flux/features/master_data/staff/models/staff_member_model.dart';
|
2026-05-04 15:36:42 +02:00
|
|
|
import 'package:flux/features/master_data/staff/ui/staff_screen.dart';
|
|
|
|
|
import 'package:flux/features/master_data/store/ui/stores_screen.dart';
|
2026-05-21 14:43:47 +02:00
|
|
|
import 'package:flux/features/notes/models/note_model.dart';
|
|
|
|
|
import 'package:flux/features/notes/ui/notes_form_screen.dart';
|
|
|
|
|
import 'package:flux/features/notes/ui/notes_list_screen.dart';
|
2026-04-22 11:06:02 +02:00
|
|
|
import 'package:flux/features/onboarding/blocs/onboarding_cubit.dart';
|
|
|
|
|
import 'package:flux/features/onboarding/ui/onboarding_screen.dart';
|
2026-05-07 16:28:01 +02:00
|
|
|
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
2026-05-08 12:28:14 +02:00
|
|
|
import 'package:flux/features/operations/blocs/operation_form_cubit.dart';
|
2026-05-04 15:36:42 +02:00
|
|
|
import 'package:flux/features/operations/models/operation_model.dart';
|
|
|
|
|
import 'package:flux/features/operations/ui/operation_form_screen.dart';
|
2026-05-08 12:28:14 +02:00
|
|
|
import 'package:flux/features/operations/ui/operation_list_screen.dart';
|
2026-05-13 12:41:07 +02:00
|
|
|
import 'package:flux/features/settings/settings_screen.dart';
|
2026-05-08 18:51:28 +02:00
|
|
|
import 'package:flux/features/settings/theme_settings_view.dart';
|
2026-05-07 16:28:01 +02:00
|
|
|
import 'package:flux/features/tickets/blocs/ticket_form_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';
|
2026-05-14 15:59:46 +02:00
|
|
|
import 'package:flux/features/tickets/ui/ticket_workspace/ticket_workspace_screen.dart';
|
|
|
|
|
import 'package:flux/features/tracking/blocs/tracking_cubit.dart';
|
|
|
|
|
import 'package:flux/features/tracking/models/tracking_model.dart';
|
2026-04-22 11:06:02 +02:00
|
|
|
import 'package:get_it/get_it.dart';
|
2026-04-09 18:17:48 +02:00
|
|
|
import 'package:go_router/go_router.dart';
|
|
|
|
|
|
|
|
|
|
class AppRouter {
|
2026-04-22 11:06:02 +02:00
|
|
|
static GoRouter createRouter(SessionCubit sessionCubit) {
|
2026-04-09 18:17:48 +02:00
|
|
|
return GoRouter(
|
|
|
|
|
initialLocation: '/',
|
2026-04-22 11:06:02 +02:00
|
|
|
refreshListenable: GoRouterRefreshStream(sessionCubit.stream),
|
|
|
|
|
redirect: (context, state) {
|
|
|
|
|
final sessionState = sessionCubit.state;
|
|
|
|
|
final isGoingToLogin = state.matchedLocation == '/login';
|
|
|
|
|
final isGoingToOnboarding = state.matchedLocation == '/onboarding';
|
2026-04-28 10:12:15 +02:00
|
|
|
final isGoingToSetPassword = state.matchedLocation == '/set-password';
|
2026-05-07 18:37:25 +02:00
|
|
|
|
|
|
|
|
// 1. LA PASSATOIA VIP (DEVE ESSERE IN CIMA)
|
|
|
|
|
// Usiamo state.uri.path perché state.matchedLocation a volte fa i capricci coi deep link iniziali
|
2026-05-07 18:08:45 +02:00
|
|
|
final isPublicRoute = state.uri.path.startsWith('/upload');
|
|
|
|
|
|
|
|
|
|
if (isPublicRoute) {
|
2026-05-07 18:37:25 +02:00
|
|
|
// Ritorna null esplicitamente per dire al router "Rimani qui e non fare altri controlli"
|
|
|
|
|
return null;
|
2026-05-07 18:08:45 +02:00
|
|
|
}
|
2026-04-09 18:17:48 +02:00
|
|
|
|
2026-05-07 18:37:25 +02:00
|
|
|
// 2. CONTROLLO INIZIALE
|
|
|
|
|
// Se la sessione sta ancora caricando la primissima volta (es. splash screen logico)
|
2026-04-29 11:40:17 +02:00
|
|
|
if (sessionState.status == SessionStatus.initial) return null;
|
2026-04-09 18:17:48 +02:00
|
|
|
|
2026-05-07 18:37:25 +02:00
|
|
|
// 3. UTENTE NON LOGGATO (Ma ci arriva solo se non è su /upload)
|
2026-04-22 11:06:02 +02:00
|
|
|
if (sessionState.status == SessionStatus.unauthenticated) {
|
2026-05-07 18:37:25 +02:00
|
|
|
// Se sta già andando alle uniche altre pagine pubbliche, lascialo andare
|
2026-04-28 10:12:15 +02:00
|
|
|
if (isGoingToLogin || isGoingToSetPassword) return null;
|
2026-05-07 18:37:25 +02:00
|
|
|
// Altrimenti bloccalo e mandalo al login
|
2026-04-28 10:12:15 +02:00
|
|
|
return '/login';
|
2026-04-09 19:25:32 +02:00
|
|
|
}
|
2026-04-09 18:17:48 +02:00
|
|
|
|
2026-05-07 18:37:25 +02:00
|
|
|
// 4. UTENTE LOGGATO MA DEVE COMPLETARE L'ONBOARDING
|
2026-04-22 11:06:02 +02:00
|
|
|
if (sessionState.status == SessionStatus.onboardingRequired) {
|
|
|
|
|
return isGoingToOnboarding ? null : '/onboarding';
|
|
|
|
|
}
|
2026-04-09 18:17:48 +02:00
|
|
|
|
2026-05-07 18:37:25 +02:00
|
|
|
// 5. UTENTE PERFETTAMENTE LOGGATO E OPERATIVO
|
2026-04-22 11:06:02 +02:00
|
|
|
if (sessionState.status == SessionStatus.authenticated) {
|
2026-05-07 18:37:25 +02:00
|
|
|
// Se per sbaglio cerca di tornare al login o all'onboarding, ributtalo in dashboard
|
2026-04-29 11:40:17 +02:00
|
|
|
if (isGoingToLogin || isGoingToOnboarding) return '/';
|
|
|
|
|
return null;
|
2026-04-22 11:06:02 +02:00
|
|
|
}
|
2026-04-09 18:17:48 +02:00
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
routes: [
|
2026-04-29 11:40:17 +02:00
|
|
|
// --- ROTTE DI SERVIZIO (FUORI DALLA SHELL) ---
|
2026-04-09 18:17:48 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/login',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.login,
|
2026-04-09 18:17:48 +02:00
|
|
|
builder: (context, state) => const AuthScreen(),
|
|
|
|
|
),
|
2026-04-28 10:12:15 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/set-password',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.setPassword,
|
2026-04-28 10:12:15 +02:00
|
|
|
builder: (context, state) => const SetPasswordScreen(),
|
|
|
|
|
),
|
2026-04-09 18:17:48 +02:00
|
|
|
GoRoute(
|
2026-04-22 11:06:02 +02:00
|
|
|
path: '/onboarding',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.onboarding,
|
2026-04-22 11:06:02 +02:00
|
|
|
builder: (context, state) => BlocProvider(
|
|
|
|
|
create: (context) => OnboardingCubit(
|
|
|
|
|
GetIt.I.get<SessionCubit>(),
|
|
|
|
|
GetIt.I.get<CoreRepository>(),
|
|
|
|
|
),
|
|
|
|
|
child: const OnboardingScreen(),
|
|
|
|
|
),
|
2026-04-09 18:17:48 +02:00
|
|
|
),
|
2026-04-29 11:40:17 +02:00
|
|
|
|
|
|
|
|
// --- CORE APP (DENTRO LA SHELL CON NAVIGATION BAR/RAIL) ---
|
|
|
|
|
ShellRoute(
|
|
|
|
|
builder: (context, state, child) => AppShell(child: child),
|
|
|
|
|
routes: [
|
|
|
|
|
// 1. DASHBOARD
|
2026-05-09 19:32:40 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.home,
|
2026-05-09 19:32:40 +02:00
|
|
|
builder: (context, state) => const HomeScreen(),
|
|
|
|
|
),
|
2026-04-29 11:40:17 +02:00
|
|
|
|
|
|
|
|
// 2. HUB ANAGRAFICHE E SOTTO-ROTTE
|
|
|
|
|
GoRoute(
|
|
|
|
|
path: '/master-data',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.masterData,
|
2026-04-29 11:40:17 +02:00
|
|
|
builder: (context, state) => const MasterDataHubScreen(),
|
|
|
|
|
routes: [
|
|
|
|
|
GoRoute(
|
|
|
|
|
path: 'products', // Diventa /master-data/products
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.products,
|
2026-05-04 15:36:42 +02:00
|
|
|
builder: (context, state) {
|
|
|
|
|
context.read<ProductsCubit>().refreshCubit();
|
|
|
|
|
|
|
|
|
|
return const ProductsScreen();
|
|
|
|
|
},
|
2026-04-29 11:40:17 +02:00
|
|
|
),
|
2026-05-08 12:28:14 +02:00
|
|
|
GoRoute(
|
2026-05-08 18:51:28 +02:00
|
|
|
path: 'company-settings',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.companySettings,
|
2026-05-08 18:51:28 +02:00
|
|
|
builder: (context, state) => BlocProvider(
|
|
|
|
|
create: (context) => CompanySettingsCubit(),
|
|
|
|
|
child: const CompanySettingsScreen(),
|
|
|
|
|
),
|
2026-05-08 12:28:14 +02:00
|
|
|
),
|
2026-04-29 11:40:17 +02:00
|
|
|
GoRoute(
|
2026-05-09 19:32:40 +02:00
|
|
|
path: 'staff',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.staff, // Diventa /master-data/staff
|
2026-05-04 15:36:42 +02:00
|
|
|
builder: (context, state) => const StaffScreen(),
|
2026-04-29 11:40:17 +02:00
|
|
|
),
|
|
|
|
|
GoRoute(
|
2026-05-09 20:42:42 +02:00
|
|
|
path: Routes.stores,
|
2026-05-09 19:32:40 +02:00
|
|
|
name: 'stores', // Diventa /master-data/stores
|
2026-05-04 15:36:42 +02:00
|
|
|
builder: (context, state) => const StoresScreen(),
|
2026-04-29 11:40:17 +02:00
|
|
|
),
|
|
|
|
|
GoRoute(
|
2026-05-15 10:12:05 +02:00
|
|
|
path: '/providers',
|
|
|
|
|
name: Routes.providers,
|
|
|
|
|
builder: (context, state) => const ProviderListScreen(),
|
2026-04-29 11:40:17 +02:00
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
// 3. IMPOSTAZIONI
|
|
|
|
|
GoRoute(
|
|
|
|
|
path: '/settings',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.settings,
|
2026-05-13 12:41:07 +02:00
|
|
|
builder: (context, state) => const SettingsScreen(),
|
2026-05-08 18:51:28 +02:00
|
|
|
routes: [
|
|
|
|
|
GoRoute(
|
2026-05-09 20:42:42 +02:00
|
|
|
path: 'themeSettings',
|
|
|
|
|
name: Routes.themeSettings,
|
2026-05-08 18:51:28 +02:00
|
|
|
builder: (context, state) => const ThemeSettingsView(),
|
2026-04-29 11:40:17 +02:00
|
|
|
),
|
2026-05-08 18:51:28 +02:00
|
|
|
],
|
2026-04-29 11:40:17 +02:00
|
|
|
),
|
2026-05-04 15:36:42 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/operations',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.operations,
|
2026-05-14 12:07:05 +02:00
|
|
|
builder: (context, state) => const OperationListScreen(),
|
2026-05-04 15:36:42 +02:00
|
|
|
),
|
|
|
|
|
GoRoute(
|
|
|
|
|
path: '/customers',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.customers,
|
2026-05-04 15:36:42 +02:00
|
|
|
builder: (context, state) =>
|
2026-05-18 19:20:38 +02:00
|
|
|
const CustomersListScreen(), // O come si chiama il tuo widget della lista!
|
2026-05-04 15:36:42 +02:00
|
|
|
),
|
2026-05-07 16:28:01 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/tickets',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.tickets,
|
2026-05-14 12:07:05 +02:00
|
|
|
builder: (context, state) => const TicketListScreen(),
|
2026-05-07 16:28:01 +02:00
|
|
|
),
|
2026-05-21 14:43:47 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/notes',
|
|
|
|
|
name: Routes.notes,
|
|
|
|
|
builder: (context, state) => const NotesListScreen(),
|
|
|
|
|
),
|
2026-04-29 11:40:17 +02:00
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
// --- DETTAGLI E OPERATIVITÀ (FUORI DALLA SHELL - TUTTO SCHERMO) ---
|
2026-05-15 10:12:05 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/providers/form',
|
|
|
|
|
name: Routes.providerForm,
|
|
|
|
|
builder: (context, state) {
|
|
|
|
|
// Estraiamo il fornitore (se stiamo modificando e non creando)
|
|
|
|
|
final existingProvider = state.extra as ProviderModel?;
|
|
|
|
|
|
|
|
|
|
return BlocProvider<ProviderFormCubit>(
|
|
|
|
|
// Inizializziamo un Cubit NUOVO ogni volta che apriamo il form
|
|
|
|
|
create: (context) => ProviderFormCubit(),
|
|
|
|
|
child: ProviderFormScreen(existingProvider: existingProvider),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
2026-05-07 16:28:01 +02:00
|
|
|
GoRoute(
|
|
|
|
|
// Il path sarà es. /tickets/form/123 oppure /tickets/form/new
|
|
|
|
|
path: '/tickets/form/:id',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.ticketForm,
|
2026-05-07 16:28:01 +02:00
|
|
|
builder: (context, state) {
|
|
|
|
|
// 1. Leggiamo l'ID dall'URL
|
|
|
|
|
final String pathId = state.pathParameters['id'] ?? 'new';
|
2026-05-13 15:41:35 +02:00
|
|
|
|
|
|
|
|
// 2. CAST DA NINJA (Aggiungi i punti interrogativi!)
|
2026-05-13 12:41:07 +02:00
|
|
|
final record =
|
|
|
|
|
state.extra
|
2026-05-13 15:41:35 +02:00
|
|
|
as ({StaffMemberModel? createdBy, TicketModel? ticket})?;
|
2026-05-07 16:28:01 +02:00
|
|
|
|
2026-05-13 15:41:35 +02:00
|
|
|
// 3. LOGICA SOBRIA
|
|
|
|
|
final String? realTicketId;
|
2026-05-13 12:41:07 +02:00
|
|
|
|
2026-05-13 15:41:35 +02:00
|
|
|
if (pathId == 'new') {
|
|
|
|
|
realTicketId = null;
|
|
|
|
|
} else if (record?.ticket?.id != null) {
|
|
|
|
|
// <-- Parentesi TONDE per la condizione, GRAFFE per il blocco!
|
|
|
|
|
realTicketId = record!.ticket!.id;
|
|
|
|
|
} else {
|
|
|
|
|
realTicketId = pathId;
|
|
|
|
|
}
|
2026-05-14 15:59:46 +02:00
|
|
|
if (realTicketId != null) {
|
|
|
|
|
context.read<TrackingCubit>().loadTrackings(
|
|
|
|
|
realTicketId,
|
|
|
|
|
TrackingParentType.ticket,
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-05-19 10:32:01 +02:00
|
|
|
context.read<CustomersListCubit>().loadCustomers();
|
2026-05-07 19:29:39 +02:00
|
|
|
context.read<ProductsCubit>().loadModels();
|
|
|
|
|
context.read<ProductsCubit>().loadBrands();
|
2026-05-07 16:28:01 +02:00
|
|
|
|
|
|
|
|
return MultiBlocProvider(
|
|
|
|
|
providers: [
|
|
|
|
|
BlocProvider(
|
|
|
|
|
create: (context) => AttachmentsBloc(
|
|
|
|
|
parentType: AttachmentParentType.ticket,
|
|
|
|
|
parentId: realTicketId,
|
|
|
|
|
),
|
|
|
|
|
),
|
2026-05-13 15:41:35 +02:00
|
|
|
BlocProvider(
|
|
|
|
|
create: (context) => TicketFormCubit(
|
|
|
|
|
// Passiamo il creatore e l'eventuale ticket esistente presi dal Record!
|
|
|
|
|
createdBy: record?.createdBy,
|
|
|
|
|
existingTicket: record?.ticket,
|
|
|
|
|
),
|
|
|
|
|
),
|
2026-05-07 16:28:01 +02:00
|
|
|
],
|
|
|
|
|
|
|
|
|
|
child: TicketFormScreen(
|
|
|
|
|
ticketId: realTicketId,
|
2026-05-13 15:41:35 +02:00
|
|
|
existingTicket: record?.ticket,
|
2026-05-07 16:28:01 +02:00
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
2026-05-14 15:59:46 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/tickets/workspace/:id',
|
|
|
|
|
name: Routes.ticketWorkspace,
|
|
|
|
|
builder: (context, state) {
|
|
|
|
|
// 1. Recuperiamo il Cubit vivo dall'extra
|
|
|
|
|
final formCubit = state.extra as TicketFormCubit?;
|
|
|
|
|
|
|
|
|
|
// 2. Controllo di sicurezza (fondamentale per Flutter Web)
|
|
|
|
|
if (formCubit != null) {
|
|
|
|
|
return BlocProvider.value(
|
|
|
|
|
value: formCubit,
|
|
|
|
|
child: const TicketWorkspaceScreen(),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
// SCENARIO REFRESH WEB:
|
|
|
|
|
// Se l'utente preme F5 del browser mentre è nel banco da lavoro,
|
|
|
|
|
// l'extra viene distrutto e diventa null.
|
|
|
|
|
// In questo caso, gli diciamo elegantemente che la sessione è persa
|
|
|
|
|
// e lo invitiamo a tornare indietro, oppure restituisci direttamente
|
|
|
|
|
// un blocco di redirect!
|
|
|
|
|
return const Scaffold(
|
|
|
|
|
body: Center(
|
|
|
|
|
child: Text(
|
|
|
|
|
'Sessione di lavoro scaduta. Torna alla lista e riapri il ticket.',
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
),
|
2026-05-07 16:28:01 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/upload-success',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.uploadSuccess,
|
2026-05-07 16:28:01 +02:00
|
|
|
builder: (context, state) => const UploadSuccessScreen(),
|
|
|
|
|
),
|
2026-04-11 12:40:03 +02:00
|
|
|
GoRoute(
|
2026-05-18 19:20:38 +02:00
|
|
|
path: '/customer/details/:id',
|
|
|
|
|
name: Routes.customerDetails,
|
2026-04-11 12:40:03 +02:00
|
|
|
builder: (context, state) {
|
|
|
|
|
final customer = state.extra as CustomerModel;
|
2026-04-26 10:15:34 +02:00
|
|
|
return BlocProvider(
|
2026-05-07 16:28:01 +02:00
|
|
|
create: (context) => AttachmentsBloc(
|
|
|
|
|
parentType: AttachmentParentType.customer,
|
|
|
|
|
parentId: customer.id,
|
|
|
|
|
),
|
2026-04-26 10:15:34 +02:00
|
|
|
child: CustomerDetailScreen(customer: customer),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
2026-05-18 19:20:38 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/customer/form/:id',
|
|
|
|
|
name: Routes.customerForm,
|
|
|
|
|
builder: (context, state) {
|
2026-05-19 10:32:01 +02:00
|
|
|
final String pathId = state.pathParameters['id'] ?? 'new';
|
|
|
|
|
final String? realCustomerId;
|
|
|
|
|
if (pathId == 'new') {
|
|
|
|
|
realCustomerId = null;
|
|
|
|
|
} else {
|
|
|
|
|
realCustomerId = pathId;
|
|
|
|
|
}
|
2026-05-18 19:20:38 +02:00
|
|
|
final customer = state.extra as CustomerModel?;
|
2026-05-19 10:32:01 +02:00
|
|
|
|
2026-05-18 19:20:38 +02:00
|
|
|
return BlocProvider(
|
2026-05-19 10:32:01 +02:00
|
|
|
create: (context) => CustomerFormCubit(
|
|
|
|
|
existingCustomer: customer,
|
|
|
|
|
customerId: realCustomerId,
|
|
|
|
|
),
|
|
|
|
|
child: CustomerFormScreen(
|
|
|
|
|
customer: customer,
|
|
|
|
|
customerId: realCustomerId,
|
2026-05-18 19:20:38 +02:00
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
2026-05-07 19:29:39 +02:00
|
|
|
|
2026-04-16 11:50:29 +02:00
|
|
|
GoRoute(
|
2026-05-07 19:29:39 +02:00
|
|
|
path: '/operations/form/:id',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.operationForm,
|
2026-04-16 11:50:29 +02:00
|
|
|
builder: (context, state) {
|
2026-05-07 19:29:39 +02:00
|
|
|
final String pathId = state.pathParameters['id'] ?? 'new';
|
2026-05-13 12:41:07 +02:00
|
|
|
|
2026-05-13 15:41:35 +02:00
|
|
|
final record =
|
|
|
|
|
state.extra
|
|
|
|
|
as ({
|
|
|
|
|
StaffMemberModel? createdBy,
|
|
|
|
|
OperationModel? operation,
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
final String? realOperationId;
|
|
|
|
|
if (pathId == 'new') {
|
|
|
|
|
realOperationId = null;
|
|
|
|
|
} else if (record?.operation?.id != null) {
|
|
|
|
|
realOperationId = record!.operation!.id;
|
|
|
|
|
} else {
|
|
|
|
|
realOperationId = pathId;
|
|
|
|
|
}
|
2026-05-04 15:36:42 +02:00
|
|
|
final currentStoreId = GetIt.I
|
|
|
|
|
.get<SessionCubit>()
|
|
|
|
|
.state
|
|
|
|
|
.currentStore!
|
|
|
|
|
.id!;
|
2026-05-19 10:32:01 +02:00
|
|
|
context.read<CustomersListCubit>().loadCustomers();
|
2026-05-15 10:12:05 +02:00
|
|
|
context.read<ProviderListCubit>().loadProviders(currentStoreId);
|
2026-05-04 15:36:42 +02:00
|
|
|
context.read<ProductsCubit>().loadModels();
|
|
|
|
|
context.read<ProductsCubit>().loadBrands();
|
2026-05-08 12:28:14 +02:00
|
|
|
return MultiBlocProvider(
|
|
|
|
|
providers: [
|
|
|
|
|
BlocProvider(
|
|
|
|
|
create: (context) => AttachmentsBloc(
|
|
|
|
|
parentId: realOperationId,
|
|
|
|
|
parentType: AttachmentParentType.operation,
|
|
|
|
|
),
|
|
|
|
|
),
|
2026-05-13 12:41:07 +02:00
|
|
|
BlocProvider(
|
|
|
|
|
create: (context) => OperationFormCubit(
|
2026-05-13 15:41:35 +02:00
|
|
|
createdBy: record?.createdBy,
|
|
|
|
|
existingOperation: record?.operation,
|
2026-05-13 12:41:07 +02:00
|
|
|
),
|
|
|
|
|
),
|
2026-05-08 12:28:14 +02:00
|
|
|
],
|
2026-05-04 15:36:42 +02:00
|
|
|
child: OperationFormScreen(
|
2026-05-08 12:28:14 +02:00
|
|
|
operationId: realOperationId,
|
2026-05-13 15:41:35 +02:00
|
|
|
existingOperation: record?.operation,
|
2026-04-26 10:15:34 +02:00
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
2026-05-07 19:29:39 +02:00
|
|
|
|
2026-05-07 16:28:01 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/upload/:type/:id',
|
2026-05-09 20:42:42 +02:00
|
|
|
name: Routes.upload,
|
2026-05-07 16:28:01 +02:00
|
|
|
builder: (context, state) {
|
|
|
|
|
final typeString = state.pathParameters['type']!;
|
|
|
|
|
final id = state.pathParameters['id']!;
|
2026-05-09 09:50:20 +02:00
|
|
|
final companyId = state.uri.queryParameters['companyId']!;
|
2026-05-07 16:28:01 +02:00
|
|
|
|
|
|
|
|
// Trasformiamo la stringa dell'URL nel nostro amato Enum!
|
|
|
|
|
final parentType = AttachmentParentType.values.firstWhere(
|
|
|
|
|
(e) => e.name == typeString,
|
|
|
|
|
orElse: () =>
|
|
|
|
|
AttachmentParentType.ticket, // Fallback di sicurezza
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Creiamo il BLoC "al volo" solo per questa schermata
|
2026-05-09 17:30:51 +02:00
|
|
|
return MultiBlocProvider(
|
|
|
|
|
providers: [
|
|
|
|
|
BlocProvider<AttachmentsBloc>(
|
|
|
|
|
create: (context) =>
|
|
|
|
|
AttachmentsBloc(parentId: id, parentType: parentType),
|
|
|
|
|
),
|
|
|
|
|
BlocProvider(create: (context) => ImageUploadCubit()),
|
|
|
|
|
],
|
|
|
|
|
|
2026-05-09 16:00:06 +02:00
|
|
|
child: ImageUploadScreen(
|
2026-05-07 16:28:01 +02:00
|
|
|
title: 'Caricamento Rapido',
|
2026-05-09 09:50:20 +02:00
|
|
|
companyId: companyId,
|
2026-04-26 10:15:34 +02:00
|
|
|
),
|
2026-04-20 16:52:20 +02:00
|
|
|
);
|
2026-04-16 11:50:29 +02:00
|
|
|
},
|
|
|
|
|
),
|
2026-05-21 14:43:47 +02:00
|
|
|
GoRoute(
|
|
|
|
|
path: '/notes/edit/:id',
|
|
|
|
|
name: Routes.noteForm,
|
|
|
|
|
builder: (context, state) {
|
|
|
|
|
final id = state.pathParameters['id']!;
|
|
|
|
|
final NoteModel note = state.extra as NoteModel;
|
|
|
|
|
|
|
|
|
|
// Creiamo il BLoC "al volo" solo per questa schermata
|
|
|
|
|
return MultiBlocProvider(
|
|
|
|
|
providers: [
|
|
|
|
|
BlocProvider<AttachmentsBloc>(
|
|
|
|
|
create: (context) => AttachmentsBloc(
|
|
|
|
|
parentId: id,
|
|
|
|
|
parentType: AttachmentParentType.note,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
child: NoteFormScreen(note: note),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
2026-04-09 18:17:48 +02:00
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 11:06:02 +02:00
|
|
|
class GoRouterRefreshStream extends ChangeNotifier {
|
|
|
|
|
GoRouterRefreshStream(Stream<dynamic> stream) {
|
2026-04-09 18:17:48 +02:00
|
|
|
notifyListeners();
|
2026-04-22 11:06:02 +02:00
|
|
|
_subscription = stream.asBroadcastStream().listen(
|
|
|
|
|
(dynamic _) => notifyListeners(),
|
|
|
|
|
);
|
2026-04-09 18:17:48 +02:00
|
|
|
}
|
|
|
|
|
late final StreamSubscription<dynamic> _subscription;
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
_subscription.cancel();
|
|
|
|
|
super.dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|