187 lines
5.5 KiB
Dart
187 lines
5.5 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../../../core/utils/table_search.dart';
|
|
|
|
import '../../domain/entities/rbac_entities.dart';
|
|
|
|
enum RbacTab { users, roles, permissions }
|
|
|
|
class RbacState {
|
|
const RbacState({
|
|
this.users = const [],
|
|
this.roles = const [],
|
|
this.selectedTab = RbacTab.users,
|
|
this.selectedRoleId = 'super_admin',
|
|
this.searchQuery = '',
|
|
this.roleFilter = 'All Roles',
|
|
this.departmentFilter = 'All Departments',
|
|
this.statusFilter = 'All Statuses',
|
|
this.currentPage = 0,
|
|
this.pageSize = 10,
|
|
});
|
|
|
|
final List<ManagedUser> users;
|
|
final List<ManagedRole> roles;
|
|
final RbacTab selectedTab;
|
|
final String selectedRoleId;
|
|
final String searchQuery;
|
|
final String roleFilter;
|
|
final String departmentFilter;
|
|
final String statusFilter;
|
|
final int currentPage;
|
|
final int pageSize;
|
|
|
|
int get totalUsers => users.length;
|
|
int get activeUsers =>
|
|
users.where((u) => u.status == UserAccountStatus.active).length;
|
|
int get inactiveUsers =>
|
|
users.where((u) => u.status == UserAccountStatus.inactive).length;
|
|
int get lockedUsers =>
|
|
users.where((u) => u.status == UserAccountStatus.locked).length;
|
|
|
|
List<ManagedUser> get filteredUsers {
|
|
return users.where((user) {
|
|
final q = searchQuery.toLowerCase();
|
|
final matchesSearch = q.isEmpty ||
|
|
user.name.toLowerCase().contains(q) ||
|
|
user.email.toLowerCase().contains(q) ||
|
|
user.employeeCode.toLowerCase().contains(q);
|
|
final matchesRole =
|
|
roleFilter == 'All Roles' || user.roleName == roleFilter;
|
|
final matchesDept = departmentFilter == 'All Departments' ||
|
|
user.department == departmentFilter;
|
|
final matchesStatus = statusFilter == 'All Statuses' ||
|
|
user.status.label == statusFilter;
|
|
return matchesSearch && matchesRole && matchesDept && matchesStatus;
|
|
}).toList();
|
|
}
|
|
|
|
List<ManagedUser> get pagedUsers {
|
|
final filtered = filteredUsers;
|
|
final start = currentPage * pageSize;
|
|
if (start >= filtered.length) return [];
|
|
final end = (start + pageSize).clamp(0, filtered.length);
|
|
return filtered.sublist(start, end);
|
|
}
|
|
|
|
int get totalPages => (filteredUsers.length / pageSize).ceil().clamp(1, 999);
|
|
|
|
ManagedRole? get selectedRole {
|
|
for (final role in roles) {
|
|
if (role.id == selectedRoleId) return role;
|
|
}
|
|
return roles.isNotEmpty ? roles.first : null;
|
|
}
|
|
|
|
RbacState copyWith({
|
|
List<ManagedUser>? users,
|
|
List<ManagedRole>? roles,
|
|
RbacTab? selectedTab,
|
|
String? selectedRoleId,
|
|
String? searchQuery,
|
|
String? roleFilter,
|
|
String? departmentFilter,
|
|
String? statusFilter,
|
|
int? currentPage,
|
|
int? pageSize,
|
|
}) {
|
|
return RbacState(
|
|
users: users ?? this.users,
|
|
roles: roles ?? this.roles,
|
|
selectedTab: selectedTab ?? this.selectedTab,
|
|
selectedRoleId: selectedRoleId ?? this.selectedRoleId,
|
|
searchQuery: searchQuery ?? this.searchQuery,
|
|
roleFilter: roleFilter ?? this.roleFilter,
|
|
departmentFilter: departmentFilter ?? this.departmentFilter,
|
|
statusFilter: statusFilter ?? this.statusFilter,
|
|
currentPage: currentPage ?? this.currentPage,
|
|
pageSize: pageSize ?? this.pageSize,
|
|
);
|
|
}
|
|
}
|
|
|
|
final rbacProvider = StateNotifierProvider<RbacNotifier, RbacState>((ref) {
|
|
return RbacNotifier();
|
|
});
|
|
|
|
class RbacNotifier extends StateNotifier<RbacState> {
|
|
RbacNotifier()
|
|
: super(RbacState(
|
|
users: List.of(defaultUsers),
|
|
roles: List.of(defaultRoles),
|
|
)) {
|
|
_recountRoleUsers();
|
|
}
|
|
|
|
void setTab(RbacTab tab) => state = state.copyWith(selectedTab: tab, currentPage: 0);
|
|
|
|
void setSearch(String query) =>
|
|
state = state.copyWith(searchQuery: TableSearch.normalize(query), currentPage: 0);
|
|
|
|
void setRoleFilter(String filter) =>
|
|
state = state.copyWith(roleFilter: filter, currentPage: 0);
|
|
|
|
void setDepartmentFilter(String filter) =>
|
|
state = state.copyWith(departmentFilter: filter, currentPage: 0);
|
|
|
|
void setStatusFilter(String filter) =>
|
|
state = state.copyWith(statusFilter: filter, currentPage: 0);
|
|
|
|
void setPage(int page) => state = state.copyWith(currentPage: page);
|
|
|
|
void selectRole(String roleId) => state = state.copyWith(selectedRoleId: roleId);
|
|
|
|
void togglePermission(String module, RbacAction action) {
|
|
final role = state.selectedRole;
|
|
if (role == null) return;
|
|
|
|
final key = permissionKey(module, action);
|
|
final updated = Set<String>.from(role.permissions);
|
|
if (updated.contains(key)) {
|
|
updated.remove(key);
|
|
} else {
|
|
updated.add(key);
|
|
}
|
|
|
|
final roles = state.roles.map((r) {
|
|
if (r.id == role.id) return r.copyWith(permissions: updated);
|
|
return r;
|
|
}).toList();
|
|
|
|
state = state.copyWith(roles: roles);
|
|
}
|
|
|
|
void savePermissions() {
|
|
// Permissions already in state; would sync to API here.
|
|
}
|
|
|
|
void addUser(ManagedUser user) {
|
|
state = state.copyWith(users: [...state.users, user]);
|
|
_recountRoleUsers();
|
|
}
|
|
|
|
void addRole(ManagedRole role) {
|
|
state = state.copyWith(
|
|
roles: [...state.roles, role],
|
|
selectedRoleId: role.id,
|
|
);
|
|
}
|
|
|
|
void deleteRole(String roleId) {
|
|
if (roleId == 'super_admin') return;
|
|
state = state.copyWith(
|
|
roles: state.roles.where((r) => r.id != roleId).toList(),
|
|
);
|
|
_recountRoleUsers();
|
|
}
|
|
|
|
void _recountRoleUsers() {
|
|
final roles = state.roles.map((role) {
|
|
final count =
|
|
state.users.where((u) => u.roleId == role.id).length;
|
|
return role.copyWith(userCount: count);
|
|
}).toList();
|
|
state = state.copyWith(roles: roles);
|
|
}
|
|
}
|