@@ -102,7 +102,7 @@ class SessionCubit extends Cubit<SessionState> {
|
|||||||
user: user,
|
user: user,
|
||||||
company: company,
|
company: company,
|
||||||
currentStore: activeStore,
|
currentStore: activeStore,
|
||||||
currentStaff: staff,
|
currentStaffMember: staff,
|
||||||
onboardingStep: OnboardingStep.none, // Svuotiamo l'onboarding
|
onboardingStep: OnboardingStep.none, // Svuotiamo l'onboarding
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class SessionState extends Equatable {
|
|||||||
User? user,
|
User? user,
|
||||||
CompanyModel? company,
|
CompanyModel? company,
|
||||||
StoreModel? currentStore,
|
StoreModel? currentStore,
|
||||||
StaffMemberModel? currentStaff,
|
StaffMemberModel? currentStaffMember,
|
||||||
OnboardingStep? onboardingStep,
|
OnboardingStep? onboardingStep,
|
||||||
bool? isMobileDevice,
|
bool? isMobileDevice,
|
||||||
}) {
|
}) {
|
||||||
@@ -51,7 +51,7 @@ class SessionState extends Equatable {
|
|||||||
user: user ?? this.user,
|
user: user ?? this.user,
|
||||||
company: company ?? this.company,
|
company: company ?? this.company,
|
||||||
currentStore: currentStore ?? this.currentStore,
|
currentStore: currentStore ?? this.currentStore,
|
||||||
currentStaffMember: currentStaff ?? this.currentStaffMember,
|
currentStaffMember: currentStaffMember ?? this.currentStaffMember,
|
||||||
onboardingStep: onboardingStep ?? this.onboardingStep,
|
onboardingStep: onboardingStep ?? this.onboardingStep,
|
||||||
isMobileDevice: isMobileDevice ?? this.isMobileDevice,
|
isMobileDevice: isMobileDevice ?? this.isMobileDevice,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
// Importa il tuo SessionCubit e lo State
|
// Importa il tuo SessionCubit e lo State
|
||||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||||
import 'package:flux/core/data/core_repository.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/customers/ui/customer_mobile_upload_screen.dart';
|
||||||
import 'package:flux/features/auth/ui/auth_screen.dart';
|
import 'package:flux/features/auth/ui/auth_screen.dart';
|
||||||
import 'package:flux/features/customers/blocs/customer_files_bloc.dart';
|
import 'package:flux/features/customers/blocs/customer_files_bloc.dart';
|
||||||
@@ -33,6 +34,7 @@ class AppRouter {
|
|||||||
final sessionState = sessionCubit.state;
|
final sessionState = sessionCubit.state;
|
||||||
final isGoingToLogin = state.matchedLocation == '/login';
|
final isGoingToLogin = state.matchedLocation == '/login';
|
||||||
final isGoingToOnboarding = state.matchedLocation == '/onboarding';
|
final isGoingToOnboarding = state.matchedLocation == '/onboarding';
|
||||||
|
final isGoingToSetPassword = state.matchedLocation == '/set-password';
|
||||||
|
|
||||||
// Caso 1: L'app si sta ancora avviando.
|
// Caso 1: L'app si sta ancora avviando.
|
||||||
// Restituiamo null per farlo rimanere sulla SplashScreen del main.dart
|
// Restituiamo null per farlo rimanere sulla SplashScreen del main.dart
|
||||||
@@ -43,7 +45,8 @@ class AppRouter {
|
|||||||
// Caso 2: Utente NON loggato.
|
// Caso 2: Utente NON loggato.
|
||||||
if (sessionState.status == SessionStatus.unauthenticated) {
|
if (sessionState.status == SessionStatus.unauthenticated) {
|
||||||
// Se sta già andando al login, lascialo andare. Altrimenti, forzalo al login.
|
// 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)
|
// 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!)
|
// Caso 4: Utente loggato e configurato (Tutto OK!)
|
||||||
if (sessionState.status == SessionStatus.authenticated) {
|
if (sessionState.status == SessionStatus.authenticated) {
|
||||||
// Se per sbaglio cerca di tornare al login o all'onboarding,
|
// Attenzione: un utente appena invitato viene considerato "loggato"
|
||||||
// lo rimbalziamo alla home.
|
// da Supabase appena clicca il link. Quindi se sta andando su /set-password,
|
||||||
|
// dobbiamo permetterglielo e non rimbalzarlo!
|
||||||
if (isGoingToLogin || isGoingToOnboarding) {
|
if (isGoingToLogin || isGoingToOnboarding) {
|
||||||
return '/';
|
return '/';
|
||||||
}
|
}
|
||||||
// Per tutte le altre rotte (dashboard, clienti, anagrafiche), lascialo passare.
|
return null; // Lascia passare per /, /customer, e anche /set-password
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -72,6 +75,10 @@ class AppRouter {
|
|||||||
//builder: (context, state) => const LoginScreen(),
|
//builder: (context, state) => const LoginScreen(),
|
||||||
builder: (context, state) => const AuthScreen(),
|
builder: (context, state) => const AuthScreen(),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/set-password',
|
||||||
|
builder: (context, state) => const SetPasswordScreen(),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/onboarding',
|
path: '/onboarding',
|
||||||
builder: (context, state) => BlocProvider(
|
builder: (context, state) => BlocProvider(
|
||||||
|
|||||||
121
lib/core/widgets/set_password_screen.dart
Normal file
121
lib/core/widgets/set_password_screen.dart
Normal file
@@ -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<SetPasswordScreen> createState() => _SetPasswordScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SetPasswordScreenState extends State<SetPasswordScreen> {
|
||||||
|
final _passwordCtrl = TextEditingController();
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_passwordCtrl.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _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<SupabaseClient>().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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -278,8 +278,9 @@ class _StaffScreenState extends State<StaffScreen> {
|
|||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onChanged: (val) {
|
onChanged: (val) {
|
||||||
if (val != null)
|
if (val != null) {
|
||||||
setModalState(() => selectedRole = val);
|
setModalState(() => selectedRole = val);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -288,7 +289,7 @@ class _StaffScreenState extends State<StaffScreen> {
|
|||||||
flex: 3,
|
flex: 3,
|
||||||
child: FluxTextField(
|
child: FluxTextField(
|
||||||
controller: jobTitleController,
|
controller: jobTitleController,
|
||||||
label: "Qualifica (Es. Tecnico)",
|
label: "Qualifica (Es. Addetto)",
|
||||||
icon: Icons.badge,
|
icon: Icons.badge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/utils/validators.dart';
|
||||||
import 'package:flux/core/widgets/flux_text_field.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/master_data/staff/models/staff_member_model.dart';
|
||||||
import 'package:flux/features/onboarding/blocs/onboarding_cubit.dart';
|
import 'package:flux/features/onboarding/blocs/onboarding_cubit.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
|
||||||
class StaffOnboardingForm extends StatefulWidget {
|
class StaffOnboardingForm extends StatefulWidget {
|
||||||
const StaffOnboardingForm({super.key});
|
const StaffOnboardingForm({super.key});
|
||||||
@@ -26,6 +28,12 @@ class _StaffOnboardingFormState extends State<StaffOnboardingForm> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_emailCtrl.text = GetIt.I.get<SessionCubit>().state.user?.email ?? '';
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
|
|||||||
Reference in New Issue
Block a user