diff --git a/ci/prod/.env.runtime b/ci/prod/.env.runtime index 2a89dc1..6abcbce 100644 --- a/ci/prod/.env.runtime +++ b/ci/prod/.env.runtime @@ -67,7 +67,7 @@ MNTX_GATEWAY_COMPOSE_PROJECT=sendico-mntx-gateway MNTX_GATEWAY_SERVICE_NAME=sendico_mntx_gateway MNTX_GATEWAY_GRPC_PORT=50075 MNTX_GATEWAY_METRICS_PORT=9404 -MNTX_GATEWAY_HTTP_PORT=8080 +MNTX_GATEWAY_HTTP_PORT=8084 MONETIX_BASE_URL=https://api.txflux.com diff --git a/frontend/pshared/lib/models/payment/methods/data.dart b/frontend/pshared/lib/models/payment/methods/data.dart index 78780bb..7f80efe 100644 --- a/frontend/pshared/lib/models/payment/methods/data.dart +++ b/frontend/pshared/lib/models/payment/methods/data.dart @@ -3,4 +3,6 @@ import 'package:pshared/models/payment/type.dart'; abstract class PaymentMethodData { PaymentType get type; -} \ No newline at end of file +} + +typedef MethodMap = Map; \ No newline at end of file diff --git a/frontend/pshared/lib/provider/recipient/pmethods.dart b/frontend/pshared/lib/provider/recipient/pmethods.dart index 03f0f70..23dff1f 100644 --- a/frontend/pshared/lib/provider/recipient/pmethods.dart +++ b/frontend/pshared/lib/provider/recipient/pmethods.dart @@ -1,7 +1,12 @@ import 'package:collection/collection.dart'; import 'package:pshared/data/mapper/payment/method.dart'; +import 'package:pshared/models/describable.dart'; +import 'package:pshared/models/organization/bound.dart'; +import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/payment/methods/type.dart'; +import 'package:pshared/models/permissions/bound.dart'; +import 'package:pshared/models/storable.dart'; import 'package:pshared/provider/organizations.dart'; import 'package:pshared/provider/recipient/provider.dart'; import 'package:pshared/provider/template.dart'; @@ -49,6 +54,23 @@ class PaymentMethodsProvider extends GenericProvider { cascade: true, ); + Future create({ + required String reacipientRef, + required PaymentMethodData data, + required String name, + }) => createObject( + _organizations.current.id, + PaymentMethod( + storable: newStorable(), + permissionBound: newPermissionBound( + organizationBound: newOrganizationBound(organizationRef: _organizations.current.id), + ), + recipientRef: reacipientRef, + data: data, + describable: newDescribable(name: name), + ).toDTO().toJson(), + ); + Future makeMain(PaymentMethod method) { // TODO: create separate backend method to manage main payment method diff --git a/frontend/pshared/lib/provider/recipient/provider.dart b/frontend/pshared/lib/provider/recipient/provider.dart index 6249291..80fdb36 100644 --- a/frontend/pshared/lib/provider/recipient/provider.dart +++ b/frontend/pshared/lib/provider/recipient/provider.dart @@ -1,7 +1,9 @@ +import 'package:pshared/data/mapper/organization/bound.dart'; import 'package:pshared/models/recipient/filter.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:pshared/provider/template.dart'; import 'package:pshared/service/recipient/service.dart'; @@ -51,6 +53,20 @@ class RecipientsProvider extends GenericProvider { notifyListeners(); } + Future create({ + required String name, + required String email, + }) async => createObject( + _organizations.current.id, + newRecipient( + organizationRef: _organizations.current.id, + email: email, + name: name, + status: RecipientStatus.ready, + type: RecipientType.internal, + ).toDTO().toJson(), + ); + void updateProviders(OrganizationsProvider organizations) { _organizations = organizations; if (_organizations.isOrganizationSet) { diff --git a/frontend/pweb/lib/l10n/en.arb b/frontend/pweb/lib/l10n/en.arb index 6f271e3..dd35628 100644 --- a/frontend/pweb/lib/l10n/en.arb +++ b/frontend/pweb/lib/l10n/en.arb @@ -429,6 +429,11 @@ "companyName": "Name of your company", "companynameRequired": "Company name required", + "errorSaveRecipient": "Failed to save recipient", + "@errorSaveRecipient": { + "description": "Error message displayed when saving a recipient fails" + }, + "errorSignUp": "Error occured while signing up, try again later", "companyDescription": "Company Description", "companyDescriptionHint": "Describe any of the fields of the Company's business", diff --git a/frontend/pweb/lib/l10n/ru.arb b/frontend/pweb/lib/l10n/ru.arb index 8c41917..f9ca079 100644 --- a/frontend/pweb/lib/l10n/ru.arb +++ b/frontend/pweb/lib/l10n/ru.arb @@ -429,6 +429,11 @@ "companyName": "Название вашей компании", "companynameRequired": "Необходимо указать название компании", + "errorSaveRecipient": "Не удалось сохранить получателя", + "@errorSaveRecipient": { + "description": "Сообщение об ошибке при неудачном сохранении получателя" + }, + "errorSignUp": "Произошла ошибка при регистрации, попробуйте позже", "companyDescription": "Описание компании", "companyDescriptionHint": "Опишите любую из сфер деятельности компании", diff --git a/frontend/pweb/lib/pages/address_book/form/method_tile.dart b/frontend/pweb/lib/pages/address_book/form/method_tile.dart index bb5ca51..fb1de11 100644 --- a/frontend/pweb/lib/pages/address_book/form/method_tile.dart +++ b/frontend/pweb/lib/pages/address_book/form/method_tile.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/payment/type.dart'; @@ -9,8 +10,8 @@ import 'package:pweb/pages/payment_methods/icon.dart'; class AdressBookPaymentMethodTile extends StatefulWidget { final PaymentType type; final String title; - final Map methods; - final ValueChanged onChanged; + final MethodMap methods; + final ValueChanged onChanged; final double spacingM; final double spacingS; diff --git a/frontend/pweb/lib/pages/address_book/form/page.dart b/frontend/pweb/lib/pages/address_book/form/page.dart index 7a835cb..29a151e 100644 --- a/frontend/pweb/lib/pages/address_book/form/page.dart +++ b/frontend/pweb/lib/pages/address_book/form/page.dart @@ -2,17 +2,22 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:pshared/models/payment/methods/data.dart'; 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:pshared/provider/recipient/pmethods.dart'; +import 'package:pshared/provider/recipient/provider.dart'; import 'package:pweb/pages/address_book/form/view.dart'; // import 'package:pweb/services/amplitude.dart'; +import 'package:pweb/utils/error/snackbar.dart'; import 'package:pweb/generated/i18n/app_localizations.dart'; +import 'package:pweb/utils/payment/label.dart'; +import 'package:pweb/utils/snackbar.dart'; class AdressBookRecipientForm extends StatefulWidget { @@ -31,7 +36,7 @@ class _AdressBookRecipientFormState extends State { late TextEditingController _emailCtrl; RecipientType _type = RecipientType.internal; RecipientStatus _status = RecipientStatus.ready; - final Map _methods = {}; + final MethodMap _methods = {}; late PaymentMethodsProvider _methodsProvider; Future _loadMethods() async { @@ -63,31 +68,43 @@ class _AdressBookRecipientFormState extends State { _loadMethods(); } + Future _doSave() async { + final recipients = context.read(); + final methods = context.read(); + final recipient = await recipients.create( + name: _nameCtrl.text, + email: _emailCtrl.text, + ); + if (methods.currentObject == null) return recipient; + final data = methods.currentObject!; + await methods.create( + reacipientRef: recipient.id, + name: getPaymentTypeLabel(context, data.type), + data: data.data, + ); + return recipient; + } + //TODO: Change when registration is ready - void _save() { + Future _save() async { if (!_formKey.currentState!.validate() || _methods.isEmpty) { - // AmplitudeService.recipientAddCompleted( - // _type, - // _status, - // _methods.keys.toSet(), - // ); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(AppLocalizations.of(context)!.recipientFormRule), - ), - ); + notifyUser(context, AppLocalizations.of(context)!.errorSaveRecipient); return; } - final recipient = newRecipient( - name: _nameCtrl.text, - email: _emailCtrl.text, - type: _type, - status: _status, - avatarUrl: null, - organizationRef: context.read().current.id + // AmplitudeService.recipientAddCompleted( + // _type, + // _status, + // _methods.keys.toSet(), + // ); + final recipient = await executeActionWithNotification( + context: context, + action: _doSave, + errorMessage: AppLocalizations.of(context)!.errorSaveRecipient, + successMessage: AppLocalizations.of(context)!.recipientFormRule, ); + widget.onSaved?.call(recipient); } diff --git a/frontend/pweb/lib/pages/address_book/form/view.dart b/frontend/pweb/lib/pages/address_book/form/view.dart index c49a8f5..cda3213 100644 --- a/frontend/pweb/lib/pages/address_book/form/view.dart +++ b/frontend/pweb/lib/pages/address_book/form/view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/payment/type.dart'; import 'package:pshared/models/recipient/status.dart'; @@ -20,10 +21,10 @@ class FormView extends StatelessWidget { final TextEditingController emailCtrl; final RecipientType type; final RecipientStatus status; - final Map methods; + final MethodMap methods; final ValueChanged onTypeChanged; final ValueChanged onStatusChanged; - final void Function(PaymentType, Object?) onMethodsChanged; + final void Function(PaymentType, PaymentMethodData?) onMethodsChanged; final VoidCallback onSave; final bool isEditing; final VoidCallback onBack; diff --git a/frontend/pweb/lib/pages/address_book/form/widgets/button.dart b/frontend/pweb/lib/pages/address_book/form/widgets/button.dart index 540d023..d0ad1b3 100644 --- a/frontend/pweb/lib/pages/address_book/form/widgets/button.dart +++ b/frontend/pweb/lib/pages/address_book/form/widgets/button.dart @@ -42,10 +42,10 @@ class SaveButton extends StatelessWidget { child: Text( text ?? AppLocalizations.of(context)!.saveRecipient, style: textStyle ?? - theme.textTheme.labelLarge?.copyWith( - color: theme.colorScheme.onPrimary, - fontWeight: FontWeight.w600, - ), + theme.textTheme.labelLarge?.copyWith( + color: theme.colorScheme.onPrimary, + fontWeight: FontWeight.w600, + ), ), ), ), diff --git a/frontend/pweb/lib/pages/dashboard/payouts/single/form/details.dart b/frontend/pweb/lib/pages/dashboard/payouts/single/form/details.dart index 52cd185..a4223ed 100644 --- a/frontend/pweb/lib/pages/dashboard/payouts/single/form/details.dart +++ b/frontend/pweb/lib/pages/dashboard/payouts/single/form/details.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/payment/type.dart'; import 'package:pweb/pages/payment_methods/form.dart'; @@ -12,7 +13,7 @@ class PaymentDetailsSection extends StatelessWidget { final bool isEditable; final VoidCallback? onToggle; final PaymentType? selectedType; - final Object? data; + final PaymentMethodData? data; const PaymentDetailsSection({ super.key, diff --git a/frontend/pweb/lib/pages/payment_methods/form.dart b/frontend/pweb/lib/pages/payment_methods/form.dart index 92f2314..daa78cd 100644 --- a/frontend/pweb/lib/pages/payment_methods/form.dart +++ b/frontend/pweb/lib/pages/payment_methods/form.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.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/wallet.dart'; @@ -16,8 +17,8 @@ import 'package:pweb/pages/payment_methods/add/wallet.dart'; class PaymentMethodForm extends StatelessWidget { final PaymentType? selectedType; - final ValueChanged onChanged; - final Object? initialData; + final ValueChanged onChanged; + final PaymentMethodData? initialData; final bool isEditable; const PaymentMethodForm({ diff --git a/frontend/pweb/lib/pages/payment_methods/widgets/payment_info_section.dart b/frontend/pweb/lib/pages/payment_methods/widgets/payment_info_section.dart index 5ba43bd..e1af21a 100644 --- a/frontend/pweb/lib/pages/payment_methods/widgets/payment_info_section.dart +++ b/frontend/pweb/lib/pages/payment_methods/widgets/payment_info_section.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/payment/type.dart'; import 'package:pshared/models/recipient/recipient.dart'; @@ -31,9 +32,9 @@ class PaymentInfoSection extends StatelessWidget { Widget build(BuildContext context) { final loc = AppLocalizations.of(context)!; final hasRecipient = recipient != null; - final availableTypes = hasRecipient + final MethodMap availableTypes = hasRecipient ? pageSelector.getAvailablePaymentTypes() - : {for (final type in PaymentType.values) type: type}; + : {for (final type in PaymentType.values) type: null}; if (hasRecipient && availableTypes.isEmpty) { return Text(loc.recipientNoPaymentDetails); diff --git a/frontend/pweb/lib/providers/page_selector.dart b/frontend/pweb/lib/providers/page_selector.dart index 06c86c3..20216f5 100644 --- a/frontend/pweb/lib/providers/page_selector.dart +++ b/frontend/pweb/lib/providers/page_selector.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; +import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/payment/methods/type.dart'; import 'package:pshared/models/payment/type.dart'; @@ -117,7 +118,7 @@ class PageSelectorProvider extends ChangeNotifier { ); } - Map getAvailablePaymentTypes() { + MethodMap getAvailablePaymentTypes() { final recipient = selectedRecipient; if ((recipient == null) || !methodsProvider.isReady) return {}; diff --git a/frontend/pweb/lib/providers/payment_flow_provider.dart b/frontend/pweb/lib/providers/payment_flow_provider.dart index d7001af..a464f62 100644 --- a/frontend/pweb/lib/providers/payment_flow_provider.dart +++ b/frontend/pweb/lib/providers/payment_flow_provider.dart @@ -1,4 +1,5 @@ import 'package:flutter/foundation.dart'; +import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/payment/type.dart'; import 'package:pshared/models/recipient/recipient.dart'; @@ -8,14 +9,14 @@ import 'package:pweb/providers/page_selector.dart'; class PaymentFlowProvider extends ChangeNotifier { PaymentType _selectedType; - Object? _manualPaymentData; + PaymentMethodData? _manualPaymentData; PaymentFlowProvider({ required PaymentType initialType, }) : _selectedType = initialType; PaymentType get selectedType => _selectedType; - Object? get manualPaymentData => _manualPaymentData; + PaymentMethodData? get manualPaymentData => _manualPaymentData; void syncWithSelector(PageSelectorProvider selector) { final recipient = selector.selectedRecipient; @@ -53,7 +54,7 @@ class PaymentFlowProvider extends ChangeNotifier { notifyListeners(); } - void setManualPaymentData(Object? data) { + void setManualPaymentData(PaymentMethodData? data) { _manualPaymentData = data; notifyListeners(); } diff --git a/frontend/pweb/lib/utils/error/snackbar.dart b/frontend/pweb/lib/utils/error/snackbar.dart index ca98a9d..e75c993 100644 --- a/frontend/pweb/lib/utils/error/snackbar.dart +++ b/frontend/pweb/lib/utils/error/snackbar.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:pshared/utils/snackbar.dart'; import 'package:pweb/utils/error/handler.dart'; import 'package:pweb/widgets/error/content.dart'; @@ -52,13 +53,18 @@ Future executeActionWithNotification({ required BuildContext context, required Future Function() action, required String errorMessage, + String? successMessage, int delaySeconds = 3, }) async { final scaffoldMessenger = ScaffoldMessenger.of(context); final localizations = AppLocalizations.of(context)!; try { - return await action(); + final result = await action(); + if (successMessage != null) { + notifyUser(context, successMessage, delaySeconds: delaySeconds); + } + return result; } catch (e) { // Report the error using your existing notifier. notifyUserOfErrorX( diff --git a/frontend/pweb/lib/utils/payment/selector_type.dart b/frontend/pweb/lib/utils/payment/selector_type.dart index 0680012..ff0e5f1 100644 --- a/frontend/pweb/lib/utils/payment/selector_type.dart +++ b/frontend/pweb/lib/utils/payment/selector_type.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/payment/type.dart'; import 'package:pweb/utils/payment/label.dart'; class PaymentTypeSelector extends StatelessWidget { - final Map availableTypes; + final MethodMap availableTypes; final PaymentType selectedType; final ValueChanged onSelected;