diff --git a/lib/features/anagrafiche/master_data_hub_content.dart b/lib/features/anagrafiche/master_data_hub_content.dart index 2a85516..ecb4c97 100644 --- a/lib/features/anagrafiche/master_data_hub_content.dart +++ b/lib/features/anagrafiche/master_data_hub_content.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flux/core/theme/theme.dart'; import 'package:flux/features/customers/ui/customers_content.dart'; import 'package:flux/features/products/ui/products_screen.dart'; +import 'package:flux/features/staff/ui/staff_screen.dart'; class MasterDataHubContent extends StatelessWidget { final Function(Widget) onOpenPage; @@ -56,7 +57,7 @@ class MasterDataHubContent extends StatelessWidget { label: 'Commessi', icon: Icons.badge_outlined, color: Colors.teal, - onTap: () {}, // Coming soon + onTap: () => onOpenPage(const StaffScreen()), // Coming soon ), _HubCard( label: 'Negozi', diff --git a/lib/features/staff/blocs/staff_cubit.dart b/lib/features/staff/blocs/staff_cubit.dart new file mode 100644 index 0000000..c1f27c3 --- /dev/null +++ b/lib/features/staff/blocs/staff_cubit.dart @@ -0,0 +1,110 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/core/blocs/session/session_bloc.dart'; +import 'package:flux/features/staff/data/staff_repository.dart'; +import 'package:flux/features/staff/models/staff_member_model.dart'; +import 'package:get_it/get_it.dart'; + +part 'staff_state.dart'; + +class StaffCubit extends Cubit { + final StaffRepository _repository = GetIt.I.get(); + final SessionBloc _sessionBloc; + + StaffCubit(this._sessionBloc) : super(const StaffState()); + + // Carica tutto lo staff della compagnia + Future loadAllStaff() async { + emit(state.copyWith(isLoading: true, error: null)); + try { + final staff = await _repository.getStaffMembers( + _sessionBloc.state.company!.id, + ); + emit(state.copyWith(allStaff: staff, isLoading: false)); + } catch (e) { + emit(state.copyWith(isLoading: false, error: e.toString())); + } + } + + // Carica lo staff di uno specifico negozio e aggiorna la mappa + Future loadStaffForStore(String storeId) async { + try { + final staffInStore = await _repository.getStaffMembersInStore(storeId); + final newMap = Map>.from( + state.staffByStore, + ); + newMap[storeId] = staffInStore; + emit(state.copyWith(staffByStore: newMap)); + } catch (e) { + // Qui potresti gestire l'errore silenziosamente per non bloccare tutta l'UI + } + } + + // Salva o aggiorna un membro + Future saveStaffMember(StaffMemberModel member) async { + emit(state.copyWith(isLoading: true)); + try { + await _repository.saveStaffMember(member); + await loadAllStaff(); // Ricarichiamo la lista aggiornata + } catch (e) { + emit(state.copyWith(isLoading: false, error: e.toString())); + } + } + + // Associa un dipendente a un negozio + Future assignMemberToStore(String staffId, String storeId) async { + try { + await _repository.assignToStore(staffId, storeId); + await loadStaffForStore(storeId); // Aggiorna solo quel negozio nell'UI + } catch (e) { + emit(state.copyWith(error: "Errore nell'assegnazione: $e")); + } + } + + // Rimuove un dipendente da un negozio + Future removeMemberFromStore(String staffId, String storeId) async { + try { + await _repository.removeFromStore(staffId, storeId); + await loadStaffForStore(storeId); + } catch (e) { + emit(state.copyWith(error: "Errore nella rimozione: $e")); + } + } + + Future saveStaffWithStores({ + required StaffMemberModel member, + required List selectedStoreIds, + }) async { + emit(state.copyWith(isLoading: true)); + try { + // 1. Salva o aggiorna l'anagrafica (ci serve l'ID) + // Se è un nuovo membro, Supabase ci restituirà l'ID generato + final savedMember = await _repository.saveStaffMember(member); + final String staffId = savedMember.id!; + + // 2. Sincronizzazione Negozi + // Per semplicità e pulizia, rimuoviamo le vecchie assegnazioni e inseriamo le nuove + // (Oppure facciamo un confronto tra liste, ma il reset & rewrite è più sicuro qui) + await _repository.clearStoreAssignments(staffId); + + if (selectedStoreIds.isNotEmpty) { + await Future.wait( + selectedStoreIds.map( + (storeId) => _repository.assignToStore(staffId, storeId), + ), + ); + } + + // 3. Rinfresca i dati + await loadAllStaff(); + // Aggiorniamo anche lo stato dei negozi coinvolti + for (var storeId in selectedStoreIds) { + await loadStaffForStore(storeId); + } + + emit(state.copyWith(isLoading: false)); + } catch (e) { + emit(state.copyWith(isLoading: false, error: e.toString())); + } + } +} diff --git a/lib/features/staff/blocs/staff_state.dart b/lib/features/staff/blocs/staff_state.dart new file mode 100644 index 0000000..079aed1 --- /dev/null +++ b/lib/features/staff/blocs/staff_state.dart @@ -0,0 +1,33 @@ +part of 'staff_cubit.dart'; + +class StaffState extends Equatable { + final List allStaff; + final Map> + staffByStore; // storeId -> List of staff + final bool isLoading; + final String? error; + + const StaffState({ + this.allStaff = const [], + this.staffByStore = const {}, + this.isLoading = false, + this.error, + }); + + StaffState copyWith({ + List? allStaff, + Map>? staffByStore, + bool? isLoading, + String? error, + }) { + return StaffState( + allStaff: allStaff ?? this.allStaff, + staffByStore: staffByStore ?? this.staffByStore, + isLoading: isLoading ?? this.isLoading, + error: error, + ); + } + + @override + List get props => [allStaff, staffByStore, isLoading, error]; +} diff --git a/lib/features/staff/data/staff_repository.dart b/lib/features/staff/data/staff_repository.dart index c03c820..23e7dfd 100644 --- a/lib/features/staff/data/staff_repository.dart +++ b/lib/features/staff/data/staff_repository.dart @@ -18,8 +18,14 @@ class StaffRepository { return (response as List).map((s) => StaffMemberModel.fromJson(s)).toList(); } - Future saveStaffMember(StaffMemberModel member) async { - await _supabase.from('staff_member').upsert(member.toJson()); + Future saveStaffMember(StaffMemberModel member) async { + final response = await _supabase + .from('staff_member') + .upsert(member.toJson()) + .select() + .single(); + + return StaffMemberModel.fromJson(response); } // --- LOGICA DI GIUNZIONE (Staff <-> Store) --- @@ -55,4 +61,14 @@ class StaffRepository { .eq('staff_member_id', staffId) .eq('store_id', storeId); } + + // Nel StaffRepository + + // Utility per pulire le assegnazioni esistenti prima di riscriverle + Future clearStoreAssignments(String staffId) async { + await _supabase + .from('staff_in_stores') + .delete() + .eq('staff_member_id', staffId); + } } diff --git a/lib/features/staff/ui/staff_screen.dart b/lib/features/staff/ui/staff_screen.dart new file mode 100644 index 0000000..05f7d56 --- /dev/null +++ b/lib/features/staff/ui/staff_screen.dart @@ -0,0 +1,291 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/core/blocs/session/session_bloc.dart'; +import 'package:flux/core/theme/theme.dart'; +import 'package:flux/core/widgets/flux_text_field.dart'; +import 'package:flux/features/staff/blocs/staff_cubit.dart'; // Tuo percorso +import 'package:flux/features/staff/models/staff_member_model.dart'; +import 'package:flux/features/store/bloc/store_bloc.dart'; + +class StaffScreen extends StatefulWidget { + const StaffScreen({super.key}); + + @override + State createState() => _StaffScreenState(); +} + +class _StaffScreenState extends State { + String? _selectedStoreId; + bool _showAllCompanyStaff = true; // Partiamo con la vista globale + + @override + void initState() { + super.initState(); + // Carichiamo subito tutto + context.read().loadAllStaff(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.background, + appBar: AppBar( + title: const Text("Anagrafica Personale"), + actions: [ + // Toggle per vista Azienda / Negozio + Padding( + padding: const EdgeInsets.only(right: 16), + child: FilterChip( + label: const Text("Tutta l'Azienda"), + selected: _showAllCompanyStaff, + onSelected: (val) => setState(() => _showAllCompanyStaff = val), + selectedColor: context.accent.withValues(alpha: 0.2), + ), + ), + ], + ), + body: Column( + children: [ + // --- BARRA FILTRO NEGOZIO (Visibile solo se non 'Tutta l'Azienda') --- + AnimatedContainer( + duration: const Duration(milliseconds: 300), + height: _showAllCompanyStaff ? 0 : 80, + child: _showAllCompanyStaff + ? const SizedBox() + : _buildStoreSelector(), + ), + + // --- LISTA PERSONALE --- + Expanded( + child: BlocBuilder( + builder: (context, state) { + final list = _showAllCompanyStaff + ? state.allStaff + : (state.staffByStore[_selectedStoreId] ?? []); + + if (state.isLoading && list.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + + if (list.isEmpty) { + return _buildEmptyState(); + } + + return ListView.separated( + padding: const EdgeInsets.all(16), + itemCount: list.length, + separatorBuilder: (_, _) => const SizedBox(height: 12), + itemBuilder: (context, index) { + final member = list[index]; + return _buildStaffCard(member); + }, + ); + }, + ), + ), + ], + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () => _openStaffForm(context), + label: const Text("Aggiungi"), + icon: const Icon(Icons.person_add_alt_1), + ), + ); + } + + Widget _buildStoreSelector() { + return BlocBuilder( + // Assumendo tu abbia uno StoreCubit + builder: (context, state) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: DropdownButtonFormField( + initialValue: _selectedStoreId, + decoration: const InputDecoration(labelText: "Filtra per Negozio"), + items: state.stores + .map((s) => DropdownMenuItem(value: s.id, child: Text(s.nome))) + .toList(), + onChanged: (id) { + setState(() => _selectedStoreId = id); + if (id != null) context.read().loadStaffForStore(id); + }, + ), + ); + }, + ); + } + + Widget _buildStaffCard(StaffMemberModel member) { + return Card( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: BorderSide(color: context.accent.withValues(alpha: 0.1)), + ), + child: ListTile( + contentPadding: const EdgeInsets.all(12), + leading: CircleAvatar( + backgroundColor: context.accent.withValues(alpha: 0.1), + child: Text(member.name[0], style: TextStyle(color: context.accent)), + ), + title: Text( + member.name, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (member.email.isNotEmpty) Text(member.email), + Text(member.phone.isNotEmpty ? member.phone : "Nessun telefono"), + ], + ), + trailing: const Icon(Icons.edit_note), + onTap: () => _openStaffForm(context, member: member), + ), + ); + } + + void _openStaffForm(BuildContext context, {StaffMemberModel? member}) { + final nameController = TextEditingController(text: member?.name); + final emailController = TextEditingController(text: member?.email); + final phoneController = TextEditingController(text: member?.phone); + + // Lista temporanea per le chip (se è un edit, andrebbe popolata con le assegnazioni attuali) + List tempSelectedStores = []; + + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) => StatefulBuilder( + // Necessario per le chip dentro il BottomSheet + builder: (context, setModalState) => Container( + decoration: BoxDecoration( + color: Theme.of(context).scaffoldBackgroundColor, + borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), + ), + padding: EdgeInsets.only( + top: 24, + left: 24, + right: 24, + bottom: MediaQuery.of(context).viewInsets.bottom + 24, + ), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + member == null + ? "Nuovo Collaboratore" + : "Modifica Collaboratore", + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 24), + FluxTextField( + controller: nameController, + label: "Nome e Cognome", + icon: Icons.person, + ), + const SizedBox(height: 16), + FluxTextField( + controller: emailController, + label: "Email", + icon: Icons.email, + ), + const SizedBox(height: 16), + FluxTextField( + controller: phoneController, + label: "Telefono", + icon: Icons.phone, + ), + const SizedBox(height: 24), + + const Text( + "Assegna ai Negozi", + style: TextStyle(fontWeight: FontWeight.bold), + ), + const SizedBox(height: 12), + + // --- SELETTORE NEGOZI (CHIPS) --- + BlocBuilder( + builder: (context, state) { + return Wrap( + spacing: 8, + runSpacing: 8, + children: state.stores.map((store) { + final isSelected = tempSelectedStores.contains( + store.id, + ); + return FilterChip( + label: Text(store.nome), + selected: isSelected, + onSelected: (selected) { + setModalState(() { + selected + ? tempSelectedStores.add(store.id!) + : tempSelectedStores.remove(store.id); + }); + }, + selectedColor: context.accent.withValues(alpha: 0.2), + checkmarkColor: context.accent, + ); + }).toList(), + ); + }, + ), + + const SizedBox(height: 32), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: () { + final newMember = StaffMemberModel( + id: member?.id, + name: nameController.text, + email: emailController.text, + phone: phoneController.text, + companyId: context + .read() + .state + .company! + .id, + ); + + context.read().saveStaffWithStores( + member: newMember, + selectedStoreIds: tempSelectedStores, + ); + Navigator.pop(context); + }, + child: const Text("SALVA COLLABORATORE"), + ), + ), + ], + ), + ), + ), + ), + ); + } + + Widget _buildEmptyState() { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.people_outline, size: 64, color: context.secondaryText), + const SizedBox(height: 16), + Text( + "Nessun membro trovato", + style: TextStyle(color: context.secondaryText), + ), + ], + ), + ); + } +} diff --git a/lib/features/store/bloc/store_bloc.dart b/lib/features/store/bloc/store_bloc.dart index 64ac561..8cdf95d 100644 --- a/lib/features/store/bloc/store_bloc.dart +++ b/lib/features/store/bloc/store_bloc.dart @@ -1,5 +1,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flux/core/blocs/session/session_bloc.dart'; import 'package:flux/features/store/data/store_repository.dart'; import 'package:flux/features/store/models/store_model.dart'; import 'package:get_it/get_it.dart'; @@ -9,9 +10,11 @@ part 'store_state.dart'; class StoreBloc extends Bloc { final StoreRepository _repository = GetIt.I(); + final SessionBloc _sessionBloc; - StoreBloc() : super(const StoreState()) { + StoreBloc(this._sessionBloc) : super(const StoreState(stores: [])) { on(_onCreateStore); + on(_onLoadStores); } Future _onCreateStore( @@ -28,4 +31,26 @@ class StoreBloc extends Bloc { ); } } + + Future _onLoadStores( + LoadStoresRequested event, + Emitter emit, + ) async { + emit(state.copyWith(status: StoreStatus.loading)); + try { + final stores = await _repository.getStoresByCompany( + _sessionBloc.state.company!.id, + ); + emit( + state.copyWith( + status: StoreStatus.success, + stores: stores, // Assicurati di avere 'stores' nello StoreState + ), + ); + } catch (e) { + emit( + state.copyWith(status: StoreStatus.failure, errorMessage: e.toString()), + ); + } + } } diff --git a/lib/features/store/bloc/store_events.dart b/lib/features/store/bloc/store_events.dart index 54206c8..bc13f5a 100644 --- a/lib/features/store/bloc/store_events.dart +++ b/lib/features/store/bloc/store_events.dart @@ -14,3 +14,5 @@ class CreateStoreRequested extends StoreEvent { @override List get props => [store]; } + +class LoadStoresRequested extends StoreEvent {} diff --git a/lib/features/store/bloc/store_state.dart b/lib/features/store/bloc/store_state.dart index a62ae40..38a1c01 100644 --- a/lib/features/store/bloc/store_state.dart +++ b/lib/features/store/bloc/store_state.dart @@ -6,25 +6,29 @@ class StoreState extends Equatable { final StoreStatus status; final StoreModel? store; final String? errorMessage; + final List stores; const StoreState({ this.status = StoreStatus.initial, this.store, this.errorMessage, + required this.stores, }); StoreState copyWith({ StoreStatus? status, StoreModel? store, String? errorMessage, + List? stores, }) { return StoreState( status: status ?? this.status, store: store ?? this.store, errorMessage: errorMessage ?? this.errorMessage, + stores: stores ?? this.stores, ); } @override - List get props => [status, store, errorMessage]; + List get props => [status, store, errorMessage, stores]; } diff --git a/lib/features/store/ui/create_store_screen.dart b/lib/features/store/ui/create_store_screen.dart index 47cd8cf..c57516a 100644 --- a/lib/features/store/ui/create_store_screen.dart +++ b/lib/features/store/ui/create_store_screen.dart @@ -82,115 +82,110 @@ class _CreateStoreScreenState extends State { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => StoreBloc(), - child: Scaffold( - appBar: AppBar(title: const Text('Il tuo primo Negozio')), - body: BlocConsumer( - listener: (context, state) { - if (state.status == StoreStatus.success) { - context.read().add(AppStarted()); - } - if (state.status == StoreStatus.failure) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(state.errorMessage ?? 'Errore salvataggio'), - ), - ); - } - }, - builder: (context, state) { - return SafeArea( - child: SingleChildScrollView( - padding: const EdgeInsets.all(24.0), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildHeader(context), - const SizedBox(height: 32), - - // Nome del Negozio (Icona obbligatoria) - FluxTextField( - label: 'Nome del Negozio', - icon: Icons.storefront_rounded, - controller: _nomeController, - ), - - const SizedBox(height: 24), - - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _SectionTitle(title: 'LOCALIZZAZIONE'), - TextButton.icon( - onPressed: _useCompanyAddress, - icon: const Icon(Icons.copy_rounded, size: 16), - label: const Text( - 'Copia da Azienda', - style: TextStyle(fontSize: 12), - ), - ), - ], - ), - const SizedBox(height: 12), - - // Indirizzo (Icona obbligatoria) - FluxTextField( - label: 'Indirizzo e n. civico', - icon: Icons.map_outlined, - controller: _indirizzoController, - ), - const SizedBox(height: 16), - - // RIGA TRIPLA: Comune, CAP, PR - Row( - crossAxisAlignment: CrossAxisAlignment - .start, // Allinea in alto in caso di errori - children: [ - Expanded( - flex: 3, - child: FluxTextField( - label: 'Comune', - icon: - Icons.location_city_rounded, // Icona aggiunta - controller: _comuneController, - ), - ), - const SizedBox(width: 12), - Expanded( - flex: 2, - child: FluxTextField( - label: 'CAP', - icon: Icons.post_add_rounded, // Icona aggiunta - controller: _capController, - keyboardType: TextInputType.number, - ), - ), - const SizedBox(width: 12), - Expanded( - flex: - 2, // Aumentato leggermente per ospitare l'icona - child: FluxTextField( - label: 'PR', - icon: Icons.explore_outlined, // Icona aggiunta - controller: _provinciaController, - ), - ), - ], - ), - - const SizedBox(height: 48), - - _buildSubmitButton(context, state), - ], - ), - ), + return Scaffold( + appBar: AppBar(title: const Text('Il tuo primo Negozio')), + body: BlocConsumer( + listener: (context, state) { + if (state.status == StoreStatus.success) { + context.read().add(AppStarted()); + } + if (state.status == StoreStatus.failure) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errorMessage ?? 'Errore salvataggio'), ), ); - }, - ), + } + }, + builder: (context, state) { + return SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeader(context), + const SizedBox(height: 32), + + // Nome del Negozio (Icona obbligatoria) + FluxTextField( + label: 'Nome del Negozio', + icon: Icons.storefront_rounded, + controller: _nomeController, + ), + + const SizedBox(height: 24), + + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _SectionTitle(title: 'LOCALIZZAZIONE'), + TextButton.icon( + onPressed: _useCompanyAddress, + icon: const Icon(Icons.copy_rounded, size: 16), + label: const Text( + 'Copia da Azienda', + style: TextStyle(fontSize: 12), + ), + ), + ], + ), + const SizedBox(height: 12), + + // Indirizzo (Icona obbligatoria) + FluxTextField( + label: 'Indirizzo e n. civico', + icon: Icons.map_outlined, + controller: _indirizzoController, + ), + const SizedBox(height: 16), + + // RIGA TRIPLA: Comune, CAP, PR + Row( + crossAxisAlignment: CrossAxisAlignment + .start, // Allinea in alto in caso di errori + children: [ + Expanded( + flex: 3, + child: FluxTextField( + label: 'Comune', + icon: Icons.location_city_rounded, // Icona aggiunta + controller: _comuneController, + ), + ), + const SizedBox(width: 12), + Expanded( + flex: 2, + child: FluxTextField( + label: 'CAP', + icon: Icons.post_add_rounded, // Icona aggiunta + controller: _capController, + keyboardType: TextInputType.number, + ), + ), + const SizedBox(width: 12), + Expanded( + flex: 2, // Aumentato leggermente per ospitare l'icona + child: FluxTextField( + label: 'PR', + icon: Icons.explore_outlined, // Icona aggiunta + controller: _provinciaController, + ), + ), + ], + ), + + const SizedBox(height: 48), + + _buildSubmitButton(context, state), + ], + ), + ), + ), + ); + }, ), ); } diff --git a/lib/main.dart b/lib/main.dart index 4e34c5b..038e970 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,6 +12,8 @@ import 'package:flux/features/customers/blocs/customer_bloc.dart'; import 'package:flux/features/customers/data/customer_repository.dart'; import 'package:flux/features/products/blocs/product_cubit.dart'; import 'package:flux/features/products/data/product_repository.dart'; +import 'package:flux/features/staff/blocs/staff_cubit.dart'; +import 'package:flux/features/staff/data/staff_repository.dart'; import 'package:flux/features/store/bloc/store_bloc.dart'; import 'package:flux/features/store/data/store_repository.dart'; import 'package:flux/features/settings/settings.dart'; @@ -56,6 +58,7 @@ Future setupLocator() async { getIt.registerLazySingleton(() => StoreRepository()); getIt.registerLazySingleton(() => CustomerRepository()); getIt.registerLazySingleton(() => ProductRepository()); + getIt.registerLazySingleton(() => StaffRepository()); } class FluxApp extends StatefulWidget { @@ -81,11 +84,19 @@ class _FluxAppState extends State { providers: [ BlocProvider(create: (_) => AuthBloc()), BlocProvider(create: (_) => CompanyBloc()), - BlocProvider(create: (_) => StoreBloc()), + BlocProvider( + create: (_) => + StoreBloc(context.read()) + ..add(LoadStoresRequested()), + ), BlocProvider(create: (_) => CustomerBloc()), BlocProvider( create: (context) => ProductCubit(context.read()), ), + BlocProvider( + create: (_) => + StaffCubit(context.read())..loadAllStaff(), + ), ], child: BlocBuilder( builder: (context, state) {