From c49964e5c5f4ca14cf14cdf282d20b5758ea2b6f Mon Sep 17 00:00:00 2001 From: Mark M2 Macbook Date: Tue, 28 Apr 2026 10:12:15 +0200 Subject: [PATCH] fixes Co-authored-by: Copilot --- lib/core/blocs/session/session_cubit.dart | 2 +- lib/core/blocs/session/session_state.dart | 4 +- lib/core/routes/app_router.dart | 17 ++- lib/core/widgets/set_password_screen.dart | 121 ++++++++++++++++++ .../master_data/staff/ui/staff_screen.dart | 5 +- .../onboarding/ui/staff_onboarding_form.dart | 8 ++ 6 files changed, 147 insertions(+), 10 deletions(-) create mode 100644 lib/core/widgets/set_password_screen.dart diff --git a/lib/core/blocs/session/session_cubit.dart b/lib/core/blocs/session/session_cubit.dart index ec5f7c1..3de0491 100644 --- a/lib/core/blocs/session/session_cubit.dart +++ b/lib/core/blocs/session/session_cubit.dart @@ -102,7 +102,7 @@ class SessionCubit extends Cubit { user: user, company: company, currentStore: activeStore, - currentStaff: staff, + currentStaffMember: staff, onboardingStep: OnboardingStep.none, // Svuotiamo l'onboarding ), ); diff --git a/lib/core/blocs/session/session_state.dart b/lib/core/blocs/session/session_state.dart index d3993f7..0bc69b9 100644 --- a/lib/core/blocs/session/session_state.dart +++ b/lib/core/blocs/session/session_state.dart @@ -42,7 +42,7 @@ class SessionState extends Equatable { User? user, CompanyModel? company, StoreModel? currentStore, - StaffMemberModel? currentStaff, + StaffMemberModel? currentStaffMember, OnboardingStep? onboardingStep, bool? isMobileDevice, }) { @@ -51,7 +51,7 @@ class SessionState extends Equatable { user: user ?? this.user, company: company ?? this.company, currentStore: currentStore ?? this.currentStore, - currentStaffMember: currentStaff ?? this.currentStaffMember, + currentStaffMember: currentStaffMember ?? this.currentStaffMember, onboardingStep: onboardingStep ?? this.onboardingStep, isMobileDevice: isMobileDevice ?? this.isMobileDevice, ); diff --git a/lib/core/routes/app_router.dart b/lib/core/routes/app_router.dart index e7eb138..ea2c5d3 100644 --- a/lib/core/routes/app_router.dart +++ b/lib/core/routes/app_router.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; // Importa il tuo SessionCubit e lo State import 'package:flux/core/blocs/session/session_cubit.dart'; import 'package:flux/core/data/core_repository.dart'; +import 'package:flux/core/widgets/set_password_screen.dart'; import 'package:flux/features/customers/ui/customer_mobile_upload_screen.dart'; import 'package:flux/features/auth/ui/auth_screen.dart'; import 'package:flux/features/customers/blocs/customer_files_bloc.dart'; @@ -33,6 +34,7 @@ class AppRouter { final sessionState = sessionCubit.state; final isGoingToLogin = state.matchedLocation == '/login'; final isGoingToOnboarding = state.matchedLocation == '/onboarding'; + final isGoingToSetPassword = state.matchedLocation == '/set-password'; // Caso 1: L'app si sta ancora avviando. // Restituiamo null per farlo rimanere sulla SplashScreen del main.dart @@ -43,7 +45,8 @@ class AppRouter { // Caso 2: Utente NON loggato. if (sessionState.status == SessionStatus.unauthenticated) { // Se sta già andando al login, lascialo andare. Altrimenti, forzalo al login. - return isGoingToLogin ? null : '/login'; + if (isGoingToLogin || isGoingToSetPassword) return null; + return '/login'; } // Caso 3: Utente loggato MA manca un pezzo dell'azienda (Flusso Canalizzatore) @@ -55,13 +58,13 @@ class AppRouter { // Caso 4: Utente loggato e configurato (Tutto OK!) if (sessionState.status == SessionStatus.authenticated) { - // Se per sbaglio cerca di tornare al login o all'onboarding, - // lo rimbalziamo alla home. + // Attenzione: un utente appena invitato viene considerato "loggato" + // da Supabase appena clicca il link. Quindi se sta andando su /set-password, + // dobbiamo permetterglielo e non rimbalzarlo! if (isGoingToLogin || isGoingToOnboarding) { return '/'; } - // Per tutte le altre rotte (dashboard, clienti, anagrafiche), lascialo passare. - return null; + return null; // Lascia passare per /, /customer, e anche /set-password } return null; @@ -72,6 +75,10 @@ class AppRouter { //builder: (context, state) => const LoginScreen(), builder: (context, state) => const AuthScreen(), ), + GoRoute( + path: '/set-password', + builder: (context, state) => const SetPasswordScreen(), + ), GoRoute( path: '/onboarding', builder: (context, state) => BlocProvider( diff --git a/lib/core/widgets/set_password_screen.dart b/lib/core/widgets/set_password_screen.dart new file mode 100644 index 0000000..33e54b9 --- /dev/null +++ b/lib/core/widgets/set_password_screen.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:flux/core/widgets/flux_text_field.dart'; +import 'package:get_it/get_it.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; +import 'package:go_router/go_router.dart'; + +class SetPasswordScreen extends StatefulWidget { + const SetPasswordScreen({super.key}); + + @override + State createState() => _SetPasswordScreenState(); +} + +class _SetPasswordScreenState extends State { + final _passwordCtrl = TextEditingController(); + bool _isLoading = false; + + @override + void dispose() { + _passwordCtrl.dispose(); + super.dispose(); + } + + Future _savePassword() async { + final newPassword = _passwordCtrl.text.trim(); + if (newPassword.length < 6) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("La password deve avere almeno 6 caratteri"), + ), + ); + return; + } + + setState(() => _isLoading = true); + + try { + // 1. Aggiorniamo la password dell'utente (che Supabase ha già loggato grazie al link della mail) + await GetIt.I.get().auth.updateUser( + UserAttributes(password: newPassword), + ); + + // 2. Finito! Lo mandiamo alla home o facciamo ricaricare la sessione al SessionCubit + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Password impostata! Benvenuto a bordo 🚀"), + ), + ); + context.go('/'); // Rimandiamo al router principale + } + } on AuthException catch (e) { + if (mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text("Errore Auth: ${e.message}"))); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text("Errore: $e"))); + } + } finally { + if (mounted) setState(() => _isLoading = false); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Benvenuto in FLUX!"), + automaticallyImplyLeading: + false, // Non può tornare indietro, deve mettere la password! + ), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Icon(Icons.lock_reset, size: 80, color: Colors.blueAccent), + const SizedBox(height: 24), + const Text( + "Imposta la tua Password", + textAlign: TextAlign.center, + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + const Text( + "Hai accettato l'invito. Scegli una password sicura per accedere in futuro.", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.grey), + ), + const SizedBox(height: 32), + FluxTextField( + controller: _passwordCtrl, + label: "Nuova Password", + icon: Icons.lock, + isPassword: true, + ), + const SizedBox(height: 32), + ElevatedButton( + onPressed: _isLoading ? null : _savePassword, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + ), + child: _isLoading + ? const CircularProgressIndicator(color: Colors.white) + : const Text( + "SALVA E INIZIA", + style: TextStyle(fontSize: 16), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/master_data/staff/ui/staff_screen.dart b/lib/features/master_data/staff/ui/staff_screen.dart index 11021cb..5d1c774 100644 --- a/lib/features/master_data/staff/ui/staff_screen.dart +++ b/lib/features/master_data/staff/ui/staff_screen.dart @@ -278,8 +278,9 @@ class _StaffScreenState extends State { ); }).toList(), onChanged: (val) { - if (val != null) + if (val != null) { setModalState(() => selectedRole = val); + } }, ), ), @@ -288,7 +289,7 @@ class _StaffScreenState extends State { flex: 3, child: FluxTextField( controller: jobTitleController, - label: "Qualifica (Es. Tecnico)", + label: "Qualifica (Es. Addetto)", icon: Icons.badge, ), ), diff --git a/lib/features/onboarding/ui/staff_onboarding_form.dart b/lib/features/onboarding/ui/staff_onboarding_form.dart index a280058..54d0fd7 100644 --- a/lib/features/onboarding/ui/staff_onboarding_form.dart +++ b/lib/features/onboarding/ui/staff_onboarding_form.dart @@ -1,9 +1,11 @@ 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/utils/validators.dart'; import 'package:flux/core/widgets/flux_text_field.dart'; import 'package:flux/features/master_data/staff/models/staff_member_model.dart'; import 'package:flux/features/onboarding/blocs/onboarding_cubit.dart'; +import 'package:get_it/get_it.dart'; class StaffOnboardingForm extends StatefulWidget { const StaffOnboardingForm({super.key}); @@ -26,6 +28,12 @@ class _StaffOnboardingFormState extends State { super.dispose(); } + @override + void initState() { + _emailCtrl.text = GetIt.I.get().state.user?.email ?? ''; + super.initState(); + } + @override Widget build(BuildContext context) { return Padding(