Files
flux/lib/core/routes/app_router.dart
2026-05-07 13:28:41 +02:00

329 lines
13 KiB
Dart

import 'dart:async';
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/data/core_repository.dart';
import 'package:flux/core/layout/app_shell.dart';
import 'package:flux/core/utils/extensions.dart';
import 'package:flux/core/widgets/set_password_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/customers/blocs/customers_cubit.dart';
import 'package:flux/features/customers/models/customer_model.dart';
import 'package:flux/features/customers/ui/customer_detail_screen.dart';
import 'package:flux/features/customers/ui/customers_content.dart';
import 'package:flux/features/home/ui/home_screen.dart';
import 'package:flux/features/master_data/master_data_hub_content.dart';
import 'package:flux/features/master_data/products/blocs/product_cubit.dart';
import 'package:flux/features/master_data/products/ui/products_screen.dart';
import 'package:flux/features/master_data/providers/blocs/provider_cubit.dart';
import 'package:flux/features/master_data/providers/ui/providers_master_data_screen.dart';
import 'package:flux/features/master_data/staff/blocs/staff_cubit.dart';
import 'package:flux/features/master_data/staff/ui/staff_screen.dart';
import 'package:flux/features/master_data/store/ui/stores_screen.dart';
import 'package:flux/features/onboarding/blocs/onboarding_cubit.dart';
import 'package:flux/features/onboarding/ui/onboarding_screen.dart';
import 'package:flux/features/attachments/blocs/attachments_bloc.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/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/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:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
// Nota: Dovrai creare questi placeholder o file per non avere errori di compilazione
// import 'package:flux/features/master_data/master_data_hub_screen.dart';
// import 'package:flux/features/master_data/staff/ui/staff_screen.dart';
// import 'package:flux/features/master_data/store/ui/stores_screen.dart';
class AppRouter {
static GoRouter createRouter(SessionCubit sessionCubit) {
return GoRouter(
initialLocation: '/',
refreshListenable: GoRouterRefreshStream(sessionCubit.stream),
redirect: (context, state) {
final sessionState = sessionCubit.state;
final isGoingToLogin = state.matchedLocation == '/login';
final isGoingToOnboarding = state.matchedLocation == '/onboarding';
final isGoingToSetPassword = state.matchedLocation == '/set-password';
if (sessionState.status == SessionStatus.initial) return null;
if (sessionState.status == SessionStatus.unauthenticated) {
if (isGoingToLogin || isGoingToSetPassword) return null;
return '/login';
}
if (sessionState.status == SessionStatus.onboardingRequired) {
return isGoingToOnboarding ? null : '/onboarding';
}
if (sessionState.status == SessionStatus.authenticated) {
if (isGoingToLogin || isGoingToOnboarding) return '/';
return null;
}
return null;
},
routes: [
// --- ROTTE DI SERVIZIO (FUORI DALLA SHELL) ---
GoRoute(
path: '/login',
builder: (context, state) => const AuthScreen(),
),
GoRoute(
path: '/set-password',
builder: (context, state) => const SetPasswordScreen(),
),
GoRoute(
path: '/onboarding',
builder: (context, state) => BlocProvider(
create: (context) => OnboardingCubit(
GetIt.I.get<SessionCubit>(),
GetIt.I.get<CoreRepository>(),
),
child: const OnboardingScreen(),
),
),
// --- CORE APP (DENTRO LA SHELL CON NAVIGATION BAR/RAIL) ---
ShellRoute(
builder: (context, state, child) => AppShell(child: child),
routes: [
// 1. DASHBOARD
GoRoute(path: '/', builder: (context, state) => const HomeScreen()),
// 2. HUB ANAGRAFICHE E SOTTO-ROTTE
GoRoute(
path: '/master-data',
builder: (context, state) => const MasterDataHubScreen(),
routes: [
GoRoute(
path: 'products', // Diventa /master-data/products
builder: (context, state) {
context.read<ProductsCubit>().refreshCubit();
return const ProductsScreen();
},
),
GoRoute(
path: 'staff', // Diventa /master-data/staff
builder: (context, state) => const StaffScreen(),
),
GoRoute(
path: 'stores', // Diventa /master-data/stores
builder: (context, state) => const StoresScreen(),
),
GoRoute(
path: 'providers', // Diventa /master-data/providers
builder: (context, state) =>
const ProvidersMasterDataScreen(),
),
],
),
// 3. IMPOSTAZIONI
GoRoute(
path: '/settings',
builder: (context, state) => Scaffold(
appBar: AppBar(title: Text(context.l10n.commonSettings)),
body: Center(
child: ElevatedButton.icon(
onPressed: () => context.read<SessionCubit>().signOut(),
icon: const Icon(Icons.logout),
label: const Text("Esci da FLUX"),
),
),
),
),
GoRoute(
path: '/operations',
builder: (context, state) => const OperationsScreen(),
),
GoRoute(
path: '/customers',
builder: (context, state) =>
const CustomersContent(), // O come si chiama il tuo widget della lista!
),
GoRoute(
path: '/tickets',
builder: (context, state) => BlocProvider(
create: (context) => TicketListCubit(),
child: const TicketListScreen(),
),
),
],
),
// --- 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 MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => AttachmentsBloc(
parentType: AttachmentParentType.ticket,
parentId: realTicketId,
),
),
BlocProvider(create: (context) => TicketFormCubit()),
],
child: TicketFormScreen(
ticketId: realTicketId,
existingTicket: ticketFromExtra,
),
);
},
),
GoRoute(
path: '/customer/:id',
builder: (context, state) {
final customer = state.extra as CustomerModel;
return BlocProvider(
create: (context) => AttachmentsBloc(
parentType: AttachmentParentType.customer,
parentId: customer.id,
),
child: CustomerDetailScreen(customer: customer),
);
},
),
/* GoRoute(
path: '/customer/:id/upload',
builder: (context, state) {
final customerId = state.pathParameters['id']!;
final customerName = state.uri.queryParameters['name'] ?? 'Cliente';
return BlocProvider(
create: (context) => AttachmentsBloc(
parentType: AttachmentParentType.customer,
parentId: customerId,
),
child: SharedMobileUploadScreen(
title: 'Aggiungi allegati al cliente $customerName',
),
);
},
), */
GoRoute(
path: '/operation-form',
name: 'operation-form',
builder: (context, state) {
final existingOperation = state.extra as OperationModel?;
final operationId = state.uri.queryParameters['operationId'];
final currentStoreId = GetIt.I
.get<SessionCubit>()
.state
.currentStore!
.id!;
context.read<CustomersCubit>().loadCustomers();
context.read<ProvidersCubit>().loadActiveProvidersForStore(
currentStoreId,
);
context.read<ProductsCubit>().loadModels();
context.read<ProductsCubit>().loadBrands();
context.read<StaffCubit>().loadStaffForStore(currentStoreId);
return BlocProvider(
create: (context) => AttachmentsBloc(
parentId: operationId ?? existingOperation?.id,
parentType: AttachmentParentType.operation,
),
child: OperationFormScreen(
operationId: operationId ?? existingOperation?.id,
existingOperation: existingOperation,
),
);
},
),
/* GoRoute(
path: '/operation/:id/upload',
builder: (context, state) {
final operationId = state.pathParameters['id']!;
final operationName =
state.uri.queryParameters['name'] ?? 'Pratica';
final currentStoreId = GetIt.I
.get<SessionCubit>()
.state
.currentStore!
.id!;
context.read<CustomersCubit>().loadCustomers();
context.read<ProvidersCubit>().loadActiveProvidersForStore(
currentStoreId,
);
context.read<ProductsCubit>().loadModels();
context.read<ProductsCubit>().loadBrands();
context.read<StaffCubit>().loadStaffForStore(currentStoreId);
return BlocProvider(
create: (context) => AttachmentsBloc(
parentId: operationId,
parentType: AttachmentParentType.operation,
),
child: SharedMobileUploadScreen(
title: 'Aggiungi allegati alla pratica $operationName',
),
);
},
), */
GoRoute(
path: '/upload/:type/:id',
builder: (context, state) {
final typeString = state.pathParameters['type']!;
final id = state.pathParameters['id']!;
// 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
return BlocProvider(
create: (context) =>
AttachmentsBloc(parentId: id, parentType: parentType),
child: const SharedMobileUploadScreen(
title: 'Caricamento Rapido',
),
);
},
),
],
);
}
}
class GoRouterRefreshStream extends ChangeNotifier {
GoRouterRefreshStream(Stream<dynamic> stream) {
notifyListeners();
_subscription = stream.asBroadcastStream().listen(
(dynamic _) => notifyListeners(),
);
}
late final StreamSubscription<dynamic> _subscription;
@override
void dispose() {
_subscription.cancel();
super.dispose();
}
}