fixes
This commit is contained in:
@@ -15,19 +15,23 @@ class OperationCard extends StatelessWidget {
|
||||
final OperationItem operation;
|
||||
final ValueChanged<OperationItem>? onTap;
|
||||
|
||||
const OperationCard({
|
||||
super.key,
|
||||
required this.operation,
|
||||
this.onTap,
|
||||
});
|
||||
const OperationCard({super.key, required this.operation, this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
final theme = Theme.of(context);
|
||||
final canOpen = onTap != null && paymentIdFromOperation(operation) != null;
|
||||
final amountLabel = formatAmount(operation.amount, operation.currency);
|
||||
final toAmountLabel = formatAmount(operation.toAmount, operation.toCurrency);
|
||||
final amountLabel = formatAmount(
|
||||
context,
|
||||
operation.amount,
|
||||
operation.currency,
|
||||
);
|
||||
final toAmountLabel = formatAmount(
|
||||
context,
|
||||
operation.toAmount,
|
||||
operation.toCurrency,
|
||||
);
|
||||
final showToAmount = shouldShowToAmount(operation);
|
||||
final timeLabel = formatOperationTime(context, operation.date);
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ class PayoutTotalsList extends StatelessWidget {
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
formatAmount(
|
||||
context,
|
||||
totals[index].amount,
|
||||
totals[index].currency,
|
||||
),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/money.dart';
|
||||
import 'package:pshared/models/payment/payment.dart';
|
||||
import 'package:pshared/models/payment/fx/quote.dart';
|
||||
import 'package:pshared/utils/currency.dart';
|
||||
import 'package:pshared/utils/money.dart';
|
||||
|
||||
import 'package:pweb/pages/report/details/section.dart';
|
||||
import 'package:pweb/pages/report/details/sections/rows.dart';
|
||||
@@ -13,26 +15,17 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
class PaymentFxSection extends StatelessWidget {
|
||||
final Payment payment;
|
||||
|
||||
const PaymentFxSection({
|
||||
super.key,
|
||||
required this.payment,
|
||||
});
|
||||
const PaymentFxSection({super.key, required this.payment});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
final fx = payment.lastQuote?.fxQuote;
|
||||
final rows = buildDetailRows([
|
||||
DetailValue(
|
||||
label: loc.fxRateLabel,
|
||||
value: _formatRate(fx),
|
||||
),
|
||||
DetailValue(label: loc.fxRateLabel, value: _formatRate(fx)),
|
||||
]);
|
||||
|
||||
return DetailsSection(
|
||||
title: loc.paymentDetailsFx,
|
||||
children: rows,
|
||||
);
|
||||
return DetailsSection(title: loc.paymentDetailsFx, children: rows);
|
||||
}
|
||||
|
||||
String? _formatRate(FxQuote? fx) {
|
||||
@@ -40,24 +33,33 @@ class PaymentFxSection extends StatelessWidget {
|
||||
final price = fx.price?.trim();
|
||||
if (price == null || price.isEmpty) return null;
|
||||
|
||||
final base = _firstNonEmpty([
|
||||
currencySymbolFromCode(fx.baseCurrency),
|
||||
currencySymbolFromCode(fx.baseAmount?.currency),
|
||||
final baseCurrency = _firstNonEmpty([
|
||||
fx.baseCurrency,
|
||||
fx.baseAmount?.currency,
|
||||
currencySymbolFromCode(fx.baseCurrency),
|
||||
currencySymbolFromCode(fx.baseAmount?.currency),
|
||||
]);
|
||||
final quote = _firstNonEmpty([
|
||||
currencySymbolFromCode(fx.quoteCurrency),
|
||||
currencySymbolFromCode(fx.quoteAmount?.currency),
|
||||
final quoteCurrency = _firstNonEmpty([
|
||||
fx.quoteCurrency,
|
||||
fx.quoteAmount?.currency,
|
||||
currencySymbolFromCode(fx.quoteCurrency),
|
||||
currencySymbolFromCode(fx.quoteAmount?.currency),
|
||||
]);
|
||||
|
||||
if (base == null || quote == null) {
|
||||
return price;
|
||||
}
|
||||
if (baseCurrency == null || quoteCurrency == null) return price;
|
||||
|
||||
return '1 $base = $price $quote';
|
||||
final baseDisplay = formatMoneyDisplay(
|
||||
Money(amount: '1', currency: baseCurrency),
|
||||
fallback: '1 $baseCurrency',
|
||||
invalidAmountFallback: '1',
|
||||
);
|
||||
final quoteDisplay = formatMoneyDisplay(
|
||||
Money(amount: _normalizeAmount(price), currency: quoteCurrency),
|
||||
fallback: '$price $quoteCurrency',
|
||||
invalidAmountFallback: price,
|
||||
);
|
||||
|
||||
return '$baseDisplay = $quoteDisplay';
|
||||
}
|
||||
|
||||
String? _firstNonEmpty(List<String?> values) {
|
||||
@@ -67,4 +69,8 @@ class PaymentFxSection extends StatelessWidget {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String _normalizeAmount(String raw) {
|
||||
return raw.replaceAll(RegExp(r'\s+'), '').replaceAll(',', '.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/payment/payment.dart';
|
||||
|
||||
import 'package:pweb/pages/report/details/row.dart';
|
||||
import 'package:pweb/pages/report/details/section.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
|
||||
class PaymentMetadataSection extends StatelessWidget {
|
||||
final Payment payment;
|
||||
|
||||
const PaymentMetadataSection({
|
||||
super.key,
|
||||
required this.payment,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
final metadata = payment.metadata ?? const {};
|
||||
const allowedKeys = {'upload_filename', 'upload_rows'};
|
||||
final filtered = Map<String, String>.fromEntries(
|
||||
metadata.entries.where((entry) => allowedKeys.contains(entry.key)),
|
||||
);
|
||||
|
||||
if (filtered.isEmpty) {
|
||||
return DetailsSection(
|
||||
title: loc.paymentDetailsMetadata,
|
||||
children: [
|
||||
Text(
|
||||
loc.metadataEmpty,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
final entries = filtered.entries.toList()
|
||||
..sort((a, b) => a.key.compareTo(b.key));
|
||||
|
||||
return DetailsSection(
|
||||
title: loc.paymentDetailsMetadata,
|
||||
children: entries
|
||||
.map(
|
||||
(entry) => DetailRow(
|
||||
label: _metadataLabel(loc, entry.key),
|
||||
value: entry.value,
|
||||
monospaced: true,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _metadataLabel(AppLocalizations loc, String key) {
|
||||
switch (key) {
|
||||
case 'upload_filename':
|
||||
return loc.metadataUploadFileName;
|
||||
case 'upload_rows':
|
||||
return loc.metadataTotalRecipients;
|
||||
default:
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ class OperationHistoryTile extends StatelessWidget {
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
final theme = Theme.of(context);
|
||||
final title = resolveOperationTitle(loc, operation.code);
|
||||
final operationLabel = operation.label?.trim();
|
||||
final stateView = resolveStepStateView(context, operation.state);
|
||||
final completedAt = formatCompletedAt(context, operation.completedAt);
|
||||
final canDownload = canDownloadDocument && onDownloadDocument != null;
|
||||
@@ -49,13 +50,24 @@ class OperationHistoryTile extends StatelessWidget {
|
||||
StepStateChip(view: stateView),
|
||||
],
|
||||
),
|
||||
if (operationLabel != null &&
|
||||
operationLabel.isNotEmpty &&
|
||||
operationLabel != title) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
operationLabel,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
'${loc.completedAtLabel}: $completedAt',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (canDownload) ...[
|
||||
const SizedBox(height: 8),
|
||||
TextButton.icon(
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:pweb/pages/report/details/summary_card/info_line.dart';
|
||||
import 'package:pweb/pages/report/table/badge.dart';
|
||||
import 'package:pweb/utils/report/amount_parts.dart';
|
||||
import 'package:pweb/utils/report/format.dart';
|
||||
import 'package:pweb/utils/money_display.dart';
|
||||
import 'package:pweb/utils/report/payment_mapper.dart';
|
||||
import 'package:pweb/utils/clipboard.dart';
|
||||
|
||||
@@ -24,6 +25,7 @@ class PaymentSummaryCard extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
final theme = Theme.of(context);
|
||||
final unavailableValue = unavailableMoneyValue(context);
|
||||
final status = statusFromPayment(payment);
|
||||
final dateLabel = formatDateLabel(context, resolvePaymentDate(payment));
|
||||
|
||||
@@ -33,14 +35,16 @@ class PaymentSummaryCard extends StatelessWidget {
|
||||
final toAmount = payment.lastQuote?.amounts?.destinationSettlement;
|
||||
final fee = quoteFeeTotal(payment.lastQuote);
|
||||
|
||||
final amountLabel = formatMoney(primaryAmount);
|
||||
final toAmountLabel = formatMoney(toAmount);
|
||||
final feeLabel = formatMoney(fee);
|
||||
final amountLabel = formatMoney(context, primaryAmount);
|
||||
final toAmountLabel = formatMoney(context, toAmount);
|
||||
final feeLabel = formatMoney(context, fee);
|
||||
final paymentRef = (payment.paymentRef ?? '').trim();
|
||||
|
||||
final showToAmount = toAmountLabel != '-';
|
||||
final showToAmount = toAmountLabel != unavailableValue;
|
||||
final showFee = payment.lastQuote != null;
|
||||
final feeText = feeLabel != '-' ? loc.fee(feeLabel) : loc.fee(loc.noFee);
|
||||
final feeText = feeLabel != unavailableValue
|
||||
? loc.fee(feeLabel)
|
||||
: loc.fee(loc.noFee);
|
||||
final showPaymentId = paymentRef.isNotEmpty;
|
||||
final amountParts = splitAmount(amountLabel);
|
||||
|
||||
@@ -73,12 +77,12 @@ class PaymentSummaryCard extends StatelessWidget {
|
||||
currency: amountParts.currency,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
if (amountLabel != '-')
|
||||
if (amountLabel != unavailableValue)
|
||||
InfoLine(
|
||||
icon: Icons.send_outlined,
|
||||
text: loc.sentAmount(amountLabel),
|
||||
),
|
||||
if (showToAmount && toAmountLabel != '-')
|
||||
if (showToAmount && toAmountLabel != unavailableValue)
|
||||
InfoLine(
|
||||
icon: Icons.south_east,
|
||||
text: loc.recipientWillReceive(toAmountLabel),
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/money.dart';
|
||||
import 'package:pshared/models/payment/operation.dart';
|
||||
import 'package:pshared/models/payment/status.dart';
|
||||
import 'package:pshared/utils/currency.dart';
|
||||
|
||||
import 'package:pweb/pages/report/table/badge.dart';
|
||||
import 'package:pweb/utils/money_display.dart';
|
||||
import 'package:pweb/utils/report/download_act.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
|
||||
class OperationRow {
|
||||
static DataRow build(OperationItem op, BuildContext context) {
|
||||
final isUnknownDate = op.date.millisecondsSinceEpoch == 0;
|
||||
@@ -35,13 +38,21 @@ class OperationRow {
|
||||
label: Text(loc.downloadAct),
|
||||
)
|
||||
: Text(op.fileName ?? '');
|
||||
final amountLabel = formatMoneyUiWithL10n(
|
||||
loc,
|
||||
Money(amount: amountToString(op.amount), currency: op.currency),
|
||||
);
|
||||
final toAmountLabel = formatMoneyUiWithL10n(
|
||||
loc,
|
||||
Money(amount: amountToString(op.toAmount), currency: op.toCurrency),
|
||||
);
|
||||
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(OperationStatusBadge(status: op.status)),
|
||||
DataCell(documentCell),
|
||||
DataCell(Text('${amountToString(op.amount)} ${op.currency}')),
|
||||
DataCell(Text('${amountToString(op.toAmount)} ${op.toCurrency}')),
|
||||
DataCell(Text(amountLabel)),
|
||||
DataCell(Text(toAmountLabel)),
|
||||
DataCell(Text(op.payId)),
|
||||
DataCell(Text(op.cardNumber ?? '-')),
|
||||
DataCell(Text(op.name)),
|
||||
|
||||
Reference in New Issue
Block a user