temp build
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
mntxservice "github.com/tech/sendico/gateway/mntx/internal/service/gateway"
|
||||
"github.com/tech/sendico/gateway/mntx/internal/service/monetix"
|
||||
"github.com/tech/sendico/pkg/api/routers"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
msg "github.com/tech/sendico/pkg/messaging"
|
||||
@@ -168,7 +169,7 @@ func (i *Imp) loadConfig() (*config, error) {
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (i *Imp) resolveMonetixConfig(cfg monetixConfig) (mntxservice.MonetixConfig, error) {
|
||||
func (i *Imp) resolveMonetixConfig(cfg monetixConfig) (monetix.Config, error) {
|
||||
baseURL := strings.TrimSpace(cfg.BaseURL)
|
||||
if env := strings.TrimSpace(cfg.BaseURLEnv); env != "" {
|
||||
if val := strings.TrimSpace(os.Getenv(env)); val != "" {
|
||||
@@ -183,7 +184,7 @@ func (i *Imp) resolveMonetixConfig(cfg monetixConfig) (mntxservice.MonetixConfig
|
||||
if id, err := strconv.ParseInt(raw, 10, 64); err == nil {
|
||||
projectID = id
|
||||
} else {
|
||||
return mntxservice.MonetixConfig{}, merrors.InvalidArgument("invalid project id in env "+cfg.ProjectIDEnv, "monetix.project_id")
|
||||
return monetix.Config{}, merrors.InvalidArgument("invalid project id in env "+cfg.ProjectIDEnv, "monetix.project_id")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,7 +204,7 @@ func (i *Imp) resolveMonetixConfig(cfg monetixConfig) (mntxservice.MonetixConfig
|
||||
statusSuccess := strings.TrimSpace(cfg.StatusSuccess)
|
||||
statusProcessing := strings.TrimSpace(cfg.StatusProcessing)
|
||||
|
||||
return mntxservice.MonetixConfig{
|
||||
return monetix.Config{
|
||||
BaseURL: baseURL,
|
||||
ProjectID: projectID,
|
||||
SecretKey: secret,
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -31,7 +30,7 @@ func NewClient(cfg Config, httpClient *http.Client, logger mlogger.Logger) *Clie
|
||||
return &Client{
|
||||
cfg: cfg,
|
||||
client: client,
|
||||
logger: cl.Named("monetix_client"),
|
||||
logger: cl.Named("client"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +64,10 @@ func (t *PaymentType) UnmarshalJSON(data []byte) error {
|
||||
|
||||
type PaymentMethod struct {
|
||||
PermissionBound `bson:",inline" json:",inline"`
|
||||
Describable `bson:",inline" json:",inline"`
|
||||
|
||||
RecipientRef primitive.ObjectID `bson:"recipientRef" json:"recipientRef"`
|
||||
Type PaymentType `bson:"type" json:"type"`
|
||||
Data bson.Raw `bson:"data" json:"data"`
|
||||
IsMain bool `bson:"isMain" json:"isMain"`
|
||||
}
|
||||
|
||||
21
frontend/pshared/lib/api/responses/payment_method.dart
Normal file
21
frontend/pshared/lib/api/responses/payment_method.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import 'package:pshared/api/responses/base.dart';
|
||||
import 'package:pshared/api/responses/token.dart';
|
||||
import 'package:pshared/data/dto/payment/method.dart';
|
||||
|
||||
part 'payment_method.g.dart';
|
||||
|
||||
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
class PaymentMethodResponse extends BaseAuthorizedResponse {
|
||||
|
||||
@JsonKey(name: 'payment_methods')
|
||||
final List<PaymentMethodDTO> paymentMethods;
|
||||
|
||||
const PaymentMethodResponse({required super.accessToken, required this.paymentMethods});
|
||||
|
||||
factory PaymentMethodResponse.fromJson(Map<String, dynamic> json) => _$PaymentMethodResponseFromJson(json);
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$PaymentMethodResponseToJson(this);
|
||||
}
|
||||
19
frontend/pshared/lib/api/responses/recipient.dart
Normal file
19
frontend/pshared/lib/api/responses/recipient.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import 'package:pshared/api/responses/base.dart';
|
||||
import 'package:pshared/api/responses/token.dart';
|
||||
import 'package:pshared/data/dto/recipient/recipient.dart';
|
||||
|
||||
part 'recipient.g.dart';
|
||||
|
||||
|
||||
@JsonSerializable(explicitToJson: true)
|
||||
class RecipientResponse extends BaseAuthorizedResponse {
|
||||
final List<RecipientDTO> recipients;
|
||||
|
||||
const RecipientResponse({required super.accessToken, required this.recipients});
|
||||
|
||||
factory RecipientResponse.fromJson(Map<String, dynamic> json) => _$RecipientResponseFromJson(json);
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$RecipientResponseToJson(this);
|
||||
}
|
||||
@@ -10,6 +10,9 @@ part 'method.g.dart';
|
||||
class PaymentMethodDTO extends PermissionBoundDTO {
|
||||
final String recipientRef;
|
||||
final String type;
|
||||
final String name;
|
||||
final String? description;
|
||||
final bool isMain;
|
||||
final Map<String, dynamic> data;
|
||||
|
||||
@JsonKey(defaultValue: false)
|
||||
@@ -24,6 +27,9 @@ class PaymentMethodDTO extends PermissionBoundDTO {
|
||||
required this.recipientRef,
|
||||
required this.type,
|
||||
required this.data,
|
||||
required this.name,
|
||||
required this.isMain,
|
||||
this.description,
|
||||
this.isArchived = false,
|
||||
});
|
||||
|
||||
|
||||
@@ -10,20 +10,21 @@ import 'package:pshared/data/mapper/payment/iban.dart';
|
||||
import 'package:pshared/data/mapper/payment/russian_bank.dart';
|
||||
import 'package:pshared/data/mapper/payment/type.dart';
|
||||
import 'package:pshared/data/mapper/payment/wallet.dart';
|
||||
import 'package:pshared/models/describable.dart';
|
||||
import 'package:pshared/models/organization/bound.dart';
|
||||
import 'package:pshared/models/payment/methods/card.dart';
|
||||
import 'package:pshared/models/payment/methods/crypto_address.dart';
|
||||
import 'package:pshared/models/payment/methods/data.dart';
|
||||
import 'package:pshared/models/payment/methods/iban.dart';
|
||||
import 'package:pshared/models/payment/methods/russian_bank.dart';
|
||||
import 'package:pshared/models/payment/methods/type.dart';
|
||||
import 'package:pshared/models/payment/methods/wallet.dart';
|
||||
import 'package:pshared/models/payment/payment_method.dart';
|
||||
import 'package:pshared/models/payment/type.dart';
|
||||
import 'package:pshared/models/permissions/bound.dart';
|
||||
import 'package:pshared/models/storable.dart';
|
||||
|
||||
|
||||
extension PaymentMethodModelMapper on PaymentMethodModel {
|
||||
extension PaymentMethodMapper on PaymentMethod {
|
||||
PaymentMethodDTO toDTO() => PaymentMethodDTO(
|
||||
id: storable.id,
|
||||
createdAt: storable.createdAt,
|
||||
@@ -34,6 +35,9 @@ extension PaymentMethodModelMapper on PaymentMethodModel {
|
||||
type: paymentTypeToValue(type),
|
||||
data: _dataToJson(data),
|
||||
isArchived: isArchived,
|
||||
name: describable.name,
|
||||
description: describable.description,
|
||||
isMain: isMain,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _dataToJson(PaymentMethodData data) {
|
||||
@@ -53,7 +57,7 @@ extension PaymentMethodModelMapper on PaymentMethodModel {
|
||||
}
|
||||
|
||||
extension PaymentMethodDTOMapper on PaymentMethodDTO {
|
||||
PaymentMethodModel toDomain() => PaymentMethodModel(
|
||||
PaymentMethod toDomain() => PaymentMethod(
|
||||
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
|
||||
permissionBound: newPermissionBound(
|
||||
organizationBound: newOrganizationBound(organizationRef: organizationRef),
|
||||
@@ -62,6 +66,8 @@ extension PaymentMethodDTOMapper on PaymentMethodDTO {
|
||||
recipientRef: recipientRef,
|
||||
data: _dataToDomain(paymentTypeFromValue(type), data),
|
||||
isArchived: isArchived,
|
||||
describable: newDescribable(name: name, description: description),
|
||||
isMain: isMain,
|
||||
);
|
||||
|
||||
PaymentMethodData _dataToDomain(PaymentType paymentType, Map<String, dynamic> payload) {
|
||||
|
||||
@@ -2,13 +2,13 @@ import 'package:pshared/data/dto/recipient/recipient.dart';
|
||||
import 'package:pshared/models/describable.dart';
|
||||
import 'package:pshared/models/organization/bound.dart';
|
||||
import 'package:pshared/models/permissions/bound.dart';
|
||||
import 'package:pshared/models/recipient/recipient_model.dart';
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/models/recipient/status.dart';
|
||||
import 'package:pshared/models/recipient/type.dart';
|
||||
import 'package:pshared/models/storable.dart';
|
||||
|
||||
|
||||
extension RecipientModelMapper on RecipientModel {
|
||||
extension RecipientModelMapper on Recipient {
|
||||
RecipientDTO toDTO() => RecipientDTO(
|
||||
id: storable.id,
|
||||
createdAt: storable.createdAt,
|
||||
@@ -26,7 +26,7 @@ extension RecipientModelMapper on RecipientModel {
|
||||
}
|
||||
|
||||
extension RecipientDTOMapper on RecipientDTO {
|
||||
RecipientModel toDomain() => RecipientModel(
|
||||
Recipient toDomain() => Recipient(
|
||||
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
|
||||
permissionBound: newPermissionBound(
|
||||
organizationBound: newOrganizationBound(organizationRef: organizationRef),
|
||||
|
||||
@@ -19,5 +19,15 @@
|
||||
"operationStatusError": "Error",
|
||||
"@operationStatusError": {
|
||||
"description": "Label for the “error” operation status"
|
||||
},
|
||||
|
||||
"resourceLoadError": "Error while loading data. Try again",
|
||||
"@resourceLoadError": {
|
||||
"description": "Default message shown when data loading fails"
|
||||
},
|
||||
|
||||
"resourceEmpty": "Empty data",
|
||||
"@resourceEmpty": {
|
||||
"description": "Default message shown when no data is available"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,5 +19,15 @@
|
||||
"operationStatusError": "Ошибка",
|
||||
"@operationStatusError": {
|
||||
"description": "Label for the “error” operation status"
|
||||
},
|
||||
|
||||
"resourceLoadError": "Ошибка при загрузке данных. Попробуйте еще раз",
|
||||
"@resourceLoadError": {
|
||||
"description": "Default message shown when data loading fails"
|
||||
},
|
||||
|
||||
"resourceEmpty": "Нет данных",
|
||||
"@resourceEmpty": {
|
||||
"description": "Default message shown when no data is available"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,62 @@
|
||||
import 'package:pshared/models/describable.dart';
|
||||
import 'package:pshared/models/payment/type.dart';
|
||||
|
||||
import 'package:pshared/models/payment/methods/data.dart';
|
||||
import 'package:pshared/models/permissions/bound.dart';
|
||||
import 'package:pshared/models/permissions/bound/storable.dart';
|
||||
import 'package:pshared/models/storable.dart';
|
||||
|
||||
class PaymentMethod {
|
||||
PaymentMethod({
|
||||
required this.id,
|
||||
required this.label,
|
||||
required this.details,
|
||||
required this.type,
|
||||
this.isEnabled = true,
|
||||
|
||||
class PaymentMethod implements PermissionBoundStorable, Describable {
|
||||
final Storable storable;
|
||||
final PermissionBound permissionBound;
|
||||
final Describable describable;
|
||||
final String recipientRef;
|
||||
final PaymentMethodData data;
|
||||
final bool isArchived;
|
||||
final bool isMain;
|
||||
|
||||
const PaymentMethod({
|
||||
required this.storable,
|
||||
required this.permissionBound,
|
||||
required this.describable,
|
||||
required this.recipientRef,
|
||||
required this.data,
|
||||
this.isArchived = false,
|
||||
this.isMain = false,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String label;
|
||||
final String details;
|
||||
final PaymentType type;
|
||||
PaymentType get type => data.type;
|
||||
|
||||
bool isEnabled;
|
||||
bool isMain;
|
||||
@override
|
||||
String get id => storable.id;
|
||||
@override
|
||||
DateTime get createdAt => storable.createdAt;
|
||||
@override
|
||||
DateTime get updatedAt => storable.updatedAt;
|
||||
|
||||
@override
|
||||
String get organizationRef => permissionBound.organizationRef;
|
||||
@override
|
||||
String get permissionRef => permissionBound.permissionRef;
|
||||
|
||||
@override
|
||||
String get name => describable.name;
|
||||
@override
|
||||
String? get description => describable.description;
|
||||
|
||||
PaymentMethod copyWith({
|
||||
PaymentMethodData? data,
|
||||
bool? isArchived,
|
||||
bool? isMain,
|
||||
Describable? describable,
|
||||
}) => PaymentMethod(
|
||||
storable: storable,
|
||||
permissionBound: permissionBound,
|
||||
recipientRef: recipientRef,
|
||||
data: data ?? this.data,
|
||||
isArchived: isArchived ?? this.isArchived,
|
||||
isMain: isMain ?? this.isMain,
|
||||
describable: describable ?? this.describable,
|
||||
);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import 'package:pshared/models/payment/methods/data.dart';
|
||||
import 'package:pshared/models/payment/type.dart';
|
||||
import 'package:pshared/models/permissions/bound.dart';
|
||||
import 'package:pshared/models/storable.dart';
|
||||
|
||||
|
||||
class PaymentMethodModel implements PermissionBound, Storable {
|
||||
final Storable storable;
|
||||
final PermissionBound permissionBound;
|
||||
final String recipientRef;
|
||||
final PaymentMethodData data;
|
||||
final bool isArchived;
|
||||
|
||||
const PaymentMethodModel({
|
||||
required this.storable,
|
||||
required this.permissionBound,
|
||||
required this.recipientRef,
|
||||
required this.data,
|
||||
this.isArchived = false,
|
||||
});
|
||||
|
||||
PaymentType get type => data.type;
|
||||
|
||||
@override
|
||||
String get id => storable.id;
|
||||
@override
|
||||
DateTime get createdAt => storable.createdAt;
|
||||
@override
|
||||
DateTime get updatedAt => storable.updatedAt;
|
||||
|
||||
@override
|
||||
String get organizationRef => permissionBound.organizationRef;
|
||||
@override
|
||||
String get permissionRef => permissionBound.permissionRef;
|
||||
|
||||
PaymentMethodModel copyWith({
|
||||
PaymentMethodData? data,
|
||||
bool? isArchived,
|
||||
}) => PaymentMethodModel(
|
||||
storable: storable,
|
||||
permissionBound: permissionBound,
|
||||
recipientRef: recipientRef,
|
||||
data: data ?? this.data,
|
||||
isArchived: isArchived ?? this.isArchived,
|
||||
);
|
||||
}
|
||||
@@ -1,86 +1,97 @@
|
||||
import 'package:pshared/models/payment/methods/card.dart';
|
||||
import 'package:pshared/models/payment/methods/iban.dart';
|
||||
import 'package:pshared/models/payment/methods/crypto_address.dart';
|
||||
import 'package:pshared/models/payment/methods/russian_bank.dart';
|
||||
import 'package:pshared/models/payment/methods/wallet.dart';
|
||||
import 'package:pshared/models/describable.dart';
|
||||
import 'package:pshared/models/organization/bound.dart';
|
||||
import 'package:pshared/models/permissions/bound/describable.dart';
|
||||
import 'package:pshared/models/permissions/bound.dart';
|
||||
import 'package:pshared/models/recipient/status.dart';
|
||||
import 'package:pshared/models/recipient/type.dart';
|
||||
import 'package:pshared/models/storable.dart';
|
||||
|
||||
|
||||
class Recipient {
|
||||
final String? avatarUrl; // network URL / local asset
|
||||
final String name;
|
||||
class Recipient implements PermissionBoundStorableDescribable {
|
||||
final Storable storable;
|
||||
final PermissionBound permissionBound;
|
||||
final Describable describable;
|
||||
final String email;
|
||||
final String? avatarUrl;
|
||||
final RecipientStatus status;
|
||||
final RecipientType type;
|
||||
final CardPaymentMethod? card;
|
||||
final IbanPaymentMethod? iban;
|
||||
final RussianBankAccountPaymentMethod? bank;
|
||||
final WalletPaymentMethod? wallet;
|
||||
final CryptoAddressPaymentMethod? cryptoAddress;
|
||||
final bool isArchived;
|
||||
|
||||
const Recipient({
|
||||
this.avatarUrl,
|
||||
required this.name,
|
||||
required this.storable,
|
||||
required this.permissionBound,
|
||||
required this.describable,
|
||||
required this.email,
|
||||
required this.status,
|
||||
required this.type,
|
||||
this.card,
|
||||
this.iban,
|
||||
this.bank,
|
||||
this.wallet,
|
||||
this.cryptoAddress,
|
||||
this.avatarUrl,
|
||||
this.isArchived = false,
|
||||
});
|
||||
|
||||
/// Convenience factory for quickly creating mock recipients.
|
||||
factory Recipient.mock({
|
||||
required String name,
|
||||
required String email,
|
||||
required RecipientStatus status,
|
||||
required RecipientType type,
|
||||
CardPaymentMethod? card,
|
||||
IbanPaymentMethod? iban,
|
||||
RussianBankAccountPaymentMethod? bank,
|
||||
WalletPaymentMethod? wallet,
|
||||
CryptoAddressPaymentMethod? cryptoAddress,
|
||||
}) =>
|
||||
Recipient(
|
||||
avatarUrl: null,
|
||||
name: name,
|
||||
email: email,
|
||||
status: status,
|
||||
type: type,
|
||||
card: card,
|
||||
iban: iban,
|
||||
bank: bank,
|
||||
wallet: wallet,
|
||||
cryptoAddress: cryptoAddress,
|
||||
);
|
||||
@override
|
||||
String get id => storable.id;
|
||||
@override
|
||||
DateTime get createdAt => storable.createdAt;
|
||||
@override
|
||||
DateTime get updatedAt => storable.updatedAt;
|
||||
|
||||
@override
|
||||
String get organizationRef => permissionBound.organizationRef;
|
||||
@override
|
||||
String get permissionRef => permissionBound.permissionRef;
|
||||
|
||||
@override
|
||||
String get name => describable.name;
|
||||
@override
|
||||
String? get description => describable.description;
|
||||
|
||||
Recipient copyWith({
|
||||
Describable? describable,
|
||||
String? email,
|
||||
String? Function()? avatarUrl,
|
||||
RecipientStatus? status,
|
||||
RecipientType? type,
|
||||
bool? isArchived,
|
||||
}) => Recipient(
|
||||
storable: storable,
|
||||
permissionBound: permissionBound,
|
||||
describable: describableCopyWithOther(this.describable, describable),
|
||||
email: email ?? this.email,
|
||||
avatarUrl: avatarUrl != null ? avatarUrl() : this.avatarUrl,
|
||||
status: status ?? this.status,
|
||||
type: type ?? this.type,
|
||||
isArchived: isArchived ?? this.isArchived,
|
||||
);
|
||||
|
||||
// TODO: move search to backend
|
||||
bool matchesQuery(String q) {
|
||||
final searchable = [
|
||||
name,
|
||||
email,
|
||||
card?.pan,
|
||||
card?.firstName,
|
||||
card?.lastName,
|
||||
iban?.iban,
|
||||
iban?.accountHolder,
|
||||
iban?.bic,
|
||||
iban?.bankName,
|
||||
bank?.accountNumber,
|
||||
bank?.recipientName,
|
||||
bank?.inn,
|
||||
bank?.kpp,
|
||||
bank?.bankName,
|
||||
bank?.bik,
|
||||
bank?.correspondentAccount,
|
||||
wallet?.walletId,
|
||||
cryptoAddress?.address,
|
||||
cryptoAddress?.network,
|
||||
cryptoAddress?.destinationTag,
|
||||
];
|
||||
|
||||
return searchable.any((field) => field?.toLowerCase().contains(q) ?? false);
|
||||
return searchable.any((field) => field.toLowerCase().contains(q.toLowerCase()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Recipient newRecipient({
|
||||
required String organizationRef,
|
||||
required String email,
|
||||
required String name,
|
||||
required RecipientStatus status,
|
||||
required RecipientType type,
|
||||
String? description,
|
||||
String? avatarUrl,
|
||||
bool isArchived = false,
|
||||
}) =>
|
||||
Recipient(
|
||||
storable: newStorable(),
|
||||
permissionBound: newPermissionBound(organizationBound: newOrganizationBound(organizationRef: organizationRef)),
|
||||
describable: newDescribable(name: name, description: description),
|
||||
email: email,
|
||||
status: status,
|
||||
type: type,
|
||||
avatarUrl: avatarUrl,
|
||||
isArchived: isArchived,
|
||||
);
|
||||
@@ -1,64 +0,0 @@
|
||||
import 'package:pshared/models/describable.dart';
|
||||
import 'package:pshared/models/permissions/bound/describable.dart';
|
||||
import 'package:pshared/models/permissions/bound.dart';
|
||||
import 'package:pshared/models/recipient/status.dart';
|
||||
import 'package:pshared/models/recipient/type.dart';
|
||||
import 'package:pshared/models/storable.dart';
|
||||
|
||||
|
||||
class RecipientModel implements PermissionBoundStorableDescribable {
|
||||
final Storable storable;
|
||||
final PermissionBound permissionBound;
|
||||
final Describable describable;
|
||||
final String email;
|
||||
final String? avatarUrl;
|
||||
final RecipientStatus status;
|
||||
final RecipientType type;
|
||||
final bool isArchived;
|
||||
|
||||
const RecipientModel({
|
||||
required this.storable,
|
||||
required this.permissionBound,
|
||||
required this.describable,
|
||||
required this.email,
|
||||
required this.status,
|
||||
required this.type,
|
||||
this.avatarUrl,
|
||||
this.isArchived = false,
|
||||
});
|
||||
|
||||
@override
|
||||
String get id => storable.id;
|
||||
@override
|
||||
DateTime get createdAt => storable.createdAt;
|
||||
@override
|
||||
DateTime get updatedAt => storable.updatedAt;
|
||||
|
||||
@override
|
||||
String get organizationRef => permissionBound.organizationRef;
|
||||
@override
|
||||
String get permissionRef => permissionBound.permissionRef;
|
||||
|
||||
@override
|
||||
String get name => describable.name;
|
||||
@override
|
||||
String? get description => describable.description;
|
||||
|
||||
RecipientModel copyWith({
|
||||
Describable? describable,
|
||||
String? email,
|
||||
String? Function()? avatarUrl,
|
||||
RecipientStatus? status,
|
||||
RecipientType? type,
|
||||
bool? isArchived,
|
||||
}) => RecipientModel(
|
||||
storable: storable,
|
||||
permissionBound: permissionBound,
|
||||
describable: describableCopyWithOther(this.describable, describable),
|
||||
email: email ?? this.email,
|
||||
avatarUrl: avatarUrl != null ? avatarUrl() : this.avatarUrl,
|
||||
status: status ?? this.status,
|
||||
type: type ?? this.type,
|
||||
isArchived: isArchived ?? this.isArchived,
|
||||
);
|
||||
}
|
||||
59
frontend/pshared/lib/provider/recipient/pmethods.dart
Normal file
59
frontend/pshared/lib/provider/recipient/pmethods.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:pshared/data/mapper/payment/method.dart';
|
||||
import 'package:pshared/models/payment/methods/type.dart';
|
||||
import 'package:pshared/provider/organizations.dart';
|
||||
import 'package:pshared/provider/recipient/provider.dart';
|
||||
import 'package:pshared/provider/template.dart';
|
||||
import 'package:pshared/service/recipient/pmethods.dart';
|
||||
|
||||
|
||||
class PaymentMethodsProvider extends GenericProvider<PaymentMethod> {
|
||||
late OrganizationsProvider _organizations;
|
||||
late RecipientsProvider _recipients;
|
||||
|
||||
PaymentMethodsProvider() : super(service: PaymentMethodService.basicService);
|
||||
|
||||
List<PaymentMethod> get methods => List<PaymentMethod>.unmodifiable(items.toList()..sort((a, b) => a.storable.createdAt.compareTo(b.storable.createdAt)));
|
||||
|
||||
void updateProviders(OrganizationsProvider organizations, RecipientsProvider recipients) {
|
||||
_organizations = organizations;
|
||||
_recipients = recipients;
|
||||
if (_organizations.isOrganizationSet && (_recipients.currentObject != null)) {
|
||||
load(_organizations.current.id, _recipients.currentObject!.id);
|
||||
}
|
||||
}
|
||||
|
||||
// void reorderMethods(int oldIndex, int newIndex) {
|
||||
// if (newIndex > oldIndex) newIndex--;
|
||||
// final item = _methods.removeAt(oldIndex);
|
||||
// _methods.insert(newIndex, item);
|
||||
// notifyListeners();
|
||||
// }
|
||||
|
||||
PaymentMethod? get main => methods.firstWhereOrNull((m) => m.isMain);
|
||||
|
||||
Future<void> updateMethod(PaymentMethod method) async => update(method.toDTO().toJson());
|
||||
|
||||
Future<void> setArchivedMethod({
|
||||
required PaymentMethod method,
|
||||
required bool newIsArchived,
|
||||
}) async => setArchived(
|
||||
organizationRef: _organizations.current.id,
|
||||
objectRef: method.id,
|
||||
newIsArchived: newIsArchived,
|
||||
cascade: true,
|
||||
);
|
||||
|
||||
|
||||
Future<void> makeMain(PaymentMethod method) {
|
||||
// TODO: create separate backend method to manage main payment method
|
||||
final updates = <Future<void>>[];
|
||||
final currentMain = main;
|
||||
if (currentMain != null) {
|
||||
updates.add(updateMethod(currentMain.copyWith(isMain: false)));
|
||||
}
|
||||
updates.add(updateMethod(method.copyWith(isMain: true)));
|
||||
return Future.wait(updates).then((_) => null);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,27 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:pshared/models/recipient/filter.dart';
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/models/recipient/status.dart';
|
||||
|
||||
import 'package:pweb/services/recipient/recipient.dart';
|
||||
import 'package:pshared/provider/organizations.dart';
|
||||
import 'package:pshared/provider/template.dart';
|
||||
import 'package:pshared/service/recipient/service.dart';
|
||||
|
||||
|
||||
class RecipientProvider extends ChangeNotifier {
|
||||
final RecipientService _service;
|
||||
class RecipientsProvider extends GenericProvider<Recipient> {
|
||||
late OrganizationsProvider _organizations;
|
||||
|
||||
RecipientProvider(this._service);
|
||||
|
||||
List<Recipient> _recipients = [];
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
RecipientFilter _selectedFilter = RecipientFilter.all;
|
||||
String _query = '';
|
||||
|
||||
Recipient? _selectedRecipient;
|
||||
|
||||
List<Recipient> get recipients => _recipients;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
RecipientFilter get selectedFilter => _selectedFilter;
|
||||
String get query => _query;
|
||||
Recipient? get selectedRecipient => _selectedRecipient;
|
||||
|
||||
List<Recipient> get recipients => List<Recipient>.unmodifiable(items.toList()..sort((a, b) => a.storable.createdAt.compareTo(b.storable.createdAt)));
|
||||
|
||||
RecipientsProvider() : super(service: RecipientService.basicService);
|
||||
|
||||
List<Recipient> get filteredRecipients {
|
||||
List<Recipient> filtered = _recipients.where((r) {
|
||||
List<Recipient> filtered = recipients.where((r) {
|
||||
switch (_selectedFilter) {
|
||||
case RecipientFilter.ready:
|
||||
return r.status == RecipientStatus.ready;
|
||||
@@ -48,21 +41,6 @@ class RecipientProvider extends ChangeNotifier {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
Future<void> loadRecipients() async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_recipients = await _service.fetchRecipients();
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void setFilter(RecipientFilter filter) {
|
||||
_selectedFilter = filter;
|
||||
notifyListeners();
|
||||
@@ -73,8 +51,10 @@ class RecipientProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void selectRecipient(Recipient? recipient) {
|
||||
_selectedRecipient = recipient;
|
||||
notifyListeners();
|
||||
void updateProviders(OrganizationsProvider organizations) {
|
||||
_organizations = organizations;
|
||||
if (_organizations.isOrganizationSet) {
|
||||
load(_organizations.current.id, _organizations.current.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,21 +32,23 @@ List<T> mergeLists<T>({
|
||||
/// to manage state (loading, error, data) without re‑implementing service logic.
|
||||
class GenericProvider<T extends PermissionBoundStorable> extends ChangeNotifier {
|
||||
final BasicService<T> service;
|
||||
bool _isLoaded = false;
|
||||
|
||||
Resource<List<T>> _resource = Resource(data: []);
|
||||
Resource<List<T>> get resource => _resource;
|
||||
|
||||
List<T> get items => List.unmodifiable(_resource.data ?? []);
|
||||
bool get isLoading => _resource.isLoading;
|
||||
bool get isEmpty => items.isEmpty;
|
||||
Object? get error => _resource.error;
|
||||
bool get isReady => (error == null) && _isLoaded;
|
||||
|
||||
bool get isCurrentSet => _currentObjectRef != null;
|
||||
String? _currentObjectRef; // Stores the currently selected project ref
|
||||
T? get currentObject => _resource.data?.firstWhereOrNull(
|
||||
(object) => object.id == _currentObjectRef,
|
||||
);
|
||||
|
||||
T? getItemById(String id) => items.firstWhereOrNull((item) => item.id == id);
|
||||
T? getItemByRef(String id) => items.firstWhereOrNull((item) => item.id == id);
|
||||
|
||||
GenericProvider({required this.service});
|
||||
|
||||
@@ -67,11 +69,13 @@ class GenericProvider<T extends PermissionBoundStorable> extends ChangeNotifier
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loadFuture(Future<List<T>> future) async {
|
||||
Future<List<T>> loadFuture(Future<List<T>> future) async {
|
||||
_setResource(_resource.copyWith(isLoading: true));
|
||||
try {
|
||||
final list = await future;
|
||||
_isLoaded = true;
|
||||
_setResource(Resource(data: list, isLoading: false));
|
||||
return list;
|
||||
} catch (e) {
|
||||
_setResource(
|
||||
_resource.copyWith(isLoading: false, error: toException(e)),
|
||||
@@ -80,17 +84,30 @@ class GenericProvider<T extends PermissionBoundStorable> extends ChangeNotifier
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> load(String organizationRef, String? parentRef) async {
|
||||
Future<void> load(
|
||||
String organizationRef,
|
||||
String? parentRef, {
|
||||
int? limit,
|
||||
int? offset,
|
||||
bool? Function()? fetchArchived,
|
||||
}) async {
|
||||
if (parentRef != null) {
|
||||
return loadFuture(service.list(organizationRef, parentRef));
|
||||
await loadFuture(
|
||||
service.list(
|
||||
organizationRef,
|
||||
parentRef,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
fetchArchived: fetchArchived == null ? null : fetchArchived(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadItem(String itemRef) async {
|
||||
return loadFuture((() async => [await service.get(itemRef)])());
|
||||
await loadFuture((() async => [await service.get(itemRef)])());
|
||||
}
|
||||
|
||||
|
||||
List<T> merge(List<T> rhs) => mergeLists<T>(
|
||||
lhs: items,
|
||||
rhs: rhs,
|
||||
@@ -134,11 +151,47 @@ class GenericProvider<T extends PermissionBoundStorable> extends ChangeNotifier
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> delete(String objectRef) async {
|
||||
Future<void> delete(String objectRef, {Map<String, dynamic>? request}) async {
|
||||
_setResource(_resource.copyWith(isLoading: true));
|
||||
|
||||
try {
|
||||
await service.delete(objectRef);
|
||||
await service.delete(objectRef, request: request);
|
||||
if (_currentObjectRef == objectRef) {
|
||||
_currentObjectRef = null;
|
||||
}
|
||||
|
||||
_setResource(Resource(
|
||||
data: _resource.data?.where((p) => p.id != objectRef).toList(),
|
||||
isLoading: false,
|
||||
));
|
||||
} catch (e) {
|
||||
_setResource(Resource(data: _resource.data, isLoading: false, error: toException(e)));
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> toggleArchived(T item, bool currentState, {bool? cascade}) => setArchived(
|
||||
organizationRef: item.organizationRef,
|
||||
objectRef: item.id,
|
||||
newIsArchived: !currentState,
|
||||
cascade: cascade ?? true,
|
||||
);
|
||||
|
||||
Future<void> setArchived({
|
||||
required String organizationRef,
|
||||
required String objectRef,
|
||||
required bool newIsArchived,
|
||||
bool? cascade,
|
||||
}) async {
|
||||
_setResource(_resource.copyWith(isLoading: true));
|
||||
|
||||
try {
|
||||
await service.archive(
|
||||
organizationRef: organizationRef,
|
||||
objectRef: objectRef,
|
||||
newIsArchived: newIsArchived,
|
||||
cascade: cascade,
|
||||
);
|
||||
if (_currentObjectRef == objectRef) {
|
||||
_currentObjectRef = null;
|
||||
}
|
||||
@@ -154,11 +207,17 @@ class GenericProvider<T extends PermissionBoundStorable> extends ChangeNotifier
|
||||
}
|
||||
|
||||
bool setCurrentObject(String? objectRef) {
|
||||
if (_currentObjectRef == objectRef) {
|
||||
// No change, skip notification
|
||||
return true;
|
||||
}
|
||||
|
||||
if (objectRef == null) {
|
||||
_currentObjectRef = null;
|
||||
notifyListeners();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_resource.data?.any((p) => p.id == objectRef) ?? false) {
|
||||
_currentObjectRef = objectRef;
|
||||
notifyListeners();
|
||||
@@ -167,4 +226,5 @@ class GenericProvider<T extends PermissionBoundStorable> extends ChangeNotifier
|
||||
|
||||
return false; // Object not found
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
37
frontend/pshared/lib/service/recipient/pmethods.dart
Normal file
37
frontend/pshared/lib/service/recipient/pmethods.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'package:pshared/api/responses/payment_method.dart';
|
||||
import 'package:pshared/data/mapper/payment/method.dart';
|
||||
import 'package:pshared/models/payment/methods/type.dart';
|
||||
import 'package:pshared/service/services.dart';
|
||||
import 'package:pshared/service/template.dart';
|
||||
|
||||
|
||||
class PaymentMethodService {
|
||||
static const String _objectType = Services.paymentMethods;
|
||||
|
||||
static final BasicService<PaymentMethod> _basicService = BasicService<PaymentMethod>(
|
||||
objectType: _objectType,
|
||||
fromJson: (json) => PaymentMethodResponse.fromJson(json).paymentMethods.map((dto) => dto.toDomain()).toList(),
|
||||
);
|
||||
|
||||
static BasicService<PaymentMethod> get basicService => _basicService;
|
||||
|
||||
static Future<List<PaymentMethod>> list(String organizationRef, String recipientRef) async {
|
||||
return _basicService.list(organizationRef, recipientRef);
|
||||
}
|
||||
|
||||
static Future<PaymentMethod> get(String recipientRef) async {
|
||||
return _basicService.get(recipientRef);
|
||||
}
|
||||
|
||||
static Future<List<PaymentMethod>> create(String organizationRef, PaymentMethod paymentMethod) async {
|
||||
return _basicService.create(organizationRef, paymentMethod.toDTO().toJson());
|
||||
}
|
||||
|
||||
static Future<List<PaymentMethod>> update(PaymentMethod paymentMethod) async {
|
||||
return _basicService.update(paymentMethod.toDTO().toJson());
|
||||
}
|
||||
|
||||
static Future<List<PaymentMethod>> delete(PaymentMethod paymentMethod) async {
|
||||
return _basicService.delete(paymentMethod.storable.id);
|
||||
}
|
||||
}
|
||||
37
frontend/pshared/lib/service/recipient/service.dart
Normal file
37
frontend/pshared/lib/service/recipient/service.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'package:pshared/api/responses/recipient.dart';
|
||||
import 'package:pshared/data/mapper/recipient/recipient.dart';
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/service/services.dart';
|
||||
import 'package:pshared/service/template.dart';
|
||||
|
||||
|
||||
class RecipientService {
|
||||
static const String _objectType = Services.recipients;
|
||||
|
||||
static final BasicService<Recipient> _basicService = BasicService<Recipient>(
|
||||
objectType: _objectType,
|
||||
fromJson: (json) => RecipientResponse.fromJson(json).recipients.map((dto) => dto.toDomain()).toList(),
|
||||
);
|
||||
|
||||
static BasicService<Recipient> get basicService => _basicService;
|
||||
|
||||
static Future<List<Recipient>> list(String organizationRef, String _) async {
|
||||
return _basicService.list(organizationRef, organizationRef);
|
||||
}
|
||||
|
||||
static Future<Recipient> get(String recipientRef) async {
|
||||
return _basicService.get(recipientRef);
|
||||
}
|
||||
|
||||
static Future<List<Recipient>> create(String organizationRef, Recipient recipient) async {
|
||||
return _basicService.create(organizationRef, recipient.toDTO().toJson());
|
||||
}
|
||||
|
||||
static Future<List<Recipient>> update(Recipient recipient) async {
|
||||
return _basicService.update(recipient.toDTO().toJson());
|
||||
}
|
||||
|
||||
static Future<List<Recipient>> delete(Recipient recipient) async {
|
||||
return _basicService.delete(recipient.storable.id);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'package:pshared/service/authorization/service.dart';
|
||||
import 'package:pshared/utils/http/params.dart';
|
||||
|
||||
|
||||
class BasicService<T> {
|
||||
@@ -15,15 +16,26 @@ class BasicService<T> {
|
||||
required this.fromJson,
|
||||
}) : _objectType = objectType, _logger = Logger('service.$objectType');
|
||||
|
||||
Future<List<T>> list(String organizationRef, String parentRef) async {
|
||||
_logger.fine('Loading all objects');
|
||||
String _refLog(String ref) => ref.isEmpty ? '<not set>' : ref;
|
||||
|
||||
Future<List<T>> list(String organizationRef, String parentRef, {int? limit, int? offset, bool? fetchArchived}) async {
|
||||
_logger.fine('Loading all for organization ${_refLog(organizationRef)} and parent ${_refLog(organizationRef)} with: limit=${_formatParameter(limit)}, offset=${_formatParameter(offset)}, fetchArchived=${_formatParameter(fetchArchived)}...');
|
||||
|
||||
return _getObjects(
|
||||
AuthorizationService.getGETResponse(_objectType, '/list/$organizationRef/$parentRef'),
|
||||
AuthorizationService.getGETResponse(
|
||||
_objectType,
|
||||
paramsToUriString(
|
||||
path: '/list/$organizationRef/$parentRef',
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
fetchArchived: fetchArchived,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<T> get(String objectRef) async {
|
||||
_logger.fine('Loading object $objectRef');
|
||||
_logger.fine('Loading $_objectType $objectRef');
|
||||
final objects = await _getObjects(
|
||||
AuthorizationService.getGETResponse(_objectType, '/$objectRef'),
|
||||
);
|
||||
@@ -31,24 +43,36 @@ class BasicService<T> {
|
||||
}
|
||||
|
||||
Future<List<T>> create(String organizationRef, Map<String, dynamic> request) async {
|
||||
_logger.fine('Creating new object...');
|
||||
_logger.fine('Creating new...');
|
||||
return _getObjects(
|
||||
AuthorizationService.getPOSTResponse(_objectType, '/$organizationRef', request),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<T>> update(Map<String, dynamic> request) async {
|
||||
_logger.fine('Patching object...');
|
||||
_logger.fine('Patching...');
|
||||
return _getObjects(
|
||||
AuthorizationService.getPUTResponse(_objectType, '/', request,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<T>> delete(String objecRef) async {
|
||||
_logger.fine('Deleting object $objecRef');
|
||||
Future<List<T>> delete(String objecRef, {Map<String, dynamic>? request}) async {
|
||||
_logger.fine('Deleting $_objectType $objecRef');
|
||||
return _getObjects(
|
||||
AuthorizationService.getDELETEResponse(_objectType, '/$objecRef', {}),
|
||||
AuthorizationService.getDELETEResponse(_objectType, '/$objecRef', request ?? {}),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<T>> archive({
|
||||
required String organizationRef,
|
||||
required String objectRef,
|
||||
required bool newIsArchived,
|
||||
bool? cascade,
|
||||
}) async {
|
||||
_logger.fine('Setting new archive status $newIsArchived to $objectRef');
|
||||
return _getObjects(
|
||||
AuthorizationService.getGETResponse(_objectType, '/archive/$organizationRef/$objectRef?archived=$newIsArchived&cascade=${cascade ?? false}'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,8 +83,12 @@ class BasicService<T> {
|
||||
_logger.fine('Fetched ${objects.length} object(s)');
|
||||
return objects;
|
||||
} catch (e, stackTrace) {
|
||||
_logger.severe('Failed to fetch objects', e, stackTrace);
|
||||
_logger.severe('Failed to fetch', e, stackTrace);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
String _formatParameter(dynamic value) {
|
||||
return value?.toString() ?? '<not specified>';
|
||||
}
|
||||
}
|
||||
|
||||
38
frontend/pshared/lib/utils/http/params.dart
Normal file
38
frontend/pshared/lib/utils/http/params.dart
Normal file
@@ -0,0 +1,38 @@
|
||||
// Query parameter constants
|
||||
const String _limitParam = 'limit';
|
||||
const String _offsetParam = 'offset';
|
||||
const String _archivedParam = 'archived';
|
||||
|
||||
void _addIfNotNull(Map<String, String> params, String key, dynamic value) {
|
||||
if (value != null) {
|
||||
params[key] = value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Uri paramsToUri({
|
||||
required String path,
|
||||
int? limit,
|
||||
int? offset,
|
||||
bool? fetchArchived,
|
||||
Map<String, String>? params,
|
||||
}) {
|
||||
Map<String, String> queryParams = params ?? {};
|
||||
_addIfNotNull(queryParams, _limitParam, limit);
|
||||
_addIfNotNull(queryParams, _offsetParam, offset);
|
||||
_addIfNotNull(queryParams, _archivedParam, fetchArchived);
|
||||
|
||||
// Build URL with query parameters using Uri class
|
||||
return Uri(
|
||||
path: path,
|
||||
queryParameters: queryParams.isEmpty ? null : queryParams,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
String paramsToUriString({
|
||||
required String path,
|
||||
Map<String, String> queryParams = const {},
|
||||
int? limit,
|
||||
int? offset,
|
||||
bool? fetchArchived,
|
||||
}) => paramsToUri(path: path, limit: limit, offset: offset, fetchArchived: fetchArchived).toString();
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/generated/i18n/ps_localizations.dart';
|
||||
import 'package:pshared/provider/template.dart';
|
||||
|
||||
|
||||
@@ -20,9 +21,10 @@ class ResourceContainer<T extends GenericProvider> extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Consumer<T>(builder: (context, provider, _) {
|
||||
if (provider.isLoading) return loading ?? Center(child: CircularProgressIndicator());
|
||||
if (provider.error != null) return error ?? Text('Error while loading data. Try again');
|
||||
if (provider.isEmpty) return empty ?? Text('Empty data');
|
||||
final l10n = PSLocalizations.of(context)!;
|
||||
if (provider.isLoading) return loading ?? const Center(child: CircularProgressIndicator());
|
||||
if (provider.error != null) return error ?? Text(l10n.resourceLoadError);
|
||||
if (provider.items.isEmpty) return empty ?? Text(l10n.resourceEmpty);
|
||||
return builder(context, provider);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -311,6 +311,7 @@
|
||||
"paymentTypeBankAccount": "Russian Bank Account",
|
||||
"paymentTypeIban": "IBAN",
|
||||
"paymentTypeWallet": "Wallet",
|
||||
"paymentTypeCryptoAddress": "Crypto address",
|
||||
|
||||
"cardNumber": "Card Number",
|
||||
"enterCardNumber": "Enter the card number",
|
||||
|
||||
@@ -311,6 +311,7 @@
|
||||
"paymentTypeBankAccount": "Российский банковский счет",
|
||||
"paymentTypeIban": "IBAN",
|
||||
"paymentTypeWallet": "Кошелек",
|
||||
"paymentTypeCryptoAddress": "Крипто-адрес",
|
||||
|
||||
"cardNumber": "Номер карты",
|
||||
"enterCardNumber": "Введите номер карты",
|
||||
|
||||
@@ -12,6 +12,8 @@ import 'package:pshared/provider/locale.dart';
|
||||
import 'package:pshared/provider/permissions.dart';
|
||||
import 'package:pshared/provider/account.dart';
|
||||
import 'package:pshared/provider/organizations.dart';
|
||||
import 'package:pshared/provider/recipient/provider.dart';
|
||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||
|
||||
import 'package:pweb/app/app.dart';
|
||||
import 'package:pweb/app/timeago.dart';
|
||||
@@ -19,17 +21,13 @@ import 'package:pweb/providers/carousel.dart';
|
||||
import 'package:pweb/providers/mock_payment.dart';
|
||||
import 'package:pweb/providers/operatioins.dart';
|
||||
import 'package:pweb/providers/page_selector.dart';
|
||||
import 'package:pweb/providers/payment_methods.dart';
|
||||
import 'package:pweb/providers/recipient.dart';
|
||||
import 'package:pweb/providers/two_factor.dart';
|
||||
import 'package:pweb/providers/upload_history.dart';
|
||||
import 'package:pweb/providers/wallets.dart';
|
||||
import 'package:pweb/providers/wallet_transactions.dart';
|
||||
// import 'package:pweb/services/amplitude.dart';
|
||||
import 'package:pweb/services/operations.dart';
|
||||
import 'package:pweb/services/payments/payment_methods.dart';
|
||||
import 'package:pweb/services/payments/upload_history.dart';
|
||||
import 'package:pweb/services/recipient/recipient.dart';
|
||||
import 'package:pweb/services/payments/history.dart';
|
||||
import 'package:pweb/services/wallet_transactions.dart';
|
||||
import 'package:pweb/services/wallets.dart';
|
||||
|
||||
@@ -77,8 +75,13 @@ void main() async {
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => UploadHistoryProvider(service: MockUploadHistoryService())..load(),
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => PaymentMethodsProvider(service: MockPaymentMethodsService())..loadMethods(),
|
||||
ChangeNotifierProxyProvider<OrganizationsProvider, RecipientsProvider>(
|
||||
create: (_) => RecipientsProvider(),
|
||||
update: (context, organizations, provider) => provider!..updateProviders(organizations),
|
||||
),
|
||||
ChangeNotifierProxyProvider2<OrganizationsProvider, RecipientsProvider, PaymentMethodsProvider>(
|
||||
create: (_) => PaymentMethodsProvider(),
|
||||
update: (context, organizations, recipients, provider) => provider!..updateProviders(organizations, recipients),
|
||||
),
|
||||
ChangeNotifierProxyProvider<OrganizationsProvider, WalletsProvider>(
|
||||
create: (_) => WalletsProvider(ApiWalletsService()),
|
||||
@@ -90,18 +93,11 @@ void main() async {
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => MockPaymentProvider(),
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => RecipientProvider(RecipientService())..loadRecipients(),
|
||||
),
|
||||
|
||||
ChangeNotifierProxyProvider3<RecipientProvider, WalletsProvider, PaymentMethodsProvider, PageSelectorProvider>(
|
||||
ChangeNotifierProxyProvider3<RecipientsProvider, WalletsProvider, PaymentMethodsProvider, PageSelectorProvider>(
|
||||
create: (context) => PageSelectorProvider(),
|
||||
update: (context, recipientProv, walletsProv, methodsProv, previous) =>
|
||||
previous ?? PageSelectorProvider(
|
||||
recipientProvider: recipientProv,
|
||||
walletsProvider: walletsProv,
|
||||
methodsProvider: methodsProv,
|
||||
)..update(recipientProv, walletsProv, methodsProv),
|
||||
previous ?? PageSelectorProvider()..update(recipientProv, walletsProv, methodsProv),
|
||||
),
|
||||
|
||||
ChangeNotifierProvider(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/models/payment/methods/card.dart';
|
||||
import 'package:pshared/models/payment/methods/crypto_address.dart';
|
||||
import 'package:pshared/models/payment/methods/iban.dart';
|
||||
@@ -9,6 +11,7 @@ import 'package:pshared/models/payment/type.dart';
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/models/recipient/status.dart';
|
||||
import 'package:pshared/models/recipient/type.dart';
|
||||
import 'package:pshared/provider/organizations.dart';
|
||||
|
||||
import 'package:pweb/pages/address_book/form/view.dart';
|
||||
import 'package:pweb/services/amplitude.dart';
|
||||
@@ -38,8 +41,8 @@ class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
final r = widget.recipient;
|
||||
_nameCtrl = TextEditingController(text: r?.name ?? "");
|
||||
_emailCtrl = TextEditingController(text: r?.email ?? "");
|
||||
_nameCtrl = TextEditingController(text: r?.name ?? '');
|
||||
_emailCtrl = TextEditingController(text: r?.email ?? '');
|
||||
_type = r?.type ?? RecipientType.internal;
|
||||
_status = r?.status ?? RecipientStatus.ready;
|
||||
|
||||
@@ -50,7 +53,7 @@ class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
|
||||
if (r?.cryptoAddress != null) _methods[PaymentType.cryptoAddress] = r!.cryptoAddress;
|
||||
}
|
||||
|
||||
//TODO Change when registration is ready
|
||||
//TODO: Change when registration is ready
|
||||
void _save() {
|
||||
if (!_formKey.currentState!.validate() || _methods.isEmpty) {
|
||||
AmplitudeService.recipientAddCompleted(
|
||||
@@ -66,47 +69,41 @@ class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
|
||||
return;
|
||||
}
|
||||
|
||||
final recipient = Recipient(
|
||||
final recipient = newRecipient(
|
||||
name: _nameCtrl.text,
|
||||
email: _emailCtrl.text,
|
||||
type: _type,
|
||||
status: _status,
|
||||
avatarUrl: null,
|
||||
card: _methods[PaymentType.card] as CardPaymentMethod?,
|
||||
iban: _methods[PaymentType.iban] as IbanPaymentMethod?,
|
||||
wallet: _methods[PaymentType.wallet] as WalletPaymentMethod?,
|
||||
bank: _methods[PaymentType.bankAccount] as RussianBankAccountPaymentMethod?,
|
||||
cryptoAddress: _methods[PaymentType.cryptoAddress] as CryptoAddressPaymentMethod?,
|
||||
organizationRef: context.read<OrganizationsProvider>().current.id
|
||||
);
|
||||
|
||||
widget.onSaved?.call(recipient);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FormView(
|
||||
formKey: _formKey,
|
||||
nameCtrl: _nameCtrl,
|
||||
emailCtrl: _emailCtrl,
|
||||
type: _type,
|
||||
status: _status,
|
||||
methods: _methods,
|
||||
onTypeChanged: (t) => setState(() => _type = t),
|
||||
onStatusChanged: (s) => setState(() => _status = s),
|
||||
onMethodsChanged: (type, data) {
|
||||
setState(() {
|
||||
if (data != null) {
|
||||
_methods[type] = data;
|
||||
} else {
|
||||
_methods.remove(type);
|
||||
}
|
||||
});
|
||||
},
|
||||
onSave: _save,
|
||||
isEditing: widget.recipient != null,
|
||||
onBack: () {
|
||||
widget.onSaved?.call(null);
|
||||
},
|
||||
);
|
||||
}
|
||||
Widget build(BuildContext context) => FormView(
|
||||
formKey: _formKey,
|
||||
nameCtrl: _nameCtrl,
|
||||
emailCtrl: _emailCtrl,
|
||||
type: _type,
|
||||
status: _status,
|
||||
methods: _methods,
|
||||
onTypeChanged: (t) => setState(() => _type = t),
|
||||
onStatusChanged: (s) => setState(() => _status = s),
|
||||
onMethodsChanged: (type, data) {
|
||||
setState(() {
|
||||
if (data != null) {
|
||||
_methods[type] = data;
|
||||
} else {
|
||||
_methods.remove(type);
|
||||
}
|
||||
});
|
||||
},
|
||||
onSave: _save,
|
||||
isEditing: widget.recipient != null,
|
||||
onBack: () {
|
||||
widget.onSaved?.call(null);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,13 +11,11 @@ class PaymentInfoRow extends StatelessWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Text(label, style: Theme.of(context).textTheme.bodySmall),
|
||||
const SizedBox(width: 8),
|
||||
Text(value, style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
);
|
||||
}
|
||||
Widget build(BuildContext context) => Row(
|
||||
children: [
|
||||
Text(label, style: Theme.of(context).textTheme.bodySmall),
|
||||
const SizedBox(width: 8),
|
||||
Text(value, style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/payment/type.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/provider/organizations.dart';
|
||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||
import 'package:pshared/provider/recipient/provider.dart';
|
||||
|
||||
import 'package:pweb/pages/dashboard/payouts/single/adress_book/avatar.dart';
|
||||
import 'package:pweb/pages/dashboard/payouts/single/adress_book/long_list/info_row.dart';
|
||||
import 'package:pweb/utils/payment/label.dart';
|
||||
|
||||
|
||||
class RecipientItem extends StatelessWidget {
|
||||
final Recipient recipient;
|
||||
final VoidCallback onTap;
|
||||
|
||||
class RecipientItem extends StatefulWidget {
|
||||
static const double _horizontalPadding = 16.0;
|
||||
static const double _verticalPadding = 8.0;
|
||||
static const double _avatarRadius = 20;
|
||||
static const double _spacingWidth = 12;
|
||||
|
||||
final Recipient recipient;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const RecipientItem({
|
||||
super.key,
|
||||
required this.recipient,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
State<RecipientItem> createState() => _RecipientItemState();
|
||||
}
|
||||
|
||||
class _RecipientItemState extends State<RecipientItem> {
|
||||
late PaymentMethodsProvider _methodsProvider;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_methodsProvider = PaymentMethodsProvider();
|
||||
_methodsProvider.updateProviders(
|
||||
context.read<OrganizationsProvider>(),
|
||||
context.read<RecipientsProvider>(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!_methodsProvider.isReady) return const Center(child: CircularProgressIndicator());
|
||||
|
||||
final recipient = widget.recipient;
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
onTap: widget.onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: _horizontalPadding,
|
||||
vertical: _verticalPadding,
|
||||
horizontal: RecipientItem._horizontalPadding,
|
||||
vertical: RecipientItem._verticalPadding,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -42,43 +66,20 @@ class RecipientItem extends StatelessWidget {
|
||||
isVisible: false,
|
||||
name: recipient.name,
|
||||
avatarUrl: recipient.avatarUrl,
|
||||
avatarRadius: _avatarRadius,
|
||||
avatarRadius: RecipientItem._avatarRadius,
|
||||
nameStyle: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
title: Text(recipient.name),
|
||||
subtitle: Text(recipient.email),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: _spacingWidth),
|
||||
const SizedBox(width: RecipientItem._spacingWidth),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (recipient.bank?.accountNumber.isNotEmpty == true)
|
||||
PaymentInfoRow(
|
||||
label: getPaymentTypeLabel(context, PaymentType.bankAccount),
|
||||
value: recipient.bank!.accountNumber,
|
||||
),
|
||||
if (recipient.card?.pan.isNotEmpty == true)
|
||||
PaymentInfoRow(
|
||||
label: getPaymentTypeLabel(context, PaymentType.card),
|
||||
value: recipient.card!.pan,
|
||||
),
|
||||
if (recipient.iban?.iban.isNotEmpty == true)
|
||||
PaymentInfoRow(
|
||||
label: getPaymentTypeLabel(context, PaymentType.iban),
|
||||
value: recipient.iban!.iban,
|
||||
),
|
||||
if (recipient.wallet?.walletId.isNotEmpty == true)
|
||||
PaymentInfoRow(
|
||||
label: getPaymentTypeLabel(context, PaymentType.wallet),
|
||||
value: recipient.wallet!.walletId,
|
||||
),
|
||||
if (recipient.cryptoAddress?.address.isNotEmpty == true)
|
||||
PaymentInfoRow(
|
||||
label: getPaymentTypeLabel(context, PaymentType.cryptoAddress),
|
||||
value: recipient.cryptoAddress!.address,
|
||||
),
|
||||
],
|
||||
children: _methodsProvider.methods.map((m) => PaymentInfoRow(
|
||||
label: getPaymentTypeLabel(context, m.type),
|
||||
value: _displayString(m),
|
||||
)).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -3,11 +3,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/provider/recipient/provider.dart';
|
||||
|
||||
import 'package:pweb/pages/address_book/page/search.dart';
|
||||
import 'package:pweb/pages/dashboard/payouts/single/adress_book/long_list/long_list.dart';
|
||||
import 'package:pweb/pages/dashboard/payouts/single/adress_book/long_list/widget.dart';
|
||||
import 'package:pweb/pages/dashboard/payouts/single/adress_book/short_list.dart';
|
||||
import 'package:pweb/providers/recipient.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
@@ -39,7 +39,7 @@ class _AdressBookPayoutState extends State<AdressBookPayout> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final provider = context.read<RecipientProvider>();
|
||||
final provider = context.read<RecipientsProvider>();
|
||||
_searchController = TextEditingController(text: provider.query);
|
||||
|
||||
_searchController.addListener(() {
|
||||
@@ -57,7 +57,7 @@ class _AdressBookPayoutState extends State<AdressBookPayout> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
final provider = context.watch<RecipientProvider>();
|
||||
final provider = context.watch<RecipientsProvider>();
|
||||
|
||||
if (provider.isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
@@ -88,14 +88,14 @@ class _AdressBookPayoutState extends State<AdressBookPayout> {
|
||||
const SizedBox(height: _spacingBetween),
|
||||
Expanded(
|
||||
child: _isExpanded
|
||||
? LongListAdressBookPayout(
|
||||
filteredRecipients: provider.filteredRecipients,
|
||||
onSelected: widget.onSelected,
|
||||
)
|
||||
: ShortListAdressBookPayout(
|
||||
recipients: provider.recipients,
|
||||
onSelected: widget.onSelected,
|
||||
),
|
||||
? LongListAdressBookPayout(
|
||||
filteredRecipients: provider.filteredRecipients,
|
||||
onSelected: widget.onSelected,
|
||||
)
|
||||
: ShortListAdressBookPayout(
|
||||
recipients: provider.recipients,
|
||||
onSelected: widget.onSelected,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/payment/methods/type.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/models/payment/methods/type.dart';
|
||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||
|
||||
import 'package:pweb/providers/payment_methods.dart';
|
||||
import 'package:pweb/utils/payment/dropdown.dart';
|
||||
|
||||
|
||||
class PaymentMethodSelector extends StatelessWidget {
|
||||
final PaymentMethodsProvider methodsProvider;
|
||||
final ValueChanged<PaymentMethod> onMethodChanged;
|
||||
|
||||
const PaymentMethodSelector({
|
||||
super.key,
|
||||
required this.methodsProvider,
|
||||
required this.onMethodChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PaymentMethodDropdown(
|
||||
methods: methodsProvider.methods,
|
||||
initialValue: methodsProvider.selectedMethod,
|
||||
onChanged: onMethodChanged,
|
||||
);
|
||||
}
|
||||
Widget build(BuildContext context) => Consumer<PaymentMethodsProvider>(builder:(context, provider, _) => PaymentMethodDropdown(
|
||||
methods: provider.methods,
|
||||
initialValue: provider.currentObject,
|
||||
onChanged: onMethodChanged,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||
import 'package:pshared/provider/recipient/provider.dart';
|
||||
|
||||
import 'package:pweb/providers/payment_flow_provider.dart';
|
||||
import 'package:pweb/pages/payment_methods/widgets/payment_page_body.dart';
|
||||
import 'package:pweb/providers/page_selector.dart';
|
||||
import 'package:pweb/providers/payment_methods.dart';
|
||||
import 'package:pweb/providers/recipient.dart';
|
||||
|
||||
|
||||
class PaymentPage extends StatefulWidget {
|
||||
@@ -49,30 +49,22 @@ class _PaymentPageState extends State<PaymentPage> {
|
||||
void _initializePaymentPage() {
|
||||
final pageSelector = context.read<PageSelectorProvider>();
|
||||
final methodsProvider = context.read<PaymentMethodsProvider>();
|
||||
final recipientProvider = context.read<RecipientProvider>();
|
||||
final recipientProvider = context.read<RecipientsProvider>();
|
||||
|
||||
pageSelector.handleWalletAutoSelection();
|
||||
|
||||
if (methodsProvider.methods.isEmpty && !methodsProvider.isLoading) {
|
||||
methodsProvider.loadMethods();
|
||||
}
|
||||
|
||||
if (recipientProvider.recipients.isEmpty && !recipientProvider.isLoading) {
|
||||
recipientProvider.loadRecipients();
|
||||
}
|
||||
|
||||
_flowProvider.syncWithSelector(pageSelector);
|
||||
}
|
||||
|
||||
void _handleSearchChanged(String query) {
|
||||
context.read<RecipientProvider>().setQuery(query);
|
||||
context.read<RecipientsProvider>().setQuery(query);
|
||||
}
|
||||
|
||||
void _handleRecipientSelected(Recipient recipient) {
|
||||
final pageSelector = context.read<PageSelectorProvider>();
|
||||
final recipientProvider = context.read<RecipientProvider>();
|
||||
final recipientProvider = context.read<RecipientsProvider>();
|
||||
|
||||
recipientProvider.selectRecipient(recipient);
|
||||
recipientProvider.setCurrentObject(recipient.id);
|
||||
pageSelector.selectRecipient(recipient);
|
||||
_flowProvider.reset(pageSelector);
|
||||
_clearSearchField();
|
||||
@@ -80,9 +72,9 @@ class _PaymentPageState extends State<PaymentPage> {
|
||||
|
||||
void _handleRecipientCleared() {
|
||||
final pageSelector = context.read<PageSelectorProvider>();
|
||||
final recipientProvider = context.read<RecipientProvider>();
|
||||
final recipientProvider = context.read<RecipientsProvider>();
|
||||
|
||||
recipientProvider.selectRecipient(null);
|
||||
recipientProvider.setCurrentObject(null);
|
||||
pageSelector.selectRecipient(null);
|
||||
_flowProvider.reset(pageSelector);
|
||||
_clearSearchField();
|
||||
@@ -91,7 +83,7 @@ class _PaymentPageState extends State<PaymentPage> {
|
||||
void _clearSearchField() {
|
||||
_searchController.clear();
|
||||
_searchFocusNode.unfocus();
|
||||
context.read<RecipientProvider>().setQuery('');
|
||||
context.read<RecipientsProvider>().setQuery('');
|
||||
}
|
||||
|
||||
void _handleSendPayment() {
|
||||
|
||||
@@ -31,7 +31,7 @@ class PaymentMethodTile extends StatelessWidget {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Opacity(
|
||||
opacity: method.isEnabled ? 1 : 0.5,
|
||||
opacity: method.isArchived ? 1 : 0.5,
|
||||
child: Card(
|
||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||
elevation: 0,
|
||||
@@ -41,11 +41,12 @@ class PaymentMethodTile extends StatelessWidget {
|
||||
onTap: makeMain,
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(child: Text(method.label)),
|
||||
Text(
|
||||
method.details,
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
Expanded(child: Text(method.name)),
|
||||
if (method.description != null)
|
||||
Text(
|
||||
method.description!,
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: Row(
|
||||
@@ -73,12 +74,10 @@ class PaymentMethodTile extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEnabledSwitch() {
|
||||
return Switch.adaptive(
|
||||
value: method.isEnabled,
|
||||
onChanged: toggleEnabled,
|
||||
);
|
||||
}
|
||||
Widget _buildEnabledSwitch() => Switch.adaptive(
|
||||
value: method.isArchived,
|
||||
onChanged: toggleEnabled,
|
||||
);
|
||||
|
||||
Widget _buildPopupMenu(AppLocalizations l10n) {
|
||||
return PopupMenuButton<String>(
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||
import 'package:pshared/provider/recipient/provider.dart';
|
||||
|
||||
import 'package:pweb/pages/payment_methods/header.dart';
|
||||
import 'package:pweb/pages/payment_methods/method_selector.dart';
|
||||
@@ -12,9 +15,8 @@ import 'package:pweb/pages/payment_methods/widgets/recipient_section.dart';
|
||||
import 'package:pweb/pages/payment_methods/widgets/section_title.dart';
|
||||
import 'package:pweb/providers/page_selector.dart';
|
||||
import 'package:pweb/providers/payment_flow_provider.dart';
|
||||
import 'package:pweb/providers/payment_methods.dart';
|
||||
import 'package:pweb/providers/recipient.dart';
|
||||
import 'package:pweb/utils/dimensions.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
|
||||
@@ -43,7 +45,7 @@ class PaymentPageBody extends StatelessWidget {
|
||||
final dimensions = AppDimensions();
|
||||
final pageSelector = context.watch<PageSelectorProvider>();
|
||||
final methodsProvider = context.watch<PaymentMethodsProvider>();
|
||||
final recipientProvider = context.watch<RecipientProvider>();
|
||||
final recipientProvider = context.watch<RecipientsProvider>();
|
||||
final flowProvider = context.watch<PaymentFlowProvider>();
|
||||
final recipient = pageSelector.selectedRecipient;
|
||||
final loc = AppLocalizations.of(context)!;
|
||||
@@ -79,8 +81,7 @@ class PaymentPageBody extends StatelessWidget {
|
||||
SectionTitle(loc.sourceOfFunds),
|
||||
SizedBox(height: dimensions.paddingSmall),
|
||||
PaymentMethodSelector(
|
||||
methodsProvider: methodsProvider,
|
||||
onMethodChanged: methodsProvider.selectMethod,
|
||||
onMethodChanged: (m) => methodsProvider.setCurrentObject(m.id),
|
||||
),
|
||||
SizedBox(height: dimensions.paddingXLarge),
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/provider/recipient/provider.dart';
|
||||
|
||||
import 'package:pweb/pages/address_book/page/search.dart';
|
||||
import 'package:pweb/pages/payment_methods/widgets/card.dart';
|
||||
import 'package:pweb/pages/payment_methods/widgets/search.dart';
|
||||
import 'package:pweb/pages/payment_methods/widgets/section_title.dart';
|
||||
import 'package:pweb/providers/recipient.dart';
|
||||
import 'package:pweb/utils/dimensions.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
@@ -15,7 +15,7 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
class RecipientSection extends StatelessWidget {
|
||||
final Recipient? recipient;
|
||||
final AppDimensions dimensions;
|
||||
final RecipientProvider recipientProvider;
|
||||
final RecipientsProvider recipientProvider;
|
||||
final TextEditingController searchController;
|
||||
final FocusNode searchFocusNode;
|
||||
final ValueChanged<String> onSearchChanged;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/provider/recipient/provider.dart';
|
||||
|
||||
import 'package:pweb/providers/recipient.dart';
|
||||
import 'package:pweb/utils/dimensions.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
@@ -10,7 +10,7 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
class RecipientSearchResults extends StatelessWidget {
|
||||
final AppDimensions dimensions;
|
||||
final RecipientProvider recipientProvider;
|
||||
final RecipientsProvider recipientProvider;
|
||||
final ValueChanged<Recipient> onRecipientSelected;
|
||||
|
||||
const RecipientSearchResults({
|
||||
|
||||
@@ -4,8 +4,8 @@ import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/models/payment/methods/data.dart';
|
||||
import 'package:pshared/models/payment/methods/type.dart';
|
||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||
|
||||
import 'package:pweb/providers/payment_methods.dart';
|
||||
import 'package:pweb/pages/payment_methods/add/widget.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
@@ -16,18 +16,10 @@ class PaymentConfigController {
|
||||
|
||||
PaymentConfigController(this.context);
|
||||
|
||||
void loadMethods() {
|
||||
context.read<PaymentMethodsProvider>().loadMethods();
|
||||
}
|
||||
|
||||
Future<void> addMethod() async {
|
||||
final methodsProvider = context.read<PaymentMethodsProvider>();
|
||||
await showDialog<PaymentMethodData>(
|
||||
context: context,
|
||||
builder: (_) => const AddPaymentMethodDialog(),
|
||||
);
|
||||
methodsProvider.loadMethods();
|
||||
}
|
||||
Future<void> addMethod() async => showDialog<PaymentMethodData>(
|
||||
context: context,
|
||||
builder: (_) => const AddPaymentMethodDialog(),
|
||||
);
|
||||
|
||||
Future<void> editMethod(PaymentMethod method) async {
|
||||
// TODO: implement edit functionality
|
||||
@@ -55,12 +47,12 @@ class PaymentConfigController {
|
||||
);
|
||||
|
||||
if (confirmed == true) {
|
||||
methodsProvider.deleteMethod(method);
|
||||
methodsProvider.delete(method.id);
|
||||
}
|
||||
}
|
||||
|
||||
void toggleEnabled(PaymentMethod method, bool value) {
|
||||
context.read<PaymentMethodsProvider>().toggleEnabled(method, value);
|
||||
context.read<PaymentMethodsProvider>().setArchivedMethod(method: method, newIsArchived: value);
|
||||
}
|
||||
|
||||
void makeMain(PaymentMethod method) {
|
||||
@@ -68,6 +60,7 @@ class PaymentConfigController {
|
||||
}
|
||||
|
||||
void reorder(int oldIndex, int newIndex) {
|
||||
context.read<PaymentMethodsProvider>().reorderMethods(oldIndex, newIndex);
|
||||
// TODO: rimplement on top of Indexable
|
||||
// context.read<PaymentMethodsProvider>().reorderMethods(oldIndex, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||
|
||||
import 'package:pweb/pages/payment_methods/title.dart';
|
||||
import 'package:pweb/pages/payout_page/methods/controller.dart';
|
||||
import 'package:pweb/providers/payment_methods.dart';
|
||||
|
||||
|
||||
class PaymentConfigList extends StatelessWidget {
|
||||
|
||||
@@ -20,7 +20,6 @@ class _MethodsWidgetState extends State<MethodsWidget> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = PaymentConfigController(context);
|
||||
controller.loadMethods();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:pweb/models/wallet.dart';
|
||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||
|
||||
import 'package:pweb/models/wallet.dart';
|
||||
import 'package:pweb/pages/payout_page/methods/widget.dart';
|
||||
import 'package:pweb/pages/payout_page/wallet/wigets.dart';
|
||||
import 'package:pweb/providers/payment_methods.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
|
||||
@@ -1,41 +1,40 @@
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'package:pshared/models/payment/methods/type.dart';
|
||||
import 'package:pshared/models/payment/type.dart';
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||
import 'package:pshared/provider/recipient/provider.dart';
|
||||
|
||||
import 'package:pweb/models/wallet.dart';
|
||||
import 'package:pweb/providers/payment_methods.dart';
|
||||
import 'package:pweb/providers/wallets.dart';
|
||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
||||
import 'package:pweb/services/amplitude.dart';
|
||||
import 'package:pweb/providers/recipient.dart';
|
||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
||||
|
||||
|
||||
class PageSelectorProvider extends ChangeNotifier {
|
||||
static final _logger = Logger('provider.page_selector');
|
||||
|
||||
PayoutDestination _selected = PayoutDestination.dashboard;
|
||||
PaymentType? _type;
|
||||
bool _cameFromRecipientList = false;
|
||||
PayoutDestination? _previousDestination;
|
||||
|
||||
RecipientProvider? recipientProvider;
|
||||
WalletsProvider? walletsProvider;
|
||||
PaymentMethodsProvider? methodsProvider;
|
||||
late RecipientsProvider recipientProvider;
|
||||
late WalletsProvider walletsProvider;
|
||||
late PaymentMethodsProvider methodsProvider;
|
||||
|
||||
PayoutDestination get selected => _selected;
|
||||
PaymentType? get type => _type;
|
||||
bool get cameFromRecipientList => _cameFromRecipientList;
|
||||
|
||||
PageSelectorProvider({
|
||||
this.recipientProvider,
|
||||
this.walletsProvider,
|
||||
this.methodsProvider,
|
||||
});
|
||||
PageSelectorProvider();
|
||||
|
||||
void update(
|
||||
RecipientProvider recipientProv,
|
||||
RecipientsProvider recipientProv,
|
||||
WalletsProvider walletsProv,
|
||||
PaymentMethodsProvider methodsProv,
|
||||
) {
|
||||
@@ -50,44 +49,30 @@ class PageSelectorProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void selectRecipient(Recipient? recipient, {bool fromList = false}) {
|
||||
if (recipientProvider != null) {
|
||||
recipientProvider!.selectRecipient(recipient);
|
||||
_cameFromRecipientList = fromList;
|
||||
_setPreviousDestination();
|
||||
_selected = PayoutDestination.payment;
|
||||
notifyListeners();
|
||||
} else {
|
||||
debugPrint("RecipientProvider is null — cannot select recipient");
|
||||
}
|
||||
recipientProvider.setCurrentObject(recipient?.id);
|
||||
_cameFromRecipientList = fromList;
|
||||
_setPreviousDestination();
|
||||
_selected = PayoutDestination.payment;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void editRecipient(Recipient? recipient, {bool fromList = false}) {
|
||||
if (recipientProvider != null) {
|
||||
recipientProvider!.selectRecipient(recipient);
|
||||
_cameFromRecipientList = fromList;
|
||||
_selected = PayoutDestination.addrecipient;
|
||||
notifyListeners();
|
||||
} else {
|
||||
debugPrint("RecipientProvider is null — cannot select recipient");
|
||||
}
|
||||
recipientProvider.setCurrentObject(recipient?.id);
|
||||
_cameFromRecipientList = fromList;
|
||||
_selected = PayoutDestination.addrecipient;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void goToAddRecipient() {
|
||||
if (recipientProvider != null) {
|
||||
AmplitudeService.recipientAddStarted();
|
||||
recipientProvider!.selectRecipient(null);
|
||||
_selected = PayoutDestination.addrecipient;
|
||||
_cameFromRecipientList = false;
|
||||
notifyListeners();
|
||||
} else {
|
||||
debugPrint("RecipientProvider is null — cannot go to add recipient");
|
||||
}
|
||||
AmplitudeService.recipientAddStarted();
|
||||
recipientProvider!.setCurrentObject(null);
|
||||
_selected = PayoutDestination.addrecipient;
|
||||
_cameFromRecipientList = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void startPaymentWithoutRecipient(PaymentType type) {
|
||||
if (recipientProvider != null) {
|
||||
recipientProvider!.selectRecipient(null);
|
||||
}
|
||||
recipientProvider.setCurrentObject(null);
|
||||
_type = type;
|
||||
_cameFromRecipientList = false;
|
||||
_setPreviousDestination();
|
||||
@@ -111,13 +96,9 @@ class PageSelectorProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void selectWallet(Wallet wallet) {
|
||||
if (walletsProvider != null) {
|
||||
walletsProvider!.selectWallet(wallet);
|
||||
_selected = PayoutDestination.editwallet;
|
||||
notifyListeners();
|
||||
} else {
|
||||
debugPrint("WalletsProvider is null — cannot select wallet");
|
||||
}
|
||||
walletsProvider.selectWallet(wallet);
|
||||
_selected = PayoutDestination.editwallet;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void startPaymentFromWallet(Wallet wallet) {
|
||||
@@ -129,26 +110,26 @@ class PageSelectorProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
PaymentMethod? getPaymentMethodForWallet(Wallet wallet) {
|
||||
if (methodsProvider == null || methodsProvider!.methods.isEmpty) {
|
||||
if (methodsProvider.methods.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return methodsProvider!.methods.firstWhereOrNull(
|
||||
(method) => method.type == PaymentType.wallet &&
|
||||
method.details.contains(wallet.walletUserID)
|
||||
(method.description?.contains(wallet.walletUserID) ?? false),
|
||||
);
|
||||
}
|
||||
|
||||
Map<PaymentType, Object> getAvailablePaymentTypes() {
|
||||
final recipient = selectedRecipient;
|
||||
if (recipient == null) return {};
|
||||
if ((recipient == null) || !methodsProvider.isReady) return {};
|
||||
|
||||
final methodsForRecipient = methodsProvider.methods.where(
|
||||
(method) => !method.isArchived && method.recipientRef == recipient.id,
|
||||
);
|
||||
|
||||
return {
|
||||
if (recipient.card != null) PaymentType.card: recipient.card!,
|
||||
if (recipient.iban != null) PaymentType.iban: recipient.iban!,
|
||||
if (recipient.wallet != null) PaymentType.wallet: recipient.wallet!,
|
||||
if (recipient.bank != null) PaymentType.bankAccount: recipient.bank!,
|
||||
if (recipient.cryptoAddress != null) PaymentType.cryptoAddress: recipient.cryptoAddress!,
|
||||
for (final method in methodsForRecipient) method.type: method.data,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -158,11 +139,11 @@ class PageSelectorProvider extends ChangeNotifier {
|
||||
|
||||
if (availableTypes.containsKey(currentType)) {
|
||||
return currentType;
|
||||
} else if (availableTypes.isNotEmpty) {
|
||||
return availableTypes.keys.first;
|
||||
} else {
|
||||
return PaymentType.bankAccount;
|
||||
}
|
||||
if (availableTypes.isNotEmpty) {
|
||||
return availableTypes.keys.first;
|
||||
}
|
||||
return PaymentType.bankAccount;
|
||||
}
|
||||
|
||||
bool shouldShowPaymentForm() {
|
||||
@@ -170,11 +151,11 @@ class PageSelectorProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void handleWalletAutoSelection() {
|
||||
if (selectedWallet != null && methodsProvider != null) {
|
||||
if (selectedWallet != null) {
|
||||
final wallet = selectedWallet!;
|
||||
final matchingMethod = getPaymentMethodForWallet(wallet);
|
||||
if (matchingMethod != null) {
|
||||
methodsProvider!.selectMethod(matchingMethod);
|
||||
methodsProvider.setCurrentObject(matchingMethod.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,6 +166,6 @@ class PageSelectorProvider extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Recipient? get selectedRecipient => recipientProvider?.selectedRecipient;
|
||||
Wallet? get selectedWallet => walletsProvider?.selectedWallet;
|
||||
Recipient? get selectedRecipient => recipientProvider.currentObject;
|
||||
Wallet? get selectedWallet => walletsProvider.selectedWallet;
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/payment/methods/type.dart';
|
||||
|
||||
import 'package:pweb/services/payments/payment_methods.dart';
|
||||
|
||||
|
||||
class PaymentMethodsProvider extends ChangeNotifier {
|
||||
final PaymentMethodsService service;
|
||||
|
||||
List<PaymentMethod> _methods = [];
|
||||
PaymentMethod? _selectedMethod;
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
|
||||
PaymentMethodsProvider({required this.service});
|
||||
|
||||
List<PaymentMethod> get methods => _methods;
|
||||
PaymentMethod? get selectedMethod => _selectedMethod;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
|
||||
Future<void> loadMethods() async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_methods = await service.fetchMethods();
|
||||
_selectedMethod = _methods.firstWhere((m) => m.isMain, orElse: () => _methods.first);
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void selectMethod(PaymentMethod method) {
|
||||
_selectedMethod = method;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void deleteMethod(PaymentMethod method) {
|
||||
_methods.remove(method);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void reorderMethods(int oldIndex, int newIndex) {
|
||||
if (newIndex > oldIndex) newIndex--;
|
||||
final item = _methods.removeAt(oldIndex);
|
||||
_methods.insert(newIndex, item);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void toggleEnabled(PaymentMethod method, bool value) {
|
||||
method.isEnabled = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void makeMain(PaymentMethod method) {
|
||||
for (final m in _methods) {
|
||||
m.isMain = false;
|
||||
}
|
||||
method.isMain = true;
|
||||
selectMethod(method);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'package:pshared/models/payment/upload_history_item.dart';
|
||||
|
||||
import 'package:pweb/providers/template.dart';
|
||||
import 'package:pweb/services/payments/upload_history.dart';
|
||||
import 'package:pweb/services/payments/history.dart';
|
||||
|
||||
|
||||
class UploadHistoryProvider extends FutureProviderTemplate<List<UploadHistoryItem>> {
|
||||
UploadHistoryProvider({required UploadHistoryService service})
|
||||
: super(loader: service.fetchHistory);
|
||||
UploadHistoryProvider({required UploadHistoryService service}) : super(loader: service.fetchHistory);
|
||||
}
|
||||
48
frontend/pweb/lib/services/payments/methods.dart
Normal file
48
frontend/pweb/lib/services/payments/methods.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
// import 'package:pshared/models/payment/methods/type.dart';
|
||||
|
||||
// import 'package:pshared/models/payment/type.dart';
|
||||
|
||||
|
||||
// abstract class PaymentMethodsService {
|
||||
// Future<List<PaymentMethod>> fetchMethods();
|
||||
// }
|
||||
|
||||
// class MockPaymentMethodsService implements PaymentMethodsService {
|
||||
// @override
|
||||
// Future<List<PaymentMethod>> fetchMethods() async {
|
||||
// await Future.delayed(const Duration(milliseconds: 200));
|
||||
// return [
|
||||
// PaymentMethod(
|
||||
// id: '1',
|
||||
// label: 'My account',
|
||||
// details: '•••4567',
|
||||
// type: PaymentType.bankAccount,
|
||||
// isMain: true,
|
||||
// ),
|
||||
// PaymentMethod(
|
||||
// id: '2',
|
||||
// label: 'Euro IBAN',
|
||||
// details: 'DE•• •••8901',
|
||||
// type: PaymentType.iban,
|
||||
// ),
|
||||
// PaymentMethod(
|
||||
// id: '3',
|
||||
// label: 'Wallet',
|
||||
// details: 'WA‑12345667',
|
||||
// type: PaymentType.wallet,
|
||||
// ),
|
||||
// PaymentMethod(
|
||||
// id: '4',
|
||||
// label: 'Wallet',
|
||||
// details: 'WA-76654321',
|
||||
// type: PaymentType.wallet,
|
||||
// ),
|
||||
// PaymentMethod(
|
||||
// id: '5',
|
||||
// label: 'Credit Card',
|
||||
// details: '21•• •••• •••• 8901',
|
||||
// type: PaymentType.card,
|
||||
// ),
|
||||
// ];
|
||||
// }
|
||||
// }
|
||||
@@ -1,48 +0,0 @@
|
||||
import 'package:pshared/models/payment/methods/type.dart';
|
||||
|
||||
import 'package:pshared/models/payment/type.dart';
|
||||
|
||||
|
||||
abstract class PaymentMethodsService {
|
||||
Future<List<PaymentMethod>> fetchMethods();
|
||||
}
|
||||
|
||||
class MockPaymentMethodsService implements PaymentMethodsService {
|
||||
@override
|
||||
Future<List<PaymentMethod>> fetchMethods() async {
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
return [
|
||||
PaymentMethod(
|
||||
id: '1',
|
||||
label: 'My account',
|
||||
details: '•••4567',
|
||||
type: PaymentType.bankAccount,
|
||||
isMain: true,
|
||||
),
|
||||
PaymentMethod(
|
||||
id: '2',
|
||||
label: 'Euro IBAN',
|
||||
details: 'DE•• •••8901',
|
||||
type: PaymentType.iban,
|
||||
),
|
||||
PaymentMethod(
|
||||
id: '3',
|
||||
label: 'Wallet',
|
||||
details: 'WA‑12345667',
|
||||
type: PaymentType.wallet,
|
||||
),
|
||||
PaymentMethod(
|
||||
id: '4',
|
||||
label: 'Wallet',
|
||||
details: 'WA-76654321',
|
||||
type: PaymentType.wallet,
|
||||
),
|
||||
PaymentMethod(
|
||||
id: '5',
|
||||
label: 'Credit Card',
|
||||
details: '21•• •••• •••• 8901',
|
||||
type: PaymentType.card,
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,88 +1,88 @@
|
||||
import 'package:pshared/models/recipient/recipient.dart';
|
||||
import 'package:pshared/models/payment/methods/card.dart';
|
||||
import 'package:pshared/models/payment/methods/iban.dart';
|
||||
import 'package:pshared/models/payment/methods/russian_bank.dart';
|
||||
import 'package:pshared/models/payment/methods/wallet.dart';
|
||||
import 'package:pshared/models/recipient/status.dart';
|
||||
import 'package:pshared/models/recipient/type.dart';
|
||||
// import 'package:pshared/models/recipient/recipient.dart';
|
||||
// import 'package:pshared/models/payment/methods/card.dart';
|
||||
// import 'package:pshared/models/payment/methods/iban.dart';
|
||||
// import 'package:pshared/models/payment/methods/russian_bank.dart';
|
||||
// import 'package:pshared/models/payment/methods/wallet.dart';
|
||||
// import 'package:pshared/models/recipient/status.dart';
|
||||
// import 'package:pshared/models/recipient/type.dart';
|
||||
|
||||
|
||||
class RecipientService {
|
||||
Future<List<Recipient>> fetchRecipients() async {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
return RecipientMockData.all;
|
||||
}
|
||||
}
|
||||
// class RecipientService {
|
||||
// Future<List<Recipient>> fetchRecipients() async {
|
||||
// await Future.delayed(const Duration(milliseconds: 500));
|
||||
// return RecipientMockData.all;
|
||||
// }
|
||||
// }
|
||||
|
||||
class RecipientMockData {
|
||||
static List<Recipient> get all => [
|
||||
Recipient.mock(
|
||||
name: 'Alice Johnson',
|
||||
email: 'alice@example.com',
|
||||
status: RecipientStatus.ready,
|
||||
type: RecipientType.internal,
|
||||
card: CardPaymentMethod(
|
||||
pan: '1213',
|
||||
firstName: 'Alice',
|
||||
lastName: 'Johnson',
|
||||
),
|
||||
),
|
||||
Recipient.mock(
|
||||
name: 'Bob & Co Ltd.',
|
||||
email: 'payout@bobco.com',
|
||||
status: RecipientStatus.registered,
|
||||
type: RecipientType.external,
|
||||
card: CardPaymentMethod(
|
||||
pan: '4343',
|
||||
firstName: 'Bob',
|
||||
lastName: 'Co',
|
||||
),
|
||||
iban: IbanPaymentMethod(
|
||||
iban: 'FR7630***890189',
|
||||
accountHolder: 'Bob & Co Ltd.',
|
||||
bic: 'AGRIFRPP',
|
||||
bankName: 'Credit Agricole',
|
||||
),
|
||||
wallet: WalletPaymentMethod(walletId: '8932231'),
|
||||
),
|
||||
Recipient.mock(
|
||||
name: 'Carlos Kline',
|
||||
email: 'carlos@acme.org',
|
||||
status: RecipientStatus.notRegistered,
|
||||
type: RecipientType.internal,
|
||||
wallet: WalletPaymentMethod(walletId: '7723490'),
|
||||
),
|
||||
Recipient.mock(
|
||||
name: 'Delta Outsourcing GmbH',
|
||||
email: 'finance@delta-os.de',
|
||||
status: RecipientStatus.registered,
|
||||
type: RecipientType.external,
|
||||
card: CardPaymentMethod(
|
||||
pan: '9988',
|
||||
firstName: 'Delta',
|
||||
lastName: 'GmbH',
|
||||
),
|
||||
iban: IbanPaymentMethod(
|
||||
iban: 'DE4450***324931',
|
||||
accountHolder: 'Delta Outsourcing GmbH',
|
||||
bic: 'INGDDEFFXXX',
|
||||
bankName: 'ING',
|
||||
),
|
||||
),
|
||||
Recipient.mock(
|
||||
name: 'Erin Patel',
|
||||
email: 'erin@labster.io',
|
||||
status: RecipientStatus.ready,
|
||||
type: RecipientType.internal,
|
||||
bank: RussianBankAccountPaymentMethod(
|
||||
accountNumber: '4081***7654',
|
||||
recipientName: 'Erin Patel',
|
||||
inn: '7812012345',
|
||||
kpp: '781201001',
|
||||
bankName: 'Alfa-Bank',
|
||||
bik: '044525593',
|
||||
correspondentAccount: '30101810200000000593',
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
// class RecipientMockData {
|
||||
// static List<Recipient> get all => [
|
||||
// Recipient.mock(
|
||||
// name: 'Alice Johnson',
|
||||
// email: 'alice@example.com',
|
||||
// status: RecipientStatus.ready,
|
||||
// type: RecipientType.internal,
|
||||
// card: CardPaymentMethod(
|
||||
// pan: '1213',
|
||||
// firstName: 'Alice',
|
||||
// lastName: 'Johnson',
|
||||
// ),
|
||||
// ),
|
||||
// Recipient.mock(
|
||||
// name: 'Bob & Co Ltd.',
|
||||
// email: 'payout@bobco.com',
|
||||
// status: RecipientStatus.registered,
|
||||
// type: RecipientType.external,
|
||||
// card: CardPaymentMethod(
|
||||
// pan: '4343',
|
||||
// firstName: 'Bob',
|
||||
// lastName: 'Co',
|
||||
// ),
|
||||
// iban: IbanPaymentMethod(
|
||||
// iban: 'FR7630***890189',
|
||||
// accountHolder: 'Bob & Co Ltd.',
|
||||
// bic: 'AGRIFRPP',
|
||||
// bankName: 'Credit Agricole',
|
||||
// ),
|
||||
// wallet: WalletPaymentMethod(walletId: '8932231'),
|
||||
// ),
|
||||
// Recipient.mock(
|
||||
// name: 'Carlos Kline',
|
||||
// email: 'carlos@acme.org',
|
||||
// status: RecipientStatus.notRegistered,
|
||||
// type: RecipientType.internal,
|
||||
// wallet: WalletPaymentMethod(walletId: '7723490'),
|
||||
// ),
|
||||
// Recipient.mock(
|
||||
// name: 'Delta Outsourcing GmbH',
|
||||
// email: 'finance@delta-os.de',
|
||||
// status: RecipientStatus.registered,
|
||||
// type: RecipientType.external,
|
||||
// card: CardPaymentMethod(
|
||||
// pan: '9988',
|
||||
// firstName: 'Delta',
|
||||
// lastName: 'GmbH',
|
||||
// ),
|
||||
// iban: IbanPaymentMethod(
|
||||
// iban: 'DE4450***324931',
|
||||
// accountHolder: 'Delta Outsourcing GmbH',
|
||||
// bic: 'INGDDEFFXXX',
|
||||
// bankName: 'ING',
|
||||
// ),
|
||||
// ),
|
||||
// Recipient.mock(
|
||||
// name: 'Erin Patel',
|
||||
// email: 'erin@labster.io',
|
||||
// status: RecipientStatus.ready,
|
||||
// type: RecipientType.internal,
|
||||
// bank: RussianBankAccountPaymentMethod(
|
||||
// accountNumber: '4081***7654',
|
||||
// recipientName: 'Erin Patel',
|
||||
// inn: '7812012345',
|
||||
// kpp: '781201001',
|
||||
// bankName: 'Alfa-Bank',
|
||||
// bik: '044525593',
|
||||
// correspondentAccount: '30101810200000000593',
|
||||
// ),
|
||||
// ),
|
||||
// ];
|
||||
// }
|
||||
|
||||
@@ -48,7 +48,7 @@ class _PaymentMethodDropdownState extends State<PaymentMethodDropdown> {
|
||||
children: [
|
||||
Icon(iconForPaymentType(method.type), size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Text('${method.label} (${method.details})'),
|
||||
Text('${method.name}' + (method.description == null ? '' : ' (${method.description!})')),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -12,6 +12,6 @@ String getPaymentTypeLabel(BuildContext context, PaymentType type) {
|
||||
PaymentType.bankAccount => l10n.paymentTypeBankAccount,
|
||||
PaymentType.iban => l10n.paymentTypeIban,
|
||||
PaymentType.wallet => l10n.paymentTypeWallet,
|
||||
PaymentType.cryptoAddress => 'Crypto address',
|
||||
PaymentType.cryptoAddress => l10n.paymentTypeCryptoAddress,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class PageSelector extends StatelessWidget {
|
||||
break;
|
||||
|
||||
case PayoutDestination.addrecipient:
|
||||
final recipient = provider.recipientProvider?.selectedRecipient;
|
||||
final recipient = provider.recipientProvider.currentObject;
|
||||
content = AdressBookRecipientForm(
|
||||
recipient: recipient,
|
||||
onSaved: (_) => provider.selectPage(PayoutDestination.recipients),
|
||||
@@ -100,7 +100,7 @@ class PageSelector extends StatelessWidget {
|
||||
break;
|
||||
|
||||
case PayoutDestination.editwallet:
|
||||
final wallet = provider.walletsProvider?.selectedWallet;
|
||||
final wallet = provider.walletsProvider.selectedWallet;
|
||||
content = wallet != null
|
||||
? WalletEditPage(
|
||||
onBack: provider.goBackFromWalletEdit,
|
||||
|
||||
Reference in New Issue
Block a user