Use UserRole enum for session role handling

Replace string-based role handling with a typed UserRole enum. Adds src/entities/enums/user_role.dart and exports it from krow_domain. Update SessionHandlerMixin to use List<UserRole> and change fetchUserRole to return UserRole?. V2SessionService now derives the role via UserRole.fromSessionData, and client/staff SessionListener widgets pass const <UserRole>[...] when initializing the auth listener. This centralizes role derivation and eliminates scattered role string literals.
This commit is contained in:
Achintha Isuru
2026-03-17 11:19:54 -04:00
parent cc4e2664b6
commit eccd2c6dbd
6 changed files with 44 additions and 24 deletions

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:flutter/cupertino.dart';
import 'package:krow_domain/krow_domain.dart' show UserRole;
/// Enum representing the current session state.
enum SessionStateType { loading, authenticated, unauthenticated, error }
@@ -85,11 +86,11 @@ mixin SessionHandlerMixin {
firebase_auth.FirebaseAuth get auth;
/// List of allowed roles for this app (set during initialization).
List<String> _allowedRoles = <String>[];
List<UserRole> _allowedRoles = <UserRole>[];
/// Initialize the auth state listener (call once on app startup).
void initializeAuthListener({
List<String> allowedRoles = const <String>[],
List<UserRole> allowedRoles = const <UserRole>[],
}) {
_allowedRoles = allowedRoles;
@@ -112,10 +113,10 @@ mixin SessionHandlerMixin {
/// Validates if user has one of the allowed roles.
Future<bool> validateUserRole(
String userId,
List<String> allowedRoles,
List<UserRole> allowedRoles,
) async {
try {
final String? userRole = await fetchUserRole(userId);
final UserRole? userRole = await fetchUserRole(userId);
return userRole != null && allowedRoles.contains(userRole);
} catch (e) {
debugPrint('Failed to validate user role: $e');
@@ -123,11 +124,9 @@ mixin SessionHandlerMixin {
}
}
/// Fetches user role from the backend.
///
/// Implementors should call `GET /auth/session` via [ApiService] and
/// extract the role from the response.
Future<String?> fetchUserRole(String userId);
/// Fetches the user role from the backend by calling `GET /auth/session`
/// and deriving the [UserRole] from the response context.
Future<UserRole?> fetchUserRole(String userId);
/// Handle user sign-in event.
Future<void> _handleSignIn(firebase_auth.User user) async {
@@ -135,7 +134,7 @@ mixin SessionHandlerMixin {
_emitSessionState(SessionState.loading());
if (_allowedRoles.isNotEmpty) {
final String? userRole = await fetchUserRole(user.uid);
final UserRole? userRole = await fetchUserRole(user.uid);
if (userRole == null) {
_emitSessionState(SessionState.unauthenticated());

View File

@@ -37,10 +37,10 @@ class V2SessionService with SessionHandlerMixin {
/// Fetches the user role by calling `GET /auth/session`.
///
/// Returns the role string (e.g. `STAFF`, `BUSINESS`, `BOTH`) or `null` if
/// Returns the [UserRole] derived from the session context, or `null` if
/// the call fails or the user has no role.
@override
Future<String?> fetchUserRole(String userId) async {
Future<UserRole?> fetchUserRole(String userId) async {
try {
final BaseApiService? api = _apiService;
if (api == null) {
@@ -61,16 +61,7 @@ class V2SessionService with SessionHandlerMixin {
// Per V2 auth doc, GET /auth/session is used for app startup hydration.
_hydrateSessionStores(data);
// Derive role from the presence of staff/business context.
// The session endpoint returns { user, tenant, business, vendor, staff }
// — there is no explicit "role" field.
final bool hasStaff = data['staff'] is Map<String, dynamic>;
final bool hasBusiness = data['business'] is Map<String, dynamic>;
if (hasStaff && hasBusiness) return 'BOTH';
if (hasStaff) return 'STAFF';
if (hasBusiness) return 'BUSINESS';
return null;
return UserRole.fromSessionData(data);
}
return null;
} catch (e) {