feat-invite_staff #9

Merged
brontomark merged 5 commits from feat-invite_staff into main 2026-04-28 15:34:33 +02:00
8 changed files with 74 additions and 6 deletions
Showing only changes of commit 32c97accd7 - Show all commits

View File

@@ -40,9 +40,26 @@ class SessionCubit extends Cubit<SessionState> {
try {
// 1. CHI È QUESTO UTENTE? (Vediamo se ha un profilo staff, che sia Invitato o Admin)
final staff = await _repository.getStaffMemberByUserId(user.id);
// 1. Controllo Azienda
StaffMemberModel? staff = await _repository.getStaffMemberByUserId(
user.id,
);
CompanyModel? company;
if (staff != null) {
// --- LA MAGIA DEL SENSORE ---
if (staff.hasJoined == false) {
// È la primissima volta che entra! Aggiorniamo il DB.
await _repository.updateStaffMember(staff.id!, {'has_joined': true});
// Aggiorniamo anche il nostro modello in memoria per questa sessione
staff = staff.copyWith(hasJoined: true);
}
company = await _repository.getCompanyById(staff.companyId);
} else {
// È l'Admin in onboarding
company = await _repository.getCompanyByOwnerId(user.id);
}
// 1. Controllo Azienda
if (staff != null) {
// L'utente esiste già nel sistema! Carichiamo l'azienda per cui lavora
company = await _repository.getCompanyById(staff.companyId);

View File

@@ -0,0 +1,2 @@
const String resetPasswordUrl =
'https://flux-web-invite.marco-6ba.workers.dev/';

View File

@@ -131,4 +131,11 @@ class CoreRepository {
'store_id': storeId,
});
}
Future<void> updateStaffMember(
String staffId,
Map<String, dynamic> data,
) async {
await _supabase.from('staff_member').update(data).eq('id', staffId);
}
}

View File

@@ -1,6 +1,7 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/data/constants.dart';
import 'package:get_it/get_it.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
part 'auth_state.dart';
@@ -64,6 +65,28 @@ class AuthCubit extends Cubit<AuthState> {
}
}
Future<void> requestPasswordReset(String email) async {
if (email.isEmpty) {
emit(
state.copyWith(
status: AuthStatus.failure,
errorMessage: 'Devi inserire l\'indirizzo email',
),
);
return;
}
await _supabase.auth.resetPasswordForEmail(
email,
redirectTo: resetPasswordUrl,
);
emit(
state.copyWith(
status: AuthStatus.pwResetSent,
infoMessage: "Email per reset password inviata a $email!",
),
);
}
Future<void> requestLogout() async {
await _supabase.auth.signOut();
emit(state.copyWith(status: AuthStatus.initial));

View File

@@ -1,6 +1,6 @@
part of 'auth_cubit.dart';
enum AuthStatus { initial, loading, failure }
enum AuthStatus { initial, pwResetSent, loading, failure }
class AuthState extends Equatable {
final AuthStatus status;

View File

@@ -162,6 +162,21 @@ class _AuthScreenState extends State<AuthScreen> {
),
),
),
if (state.isLoginMode) ...[
const SizedBox(height: 24),
TextButton(
onPressed: () => context
.read<AuthCubit>()
.requestPasswordReset(_emailController.text.trim()),
child: Text(
'Pw dimenticata/Invito scaduto?',
style: TextStyle(
color: context.accent,
fontWeight: FontWeight.bold,
),
),
),
],
],
),
),

View File

@@ -25,6 +25,7 @@ class StaffMemberModel extends Equatable {
final String? jobTitle;
final SystemRole systemRole;
final bool isActive;
final bool hasJoined;
const StaffMemberModel({
this.id,
@@ -36,6 +37,7 @@ class StaffMemberModel extends Equatable {
this.jobTitle,
this.systemRole = SystemRole.user,
this.isActive = true,
this.hasJoined = false,
});
StaffMemberModel copyWith({
@@ -49,6 +51,7 @@ class StaffMemberModel extends Equatable {
String? jobTitle,
SystemRole? systemRole,
bool? isActive,
bool? hasJoined,
}) {
return StaffMemberModel(
id: id ?? this.id,
@@ -60,6 +63,7 @@ class StaffMemberModel extends Equatable {
jobTitle: jobTitle ?? this.jobTitle,
systemRole: systemRole ?? this.systemRole,
isActive: isActive ?? this.isActive,
hasJoined: hasJoined ?? this.hasJoined,
);
}
@@ -71,8 +75,6 @@ class StaffMemberModel extends Equatable {
email: '',
phoneNumber: '',
jobTitle: '',
systemRole: SystemRole.user,
isActive: true,
);
}
@@ -87,6 +89,7 @@ class StaffMemberModel extends Equatable {
jobTitle: map['job_title'] as String?,
systemRole: SystemRole.fromString(map['system_role']),
isActive: map['is_active'] ?? true,
hasJoined: map['has_joined'] ?? false,
);
}
@@ -101,6 +104,7 @@ class StaffMemberModel extends Equatable {
if (jobTitle != null) 'job_title': jobTitle,
'system_role': systemRole.name, // Trasforma SystemRole.admin in 'admin'
'is_active': isActive,
'has_joined': hasJoined,
};
}
@@ -115,5 +119,6 @@ class StaffMemberModel extends Equatable {
jobTitle,
systemRole,
isActive,
hasJoined,
];
}

View File

@@ -174,7 +174,6 @@ class AttachmentsSection extends StatelessWidget {
);
},
),
// --- PANNELLO AZIONI CONTESTUALI (LA MAGIA) ---
// Appare SOLO se c'è almeno un file selezionato
if (state.selectedFiles.isNotEmpty)