bharat_erp/lib/modules/audit/presentation/providers/audit_provider.dart
2026-07-22 18:17:43 +05:30

266 lines
8.0 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/audit_log_model.dart';
import '../../../../shared/models/export_file_result.dart';
import '../../data/repositories/audit_repository_impl.dart';
import '../../domain/usecases/audit_usecases.dart';
class AuditLogsListState {
const AuditLogsListState({
this.items = const [],
this.filters = const AuditLogFilterOptions(),
this.query = const AuditLogListQuery(),
this.total = 0,
this.totalPages = 1,
this.filtersRequired = true,
this.isRefreshing = false,
this.isExporting = false,
this.actionError,
this.actionSuccess,
});
final List<AuditLogEntryModel> items;
final AuditLogFilterOptions filters;
final AuditLogListQuery query;
final int total;
final int totalPages;
final bool filtersRequired;
final bool isRefreshing;
final bool isExporting;
final String? actionError;
final String? actionSuccess;
AuditLogsListState copyWith({
List<AuditLogEntryModel>? items,
AuditLogFilterOptions? filters,
AuditLogListQuery? query,
int? total,
int? totalPages,
bool? filtersRequired,
bool? isRefreshing,
bool? isExporting,
String? actionError,
String? actionSuccess,
bool clearMessages = false,
}) {
return AuditLogsListState(
items: items ?? this.items,
filters: filters ?? this.filters,
query: query ?? this.query,
total: total ?? this.total,
totalPages: totalPages ?? this.totalPages,
filtersRequired: filtersRequired ?? this.filtersRequired,
isRefreshing: isRefreshing ?? this.isRefreshing,
isExporting: isExporting ?? this.isExporting,
actionError: clearMessages ? null : actionError ?? this.actionError,
actionSuccess: clearMessages ? null : actionSuccess ?? this.actionSuccess,
);
}
}
final getAuditFiltersUseCaseProvider = Provider((ref) {
return GetAuditFiltersUseCase(ref.watch(auditRepositoryProvider));
});
final getAuditLogsUseCaseProvider = Provider((ref) {
return GetAuditLogsUseCase(ref.watch(auditRepositoryProvider));
});
final getAuditLogByIdUseCaseProvider = Provider((ref) {
return GetAuditLogByIdUseCase(ref.watch(auditRepositoryProvider));
});
final exportAuditLogsUseCaseProvider = Provider((ref) {
return ExportAuditLogsUseCase(ref.watch(auditRepositoryProvider));
});
final auditLogsListProvider =
AsyncNotifierProvider<AuditLogsListNotifier, AuditLogsListState>(
AuditLogsListNotifier.new,
);
class AuditLogsListNotifier extends AsyncNotifier<AuditLogsListState> {
final _columnSearch = ColumnSearchPaging();
@override
Future<AuditLogsListState> build() async {
ref.keepAlive();
return _loadAll(const AuditLogListQuery(limit: 20));
}
Future<AuditLogsListState> _loadAll(AuditLogListQuery query) async {
final filtersResult = await ref.read(getAuditFiltersUseCaseProvider)();
final listResult = await ref.read(getAuditLogsUseCaseProvider)(query);
if (listResult.failure != null) throw listResult.failure!;
final page = listResult.data!;
return AuditLogsListState(
items: page.items,
filters: filtersResult.data ?? const AuditLogFilterOptions(),
query: query,
total: page.total,
totalPages: page.totalPages,
filtersRequired: page.filtersRequired || !query.hasActiveFilter,
);
}
Future<void> refresh() async {
final current = state.valueOrNull ?? const AuditLogsListState();
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(AuditLogListQuery query) async {
final previous = state.valueOrNull;
if (previous == null) {
state = const AsyncLoading();
} else {
state = AsyncData(previous.copyWith(query: query, clearMessages: true));
}
try {
final filters = previous?.filters ?? const AuditLogFilterOptions();
final listResult = await ref.read(getAuditLogsUseCaseProvider)(query);
if (listResult.failure != null) throw listResult.failure!;
final page = listResult.data!;
state = AsyncData(
AuditLogsListState(
items: page.items,
filters: filters,
query: query,
total: page.total,
totalPages: page.totalPages,
filtersRequired: page.filtersRequired || !query.hasActiveFilter,
),
);
} 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 setTableName(String? tableName) {
final current = state.valueOrNull;
if (current == null) return;
applyQuery(current.query.copyWith(tableName: tableName, page: 1));
}
void setAction(String? action) {
final current = state.valueOrNull;
if (current == null) return;
applyQuery(current.query.copyWith(action: action, page: 1));
}
void setPerformedBy(int? performedBy) {
final current = state.valueOrNull;
if (current == null) return;
applyQuery(current.query.copyWith(performedBy: performedBy, page: 1));
}
void setDateRange(DateTime? dateFrom, DateTime? dateTo) {
final current = state.valueOrNull;
if (current == null) return;
applyQuery(
current.query.copyWith(dateFrom: dateFrom, dateTo: dateTo, page: 1),
);
}
Future<void> resetFilters() async {
final current = state.valueOrNull;
if (current == null) return;
await applyQuery(AuditLogListQuery(limit: current.query.limit));
}
Future<ExportFileResult?> exportLogs() async {
final current = state.valueOrNull;
if (current == null) return null;
if (!current.query.hasActiveFilter) {
state = AsyncData(
current.copyWith(
actionError: 'Apply at least one filter before exporting.',
),
);
return null;
}
state = AsyncData(current.copyWith(isExporting: true, clearMessages: true));
final result = await ref.read(exportAuditLogsUseCaseProvider)(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;
}
}
final auditLogDetailProvider = AsyncNotifierProvider.family<
AuditLogDetailNotifier, AuditLogDetailModel, String>(
AuditLogDetailNotifier.new,
);
class AuditLogDetailNotifier
extends FamilyAsyncNotifier<AuditLogDetailModel, String> {
@override
Future<AuditLogDetailModel> build(String arg) async {
final result = await ref.read(getAuditLogByIdUseCaseProvider)(arg);
if (result.failure != null) throw result.failure!;
return result.data!;
}
}