bharat_erp/lib/modules/users/presentation/providers/users_provider.dart
2026-07-24 14:01:50 +05:30

351 lines
10 KiB
Dart

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<ManagedUserModel> 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<ManagedUserModel>? 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, UsersListState>(UsersListNotifier.new);
class UsersListNotifier extends AsyncNotifier<UsersListState> {
final _columnSearch = ColumnSearchPaging(defaultLimit: 10);
@override
Future<UsersListState> build() async {
ref.keepAlive();
return _loadAll(const UserListQuery(limit: 10));
}
Future<UsersListState> _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<void> 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<void> 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<void> 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<void> 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<ExportFileResult?> 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<bool> 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<bool> 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, ManagedUserModel, String>(
UserDetailNotifier.new,
);
class UserDetailNotifier extends FamilyAsyncNotifier<ManagedUserModel, String> {
@override
Future<ManagedUserModel> 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<bool> 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, ManagedUserModel?, String?>(
UserFormNotifier.new,
);
class UserFormNotifier extends FamilyAsyncNotifier<ManagedUserModel?, String?> {
@override
Future<ManagedUserModel?> 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<ManagedUserModel?> 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<ManagedUserModel?> 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;
}
}