default provider
This commit is contained in:
@@ -17,14 +17,18 @@ class StoreCubit extends Cubit<StoreState> {
|
||||
|
||||
StoreCubit() : super(const StoreState(stores: []));
|
||||
|
||||
Future<void> createStore(final StoreModel store) async {
|
||||
Future<void> saveStore(final StoreModel store) async {
|
||||
emit(state.copyWith(status: StoreStatus.loading));
|
||||
try {
|
||||
await _repository.createStore(store);
|
||||
emit(state.copyWith(status: StoreStatus.success));
|
||||
final savedStore = await _repository.saveStore(store);
|
||||
emit(state.copyWith(status: StoreStatus.success, savedStore: savedStore));
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(status: StoreStatus.failure, errorMessage: e.toString()),
|
||||
state.copyWith(
|
||||
status: StoreStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
savedStore: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -70,6 +74,7 @@ class StoreCubit extends Cubit<StoreState> {
|
||||
state.copyWith(
|
||||
status: StoreStatus.failure,
|
||||
errorMessage: "Errore nel salvataggio dei provider: $e",
|
||||
savedStore: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -90,6 +95,7 @@ class StoreCubit extends Cubit<StoreState> {
|
||||
state.copyWith(
|
||||
status: StoreStatus.failure,
|
||||
errorMessage: "Errore nel salvataggio dello staff: $e",
|
||||
savedStore: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -110,6 +116,7 @@ class StoreCubit extends Cubit<StoreState> {
|
||||
state.copyWith(
|
||||
status: StoreStatus.failure,
|
||||
errorMessage: "Errore nell'associazione: $e",
|
||||
savedStore: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -130,6 +137,7 @@ class StoreCubit extends Cubit<StoreState> {
|
||||
state.copyWith(
|
||||
status: StoreStatus.failure,
|
||||
errorMessage: "Errore nella rimozione: $e",
|
||||
savedStore: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -142,7 +150,11 @@ class StoreCubit extends Cubit<StoreState> {
|
||||
loadStores();
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(status: StoreStatus.failure, errorMessage: e.toString()),
|
||||
state.copyWith(
|
||||
status: StoreStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
savedStore: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -157,6 +169,7 @@ class StoreCubit extends Cubit<StoreState> {
|
||||
state.copyWith(
|
||||
status: StoreStatus.failure,
|
||||
errorMessage: "Errore nella rimozione: $e",
|
||||
savedStore: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ class StoreState extends Equatable {
|
||||
final StoreModel? store;
|
||||
final String? errorMessage;
|
||||
final List<StoreModel> stores;
|
||||
final StoreModel?
|
||||
savedStore; // Per tenere traccia del negozio appena salvato (utile per aggiornare la sessione)
|
||||
final Map<String, List<StaffMemberModel>> staffByStore;
|
||||
|
||||
const StoreState({
|
||||
@@ -14,6 +16,7 @@ class StoreState extends Equatable {
|
||||
this.store,
|
||||
this.errorMessage,
|
||||
required this.stores,
|
||||
this.savedStore,
|
||||
this.staffByStore = const {},
|
||||
});
|
||||
|
||||
@@ -22,6 +25,7 @@ class StoreState extends Equatable {
|
||||
StoreModel? store,
|
||||
String? errorMessage,
|
||||
List<StoreModel>? stores,
|
||||
StoreModel? savedStore,
|
||||
Map<String, List<StaffMemberModel>>? staffByStore,
|
||||
}) {
|
||||
return StoreState(
|
||||
@@ -29,6 +33,7 @@ class StoreState extends Equatable {
|
||||
store: store ?? this.store,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
stores: stores ?? this.stores,
|
||||
savedStore: savedStore ?? this.savedStore,
|
||||
staffByStore: staffByStore ?? this.staffByStore,
|
||||
);
|
||||
}
|
||||
@@ -39,6 +44,7 @@ class StoreState extends Equatable {
|
||||
store,
|
||||
errorMessage,
|
||||
stores,
|
||||
savedStore,
|
||||
staffByStore,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ class StoreRepository {
|
||||
final SupabaseClient _supabase = GetIt.I.get<SupabaseClient>();
|
||||
|
||||
/// Crea un nuovo negozio associato alla compagnia dell'utente
|
||||
Future<void> createStore(StoreModel store) async {
|
||||
/* Future<void> createStore(StoreModel store) async {
|
||||
try {
|
||||
await _supabase.from(Tables.stores).insert(store.toMap());
|
||||
} on PostgrestException catch (e) {
|
||||
@@ -18,7 +18,7 @@ class StoreRepository {
|
||||
} catch (e) {
|
||||
throw 'Errore imprevisto durante la creazione del negozio: $e';
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
Future<StoreModel> saveStore(StoreModel store) async {
|
||||
try {
|
||||
|
||||
@@ -16,6 +16,7 @@ class StoreModel extends Equatable {
|
||||
final List<ProviderModel> associatedProviders; // Provider associati
|
||||
final List<StaffMemberModel>
|
||||
associatedStaffMembers; // Membri dello staff associati
|
||||
final String? defaultProviderId; // ID del provider di default (opzionale)
|
||||
|
||||
const StoreModel({
|
||||
this.id,
|
||||
@@ -30,6 +31,7 @@ class StoreModel extends Equatable {
|
||||
required this.province,
|
||||
this.associatedProviders = const [],
|
||||
this.associatedStaffMembers = const [],
|
||||
this.defaultProviderId,
|
||||
});
|
||||
|
||||
// Fondamentale per Equatable: definisce quali proprietà determinano l'uguaglianza
|
||||
@@ -47,6 +49,7 @@ class StoreModel extends Equatable {
|
||||
province,
|
||||
associatedProviders,
|
||||
associatedStaffMembers,
|
||||
defaultProviderId,
|
||||
];
|
||||
|
||||
// Il mitico copyWith per creare nuove istanze modificando solo ciò che serve
|
||||
@@ -63,6 +66,7 @@ class StoreModel extends Equatable {
|
||||
String? province,
|
||||
List<ProviderModel>? associatedProviders,
|
||||
List<StaffMemberModel>? associatedStaffMembers,
|
||||
String? Function()? defaultProviderId,
|
||||
}) {
|
||||
return StoreModel(
|
||||
id: id ?? this.id,
|
||||
@@ -78,6 +82,9 @@ class StoreModel extends Equatable {
|
||||
associatedProviders: associatedProviders ?? this.associatedProviders,
|
||||
associatedStaffMembers:
|
||||
associatedStaffMembers ?? this.associatedStaffMembers,
|
||||
defaultProviderId: defaultProviderId != null
|
||||
? defaultProviderId()
|
||||
: this.defaultProviderId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -131,6 +138,7 @@ class StoreModel extends Equatable {
|
||||
province: map['province'],
|
||||
associatedProviders: providers,
|
||||
associatedStaffMembers: staffMembers,
|
||||
defaultProviderId: map['default_provider_id'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -147,6 +155,7 @@ class StoreModel extends Equatable {
|
||||
'zip_code': zipCode,
|
||||
'city': city,
|
||||
'province': province,
|
||||
'default_provider_id': defaultProviderId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class _CreateStoreScreenState extends State<CreateStoreScreen> {
|
||||
province: _provinciaController.text.trim().toUpperCase(),
|
||||
);
|
||||
|
||||
context.read<StoreCubit>().createStore(store);
|
||||
context.read<StoreCubit>().saveStore(store);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ 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/widgets/flux_text_field.dart';
|
||||
import 'package:flux/features/master_data/providers/blocs/provider_list_cubit.dart';
|
||||
import 'package:flux/features/master_data/store/bloc/store_cubit.dart';
|
||||
import 'package:flux/features/master_data/store/models/store_model.dart';
|
||||
|
||||
@@ -19,6 +20,8 @@ class _StoreFormState extends State<StoreForm> {
|
||||
final capController = TextEditingController();
|
||||
final comuneController = TextEditingController();
|
||||
final provinciaController = TextEditingController();
|
||||
String?
|
||||
_selectedDefaultProviderId; // Per tenere traccia del provider di default selezionato
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -29,129 +32,241 @@ class _StoreFormState extends State<StoreForm> {
|
||||
capController.text = widget.store!.zipCode;
|
||||
comuneController.text = widget.store!.city;
|
||||
provinciaController.text = widget.store!.province;
|
||||
_selectedDefaultProviderId = widget.store!.defaultProviderId;
|
||||
}
|
||||
context.read<ProviderListCubit>().loadProviders(
|
||||
widget.store!.id!,
|
||||
); // Carichiamo i gestori per la dropdown
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return 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(
|
||||
widget.store == null ? "Nuovo Punto Vendita" : "Modifica Negozio",
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
return BlocListener<StoreCubit, StoreState>(
|
||||
listener: (context, state) {
|
||||
if (state.status == StoreStatus.success) {
|
||||
// 1. Diciamo alla schermata di ricaricare la lista generale dei negozi (se serve)
|
||||
context.read<StoreCubit>().loadStores();
|
||||
|
||||
// 🥷 2. IL TOCCO FINALE: Aggiorniamo la sessione globale se stiamo modificando il negozio attivo!
|
||||
if (state.savedStore != null) {
|
||||
context.read<SessionCubit>().updateCurrentStoreLocally(
|
||||
state.savedStore!,
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Chiudiamo il form
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Negozio aggiornato con successo!',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
// --- DATI PRINCIPALI ---
|
||||
FluxTextField(
|
||||
controller: nomeController,
|
||||
label: "Nome Negozio (es. Flux Milano)",
|
||||
icon: Icons.storefront_rounded,
|
||||
keyboardType: TextInputType.name,
|
||||
if (state.status == StoreStatus.failure) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(state.errorMessage ?? 'Errore di salvataggio'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
FluxTextField(
|
||||
controller: indirizzoController,
|
||||
label: "Indirizzo",
|
||||
icon: Icons.map_outlined,
|
||||
keyboardType: TextInputType.streetAddress,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// --- CAP, COMUNE, PROVINCIA (In riga) ---
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: FluxTextField(
|
||||
controller: capController,
|
||||
label: "CAP",
|
||||
icon: Icons.post_add_rounded,
|
||||
keyboardType: TextInputType.number,
|
||||
maxLength: 5,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: FluxTextField(
|
||||
controller: comuneController,
|
||||
label: "Comune",
|
||||
icon: Icons.location_city_rounded,
|
||||
keyboardType: TextInputType.name,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: FluxTextField(
|
||||
controller: provinciaController,
|
||||
label: "Prov",
|
||||
icon: Icons.explore_outlined,
|
||||
keyboardType: TextInputType.name,
|
||||
onChanged: (value) => value.toUpperCase(),
|
||||
maxLength: 2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// --- TASTO SALVA ---
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 50,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
if (nomeController.text.isEmpty) return;
|
||||
|
||||
final storeData = StoreModel(
|
||||
id: widget
|
||||
.store
|
||||
?.id, // Se nullo, Supabase ne crea uno nuovo
|
||||
name: nomeController.text,
|
||||
address: indirizzoController.text,
|
||||
zipCode: capController.text,
|
||||
city: comuneController.text,
|
||||
province: provinciaController.text,
|
||||
companyId: context
|
||||
.read<SessionCubit>()
|
||||
.state
|
||||
.company!
|
||||
.id!, // Recuperiamo la companyId
|
||||
isActive: widget.store?.isActive ?? true,
|
||||
isPaid: widget.store?.isPaid ?? false,
|
||||
paymentExpiration: widget.store?.paymentExpiration,
|
||||
);
|
||||
|
||||
// Chiamata al Bloc per il salvataggio
|
||||
context.read<StoreCubit>().createStore(storeData);
|
||||
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
widget.store == null ? "CREA NEGOZIO" : "AGGIORNA DATI",
|
||||
);
|
||||
}
|
||||
},
|
||||
child: 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(
|
||||
widget.store == null
|
||||
? "Nuovo Punto Vendita"
|
||||
: "Modifica Negozio",
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// --- DATI PRINCIPALI ---
|
||||
FluxTextField(
|
||||
controller: nomeController,
|
||||
label: "Nome Negozio (es. Flux Milano)",
|
||||
icon: Icons.storefront_rounded,
|
||||
keyboardType: TextInputType.name,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
FluxTextField(
|
||||
controller: indirizzoController,
|
||||
label: "Indirizzo",
|
||||
icon: Icons.map_outlined,
|
||||
keyboardType: TextInputType.streetAddress,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// --- CAP, COMUNE, PROVINCIA (In riga) ---
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: FluxTextField(
|
||||
controller: capController,
|
||||
label: "CAP",
|
||||
icon: Icons.post_add_rounded,
|
||||
keyboardType: TextInputType.number,
|
||||
maxLength: 5,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: FluxTextField(
|
||||
controller: comuneController,
|
||||
label: "Comune",
|
||||
icon: Icons.location_city_rounded,
|
||||
keyboardType: TextInputType.name,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: FluxTextField(
|
||||
controller: provinciaController,
|
||||
label: "Prov",
|
||||
icon: Icons.explore_outlined,
|
||||
keyboardType: TextInputType.name,
|
||||
onChanged: (value) => value.toUpperCase(),
|
||||
maxLength: 2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// --- GESTORI ---
|
||||
_defaultProviderDropdown(),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// --- TASTO SALVA ---
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 50,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
if (nomeController.text.isEmpty) return;
|
||||
|
||||
final storeData = StoreModel(
|
||||
id: widget
|
||||
.store
|
||||
?.id, // Se nullo, Supabase ne crea uno nuovo
|
||||
name: nomeController.text,
|
||||
address: indirizzoController.text,
|
||||
zipCode: capController.text,
|
||||
city: comuneController.text,
|
||||
province: provinciaController.text,
|
||||
companyId: context
|
||||
.read<SessionCubit>()
|
||||
.state
|
||||
.company!
|
||||
.id!, // Recuperiamo la companyId
|
||||
isActive: widget.store?.isActive ?? true,
|
||||
isPaid: widget.store?.isPaid ?? false,
|
||||
paymentExpiration: widget.store?.paymentExpiration,
|
||||
defaultProviderId: _selectedDefaultProviderId,
|
||||
);
|
||||
|
||||
// Chiamata al Bloc per il salvataggio
|
||||
context.read<StoreCubit>().saveStore(storeData);
|
||||
},
|
||||
child: Text(
|
||||
widget.store == null ? "CREA NEGOZIO" : "AGGIORNA DATI",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _defaultProviderDropdown() {
|
||||
return BlocBuilder<ProviderListCubit, ProviderListState>(
|
||||
builder: (context, state) {
|
||||
if (state.status == ProviderListStatus.loading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
final activeProviders = state.providers
|
||||
.where((p) => p.isActive)
|
||||
.toList();
|
||||
|
||||
// 🥷 SCENARIO ONBOARDING: La lista dei gestori è vuota
|
||||
if (activeProviders.isEmpty) {
|
||||
return TextFormField(
|
||||
enabled: false, // Disabilitiamo il campo
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Gestore di Default',
|
||||
hintText: 'Configura prima i gestori nell\'hub anagrafiche',
|
||||
hintStyle: TextStyle(color: Colors.grey[500], fontSize: 13),
|
||||
prefixIcon: const Icon(Icons.star_border, color: Colors.grey),
|
||||
disabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: Colors.grey[300]!),
|
||||
),
|
||||
fillColor: Colors.grey[50],
|
||||
filled: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// SCENARIO STANDARD: Ci sono gestori censiti, mostriamo la dropdown
|
||||
return DropdownButtonFormField<String?>(
|
||||
initialValue: _selectedDefaultProviderId,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Gestore di Default (Opzionale)',
|
||||
hintText: 'Seleziona se questo è un negozio monomarca',
|
||||
prefixIcon: const Icon(Icons.star_border, color: Colors.amber),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
items: [
|
||||
const DropdownMenuItem<String?>(
|
||||
value: null,
|
||||
child: Text(
|
||||
'Nessun gestore (Multi-brand)',
|
||||
style: TextStyle(fontStyle: FontStyle.italic),
|
||||
),
|
||||
),
|
||||
...activeProviders.map((p) {
|
||||
return DropdownMenuItem<String?>(
|
||||
value: p.id,
|
||||
child: Text(p.name),
|
||||
);
|
||||
}),
|
||||
],
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
_selectedDefaultProviderId = val;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user