redesigned payment page + a lot of fixes
This commit is contained in:
117
frontend/pweb/lib/controllers/operations/report_operations.dart
Normal file
117
frontend/pweb/lib/controllers/operations/report_operations.dart
Normal file
@@ -0,0 +1,117 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/payment/operation.dart';
|
||||
import 'package:pshared/models/payment/status.dart';
|
||||
import 'package:pshared/provider/payment/payments.dart';
|
||||
|
||||
import 'package:pweb/models/state/load_more_state.dart';
|
||||
import 'package:pweb/utils/report/operations.dart';
|
||||
import 'package:pweb/utils/report/payment_mapper.dart';
|
||||
|
||||
|
||||
class ReportOperationsController extends ChangeNotifier {
|
||||
PaymentsProvider? _payments;
|
||||
DateTimeRange? _selectedRange;
|
||||
final Set<OperationStatus> _selectedStatuses = {};
|
||||
List<OperationItem> _operations = const [];
|
||||
List<OperationItem> _filtered = const [];
|
||||
|
||||
List<OperationItem> get operations => _operations;
|
||||
List<OperationItem> get filteredOperations => _filtered;
|
||||
DateTimeRange? get selectedRange => _selectedRange;
|
||||
Set<OperationStatus> get selectedStatuses =>
|
||||
UnmodifiableSetView(_selectedStatuses);
|
||||
|
||||
bool get isLoading => _payments?.isLoading ?? false;
|
||||
Exception? get error => _payments?.error;
|
||||
LoadMoreState get loadMoreState {
|
||||
if (_payments?.isLoadingMore ?? false) {
|
||||
return LoadMoreState.loading;
|
||||
}
|
||||
if (_payments?.canLoadMore ?? false) {
|
||||
return LoadMoreState.available;
|
||||
}
|
||||
return LoadMoreState.hidden;
|
||||
}
|
||||
|
||||
void update(PaymentsProvider provider) {
|
||||
if (!identical(_payments, provider)) {
|
||||
_payments = provider;
|
||||
}
|
||||
_rebuildOperations();
|
||||
}
|
||||
|
||||
void setRange(DateTimeRange? range) {
|
||||
if (_isSameRange(_selectedRange, range)) return;
|
||||
_selectedRange = range;
|
||||
_rebuildFiltered();
|
||||
}
|
||||
|
||||
void toggleStatus(OperationStatus status) {
|
||||
if (_selectedStatuses.contains(status)) {
|
||||
_selectedStatuses.remove(status);
|
||||
} else {
|
||||
_selectedStatuses.add(status);
|
||||
}
|
||||
_rebuildFiltered();
|
||||
}
|
||||
|
||||
void clearFilters() {
|
||||
if (_selectedRange == null && _selectedStatuses.isEmpty) return;
|
||||
_selectedRange = null;
|
||||
_selectedStatuses.clear();
|
||||
_rebuildFiltered();
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
await _payments?.refresh();
|
||||
}
|
||||
|
||||
Future<void> loadMore() async {
|
||||
await _payments?.loadMore();
|
||||
}
|
||||
|
||||
void _rebuildOperations() {
|
||||
final items = _payments?.payments ?? const [];
|
||||
_operations = items.map(mapPaymentToOperation).toList();
|
||||
_rebuildFiltered(notify: true);
|
||||
}
|
||||
|
||||
void _rebuildFiltered({bool notify = true}) {
|
||||
_filtered = _applyFilters(_operations);
|
||||
if (notify) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
List<OperationItem> _applyFilters(List<OperationItem> operations) {
|
||||
if (_selectedRange == null && _selectedStatuses.isEmpty) {
|
||||
return sortOperations(operations);
|
||||
}
|
||||
|
||||
final filtered = operations.where((op) {
|
||||
final statusMatch =
|
||||
_selectedStatuses.isEmpty || _selectedStatuses.contains(op.status);
|
||||
final dateMatch = _selectedRange == null ||
|
||||
isUnknownDate(op.date) ||
|
||||
(op.date.isAfter(
|
||||
_selectedRange!.start.subtract(const Duration(seconds: 1)),
|
||||
) &&
|
||||
op.date.isBefore(
|
||||
_selectedRange!.end.add(const Duration(seconds: 1)),
|
||||
));
|
||||
return statusMatch && dateMatch;
|
||||
}).toList();
|
||||
|
||||
return sortOperations(filtered);
|
||||
}
|
||||
|
||||
bool _isSameRange(DateTimeRange? left, DateTimeRange? right) {
|
||||
if (left == null && right == null) return true;
|
||||
if (left == null || right == null) return false;
|
||||
return left.start.isAtSameMomentAs(right.start) &&
|
||||
left.end.isAtSameMomentAs(right.end);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/payment/status.dart';
|
||||
|
||||
import 'package:pweb/models/wallet/wallet_transaction.dart';
|
||||
import 'package:pweb/providers/wallet_transactions.dart';
|
||||
|
||||
|
||||
class WalletTransactionsController extends ChangeNotifier {
|
||||
List<WalletTransaction> _filteredTransactions = [];
|
||||
DateTimeRange? _dateRange;
|
||||
final Set<OperationStatus> _selectedStatuses = {};
|
||||
final Set<WalletTransactionType> _selectedTypes = {};
|
||||
WalletTransactionsProvider? _provider;
|
||||
|
||||
List<WalletTransaction> get transactions =>
|
||||
_provider?.transactions ?? const [];
|
||||
List<WalletTransaction> get filteredTransactions => _filteredTransactions;
|
||||
DateTimeRange? get dateRange => _dateRange;
|
||||
Set<OperationStatus> get selectedStatuses => _selectedStatuses;
|
||||
Set<WalletTransactionType> get selectedTypes => _selectedTypes;
|
||||
bool get isLoading => _provider?.isLoading ?? false;
|
||||
String? get error => _provider?.error;
|
||||
bool get hasFilters =>
|
||||
_dateRange != null ||
|
||||
_selectedStatuses.isNotEmpty ||
|
||||
_selectedTypes.isNotEmpty;
|
||||
|
||||
void update(WalletTransactionsProvider provider) {
|
||||
if (identical(_provider, provider)) return;
|
||||
_provider?.removeListener(_onProviderChanged);
|
||||
_provider = provider;
|
||||
_provider?.addListener(_onProviderChanged);
|
||||
_rebuildFiltered(notify: false);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setDateRange(DateTimeRange? range) {
|
||||
_dateRange = range;
|
||||
_rebuildFiltered();
|
||||
}
|
||||
|
||||
void toggleStatus(OperationStatus status) {
|
||||
if (_selectedStatuses.contains(status)) {
|
||||
_selectedStatuses.remove(status);
|
||||
} else {
|
||||
_selectedStatuses.add(status);
|
||||
}
|
||||
_rebuildFiltered();
|
||||
}
|
||||
|
||||
void toggleType(WalletTransactionType type) {
|
||||
if (_selectedTypes.contains(type)) {
|
||||
_selectedTypes.remove(type);
|
||||
} else {
|
||||
_selectedTypes.add(type);
|
||||
}
|
||||
_rebuildFiltered();
|
||||
}
|
||||
|
||||
void resetFilters() {
|
||||
_dateRange = null;
|
||||
_selectedStatuses.clear();
|
||||
_selectedTypes.clear();
|
||||
_rebuildFiltered();
|
||||
}
|
||||
|
||||
void _onProviderChanged() {
|
||||
_rebuildFiltered();
|
||||
}
|
||||
|
||||
void _rebuildFiltered({bool notify = true}) {
|
||||
final source = _provider?.transactions ?? const <WalletTransaction>[];
|
||||
_filteredTransactions = source.where((tx) {
|
||||
final statusMatch =
|
||||
_selectedStatuses.isEmpty || _selectedStatuses.contains(tx.status);
|
||||
final typeMatch =
|
||||
_selectedTypes.isEmpty || _selectedTypes.contains(tx.type);
|
||||
final dateMatch = _dateRange == null ||
|
||||
(tx.date.isAfter(_dateRange!.start.subtract(const Duration(seconds: 1))) &&
|
||||
tx.date.isBefore(_dateRange!.end.add(const Duration(seconds: 1))));
|
||||
|
||||
return statusMatch && typeMatch && dateMatch;
|
||||
}).toList();
|
||||
|
||||
if (notify) notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_provider?.removeListener(_onProviderChanged);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user