import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../core/utils/column_search_paging.dart'; import '../../../../core/utils/table_search.dart'; import '../../../../shared/models/export_file_result.dart'; import '../../../../shared/models/user_management_models.dart'; import '../../data/repositories/user_repository_impl.dart'; import '../../domain/usecases/user_usecases.dart'; class UsersListState { const UsersListState({ this.users = const [], this.summary, this.filters, this.query = const UserListQuery(), this.total = 0, this.totalPages = 1, this.isRefreshing = false, this.isExporting = false, this.actionError, this.actionSuccess, }); final List users; final UserSummaryModel? summary; final UserFiltersModel? filters; final UserListQuery query; final int total; final int totalPages; final bool isRefreshing; final bool isExporting; final String? actionError; final String? actionSuccess; UsersListState copyWith({ List? users, UserSummaryModel? summary, UserFiltersModel? filters, UserListQuery? query, int? total, int? totalPages, bool? isRefreshing, bool? isExporting, String? actionError, String? actionSuccess, bool clearMessages = false, }) { return UsersListState( users: users ?? this.users, summary: summary ?? this.summary, filters: filters ?? this.filters, query: query ?? this.query, total: total ?? this.total, totalPages: totalPages ?? this.totalPages, isRefreshing: isRefreshing ?? this.isRefreshing, isExporting: isExporting ?? this.isExporting, actionError: clearMessages ? null : actionError ?? this.actionError, actionSuccess: clearMessages ? null : actionSuccess ?? this.actionSuccess, ); } } final getUsersUseCaseProvider = Provider((ref) { return GetUsersUseCase(ref.watch(userRepositoryProvider)); }); final getUserSummaryUseCaseProvider = Provider((ref) { return GetUserSummaryUseCase(ref.watch(userRepositoryProvider)); }); final getUserFiltersUseCaseProvider = Provider((ref) { return GetUserFiltersUseCase(ref.watch(userRepositoryProvider)); }); final createUserUseCaseProvider = Provider((ref) { return CreateUserUseCase(ref.watch(userRepositoryProvider)); }); final updateUserUseCaseProvider = Provider((ref) { return UpdateUserUseCase(ref.watch(userRepositoryProvider)); }); final deleteUserUseCaseProvider = Provider((ref) { return DeleteUserUseCase(ref.watch(userRepositoryProvider)); }); final updateProfileUseCaseProvider = Provider((ref) { return UpdateProfileUseCase(ref.watch(userRepositoryProvider)); }); final getUserByIdUseCaseProvider = Provider((ref) { return GetUserByIdUseCase(ref.watch(userRepositoryProvider)); }); final exportUsersUseCaseProvider = Provider((ref) { return ExportUsersUseCase(ref.watch(userRepositoryProvider)); }); final usersListProvider = AsyncNotifierProvider(UsersListNotifier.new); class UsersListNotifier extends AsyncNotifier { final _columnSearch = ColumnSearchPaging(defaultLimit: 10); @override Future build() async { ref.keepAlive(); return _loadAll(const UserListQuery(limit: 10)); } Future _loadAll(UserListQuery query) async { final summaryResult = await ref.read(getUserSummaryUseCaseProvider)(); final filtersResult = await ref.read(getUserFiltersUseCaseProvider)(); final usersResult = await ref.read(getUsersUseCaseProvider)(query); if (usersResult.failure != null) throw usersResult.failure!; final page = usersResult.data!; return UsersListState( users: page.items, summary: summaryResult.data, filters: filtersResult.data, query: query, total: page.total, totalPages: page.totalPages, ); } Future refresh() async { final current = state.valueOrNull ?? const UsersListState(); state = AsyncData(current.copyWith(isRefreshing: true, clearMessages: true)); try { state = AsyncData(await _loadAll(current.query)); } catch (e, st) { state = AsyncError(e, st); } } Future applyQuery(UserListQuery query) async { final previous = state.valueOrNull; if (previous == null) { state = const AsyncLoading(); } try { state = AsyncData(await _loadAll(query)); } catch (e, st) { state = AsyncError(e, st); } } void setSearch(String search) { final current = state.valueOrNull; if (current == null) return; applyQuery(current.query.copyWith(search: TableSearch.normalize(search), page: 1)); } Future ensureColumnSearchDataset() async { final current = state.valueOrNull; if (current == null) return; final limit = _columnSearch.beginFullDataset( currentLimit: current.query.limit, total: current.total, ); if (limit == null) return; await applyQuery( current.query.copyWith(search: null, page: 1, limit: limit), ); } void clearColumnSearchDataset() { final current = state.valueOrNull; if (current == null) return; final limit = _columnSearch.endFullDataset(); if (limit == null) return; applyQuery(current.query.copyWith(search: null, page: 1, limit: limit)); } void setPage(int page) { final current = state.valueOrNull; if (current == null) return; applyQuery(current.query.copyWith(page: page)); } void setPageSize(int limit) { final current = state.valueOrNull; if (current == null) return; applyQuery(current.query.copyWith(limit: limit, page: 1)); } void setSort(String sortBy, String sortOrder) { final current = state.valueOrNull; if (current == null) return; applyQuery(current.query.copyWith(sortBy: sortBy, sortOrder: sortOrder, page: 1)); } void setStatusFilter(String? status) { final current = state.valueOrNull; if (current == null) return; applyQuery(current.query.copyWith(status: status, page: 1)); } void setRoleFilter(int? roleId) { final current = state.valueOrNull; if (current == null) return; applyQuery(current.query.copyWith(roleId: roleId, page: 1)); } void setDepartmentFilter(int? departmentId) { final current = state.valueOrNull; if (current == null) return; applyQuery(current.query.copyWith(departmentId: departmentId, page: 1)); } Future resetFilters() async { final current = state.valueOrNull; if (current == null) return; final clearedQuery = UserListQuery(limit: current.query.limit); final query = current.query; final hasActiveFilters = (query.search?.isNotEmpty ?? false) || query.roleId != null || query.departmentId != null || query.status != null; if (!hasActiveFilters && query.page == clearedQuery.page && query.limit == clearedQuery.limit) { return; } await applyQuery(clearedQuery); } Future exportUsers() async { final current = state.valueOrNull; if (current == null) return null; state = AsyncData(current.copyWith(isExporting: true, clearMessages: true)); final result = await ref.read(exportUsersUseCaseProvider)(current.query); final latest = state.valueOrNull ?? current; if (result.failure != null) { state = AsyncData( latest.copyWith( isExporting: false, actionError: result.failure!.message, ), ); return null; } state = AsyncData(latest.copyWith(isExporting: false)); return result.data; } Future deactivateUser(String id) async { final result = await ref.read(deleteUserUseCaseProvider)(id); if (result.failure != null) { final current = state.valueOrNull; if (current != null) { state = AsyncData( current.copyWith(actionError: result.failure!.message), ); } return false; } await refresh(); return true; } Future toggleUserStatus(ManagedUserModel user) async { final updateUseCase = ref.read(updateUserUseCaseProvider); final activating = user.status != 'active'; final result = await updateUseCase( user.id, UpdateUserRequest( status: activating ? 'active' : 'inactive', isActive: activating, ), ); if (result.failure != null) { final current = state.valueOrNull; if (current != null) { state = AsyncData( current.copyWith(actionError: result.failure!.message), ); } return false; } await refresh(); return true; } } final userDetailProvider = AsyncNotifierProvider.family( UserDetailNotifier.new, ); class UserDetailNotifier extends FamilyAsyncNotifier { @override Future build(String arg) async { final useCase = ref.read(getUserByIdUseCaseProvider); final result = await useCase(arg); if (result.failure != null) throw result.failure!; return result.data!; } Future deactivate() async { final deleteUseCase = ref.read(deleteUserUseCaseProvider); final result = await deleteUseCase(arg); if (result.failure != null) return false; ref.invalidateSelf(); await future; return true; } } final userFormProvider = AsyncNotifierProvider.family( UserFormNotifier.new, ); class UserFormNotifier extends FamilyAsyncNotifier { @override Future build(String? arg) async { if (arg == null) return null; final useCase = ref.read(getUserByIdUseCaseProvider); final result = await useCase(arg); if (result.failure != null) throw result.failure!; return result.data; } Future submitCreate(CreateUserRequest request) async { final useCase = ref.read(createUserUseCaseProvider); final result = await useCase(request); if (result.failure != null) throw result.failure!; ref.invalidate(usersListProvider); return result.data; } Future submitUpdate(String id, UpdateUserRequest request) async { final useCase = ref.read(updateUserUseCaseProvider); final result = await useCase(id, request); if (result.failure != null) throw result.failure!; ref.invalidate(usersListProvider); ref.invalidate(userDetailProvider(id)); return result.data; } }