Merge pull request 'devKA' (#4) from devKA into main
All checks were successful
ci/woodpecker/push/fx/1 Pipeline was successful
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx/2 Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful

Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
2025-11-14 04:09:19 +00:00
503 changed files with 25498 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

16
frontend/pshared/.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
build/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
*.g.dart
lib/generated
untranslated.txt
.flutter-plugins
.flutter-plugins-dependencies
devtools_options.yaml

View File

@@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

View File

@@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/developing-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View File

@@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

Binary file not shown.

View File

@@ -0,0 +1,7 @@
arb-dir: lib/l10n
template-arb-file: ps_en.arb
output-dir: lib/generated/i18n
output-localization-file: ps_localizations.dart
output-class: PSLocalizations
synthetic-package: false
untranslated-messages-file: untranslated.txt

View File

@@ -0,0 +1,2 @@
class AuthorizationFailed implements Exception {
}

View File

@@ -0,0 +1,2 @@
class ErrorFailedToReadImage implements Exception {
}

View File

@@ -0,0 +1,2 @@
class ErrorUnauthorized implements Exception {
}

View File

@@ -0,0 +1,2 @@
class ErrorUploadFailed implements Exception {
}

View File

@@ -0,0 +1,18 @@
import 'package:json_annotation/json_annotation.dart';
part 'change_password.g.dart';
@JsonSerializable(explicitToJson: true)
class ChangePassword {
@JsonKey(name: 'old')
final String oldPassword;
@JsonKey(name: 'new')
final String newPassword;
const ChangePassword({required this.oldPassword, required this.newPassword});
factory ChangePassword.fromJson(Map<String, dynamic> json) => _$ChangePasswordFromJson(json);
Map<String, dynamic> toJson() => _$ChangePasswordToJson(this);
}

View File

@@ -0,0 +1,18 @@
import 'package:json_annotation/json_annotation.dart';
part 'change_role.g.dart';
@JsonSerializable(explicitToJson: true)
class ChangeRole {
final String accountRef;
final String newRoleDescriptionRef;
const ChangeRole({
required this.accountRef,
required this.newRoleDescriptionRef,
});
factory ChangeRole.fromJson(Map<String, dynamic> json) => _$ChangeRoleFromJson(json);
Map<String, dynamic> toJson() => _$ChangeRoleToJson(this);
}

View File

@@ -0,0 +1,15 @@
import 'package:json_annotation/json_annotation.dart';
part 'file_upload.g.dart';
@JsonSerializable(explicitToJson: true)
class FileUpload {
final String objRef;
const FileUpload({ required this.objRef });
factory FileUpload.fromJson(Map<String, dynamic> json) => _$FileUploadFromJson(json);
Map<String, dynamic> toJson() => _$FileUploadToJson(this);
}

View File

@@ -0,0 +1,24 @@
import 'package:json_annotation/json_annotation.dart';
part 'login.g.dart';
@JsonSerializable(explicitToJson: true)
class LoginRequest {
final String login;
final String password;
final String locale;
final String clientId;
final String deviceId;
const LoginRequest({
required this.login,
required this.password,
required this.locale,
required this.clientId,
required this.deviceId,
});
factory LoginRequest.fromJson(Map<String, dynamic> json) => _$LoginRequestFromJson(json);
Map<String, dynamic> toJson() => _$LoginRequestToJson(this);
}

View File

@@ -0,0 +1,38 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/permissions/data/policy.dart';
import 'package:pshared/data/mapper/permissions/data/policy.dart';
import 'package:pshared/models/permissions/data/policy.dart';
part 'change_policies.g.dart';
@JsonSerializable(explicitToJson: true)
class PoliciesChangeRequest {
final List<PolicyDTO>? add;
final List<PolicyDTO>? remove;
const PoliciesChangeRequest({
this.add,
this.remove,
});
factory PoliciesChangeRequest.add({required List<Policy> policies}) => PoliciesChangeRequest(
add: policies.map((policy) => policy.toDTO()).toList(),
);
factory PoliciesChangeRequest.remove({required List<Policy> policies}) => PoliciesChangeRequest(
remove: policies.map((policy) => policy.toDTO()).toList(),
);
factory PoliciesChangeRequest.change({
required List<Policy> add,
required List<Policy> remove,
}) => PoliciesChangeRequest(
add: add.map((policy) => policy.toDTO()).toList(),
remove: remove.map((policy) => policy.toDTO()).toList(),
);
factory PoliciesChangeRequest.fromJson(Map<String, dynamic> json) => _$PoliciesChangeRequestFromJson(json);
Map<String, dynamic> toJson() => _$PoliciesChangeRequestToJson(this);
}

View File

@@ -0,0 +1,42 @@
import 'package:json_annotation/json_annotation.dart';
part 'signup.g.dart';
@JsonSerializable(explicitToJson: true)
class SignupRequest {
final String name;
final String login;
final String password;
final String locale;
final String organizationName;
final String organizationTimeZone;
const SignupRequest({
required this.name,
required this.login,
required this.password,
required this.locale,
required this.organizationName,
required this.organizationTimeZone,
});
factory SignupRequest.build({
required String name,
required String login,
required String password,
required String locale,
required String organizationName,
required String organizationTimeZone,
}) => SignupRequest(
name: name,
login: login,
password: password,
locale: locale,
organizationName: organizationName,
organizationTimeZone: organizationTimeZone,
);
factory SignupRequest.fromJson(Map<String, dynamic> json) => _$SignupRequestFromJson(json);
Map<String, dynamic> toJson() => _$SignupRequestToJson(this);
}

View File

@@ -0,0 +1,4 @@
import 'package:pshared/api/requests/tokens/refresh_rotate.dart';
typedef AccessTokenRefreshRequest = RotateRefreshTokenRequest;

View File

@@ -0,0 +1,21 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/api/requests/tokens/session_id.dart';
part 'refresh_rotate.g.dart';
@JsonSerializable(explicitToJson: true)
class RotateRefreshTokenRequest extends SessionID {
final String token;
const RotateRefreshTokenRequest({
required this.token,
required super.clientId,
required super.deviceId,
});
factory RotateRefreshTokenRequest.fromJson(Map<String, dynamic> json) => _$RotateRefreshTokenRequestFromJson(json);
@override
Map<String, dynamic> toJson() => _$RotateRefreshTokenRequestToJson(this);
}

View File

@@ -0,0 +1,18 @@
import 'package:json_annotation/json_annotation.dart';
part 'session_id.g.dart';
@JsonSerializable(explicitToJson: true)
class SessionID {
final String clientId;
final String deviceId;
const SessionID({
required this.clientId,
required this.deviceId,
});
factory SessionID.fromJson(Map<String, dynamic> json) => _$SessionIDFromJson(json);
Map<String, dynamic> toJson() => _$SessionIDToJson(this);
}

View File

@@ -0,0 +1,18 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/api/responses/token.dart';
import 'package:pshared/data/dto/account/account.dart';
part 'account.g.dart';
@JsonSerializable(explicitToJson: true)
class AccountResponse {
final AccountDTO account;
final TokenData accessToken;
const AccountResponse({required this.accessToken, required this.account});
factory AccountResponse.fromJson(Map<String, dynamic> json) => _$AccountResponseFromJson(json);
Map<String, dynamic> toJson() => _$AccountResponseToJson(this);
}

View File

@@ -0,0 +1,16 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/api/responses/token.dart';
part 'base.g.dart';
@JsonSerializable(explicitToJson: true)
class BaseAuthorizedResponse {
final TokenData accessToken;
const BaseAuthorizedResponse({required this.accessToken});
factory BaseAuthorizedResponse.fromJson(Map<String, dynamic> json) => _$BaseAuthorizedResponseFromJson(json);
Map<String, dynamic> toJson() => _$BaseAuthorizedResponseToJson(this);
}

View File

@@ -0,0 +1,18 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/api/responses/token.dart';
import 'package:pshared/data/dto/account/account.dart';
part 'employees.g.dart';
@JsonSerializable(explicitToJson: true)
class EmployeesResponse {
final List<AccountDTO> accounts;
final TokenData accessToken;
const EmployeesResponse({required this.accessToken, required this.accounts});
factory EmployeesResponse.fromJson(Map<String, dynamic> json) => _$EmployeesResponseFromJson(json);
Map<String, dynamic> toJson() => _$EmployeesResponseToJson(this);
}

View File

@@ -0,0 +1,13 @@
class ConnectivityError implements Exception {
final int? code;
final String message;
const ConnectivityError({this.code, required this.message});
@override
String toString() {
return code == null
? 'Error response, message: $message)'
: 'Error response (code: $code, message: $message)';
}
}

View File

@@ -0,0 +1,43 @@
import 'package:json_annotation/json_annotation.dart';
part 'server.g.dart';
@JsonSerializable()
class ErrorResponse implements Exception {
final int code;
final String details;
final String source;
final String error;
const ErrorResponse({
required this.code,
required this.details,
required this.error,
required this.source,
});
@override
String toString() {
final buffer = StringBuffer('Error response (code: $code');
if (details.isNotEmpty) {
buffer.write(', details: $details');
}
if (error.isNotEmpty) {
buffer.write(', error: $error');
}
if (source.isNotEmpty) {
buffer.write(', source: $source');
}
buffer.write(')');
return buffer.toString();
}
factory ErrorResponse.fromJson(Map<String, dynamic> json) => _$ErrorResponseFromJson(json);
Map<String, dynamic> toJson() => _$ErrorResponseToJson(this);
}

View File

@@ -0,0 +1,15 @@
import 'package:json_annotation/json_annotation.dart';
part 'file_uploaded.g.dart';
@JsonSerializable()
class FileUploaded {
final String url;
const FileUploaded({ required this.url });
factory FileUploaded.fromJson(Map<String, dynamic> json) => _$FileUploadedFromJson(json);
Map<String, dynamic> toJson() => _$FileUploadedToJson(this);
}

View File

@@ -0,0 +1,19 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/api/responses/account.dart';
import 'package:pshared/api/responses/token.dart';
import 'package:pshared/data/dto/account/account.dart';
part 'login.g.dart';
@JsonSerializable(explicitToJson: true)
class LoginResponse extends AccountResponse {
final TokenData refreshToken;
const LoginResponse({required super.accessToken, required super.account, required this.refreshToken});
factory LoginResponse.fromJson(Map<String, dynamic> json) => _$LoginResponseFromJson(json);
@override
Map<String, dynamic> toJson() => _$LoginResponseToJson(this);
}

View File

@@ -0,0 +1,19 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/api/responses/type.dart';
part 'message.g.dart';
@JsonSerializable(explicitToJson: true)
class HTTPMessage {
@JsonKey(fromJson: MessageTypeExtension.fromJson, toJson: MessageTypeExtension.toJson)
final MessageType status;
final Map<String, dynamic> data;
const HTTPMessage({ required this.data, required this.status });
factory HTTPMessage.fromJson(Map<String, dynamic> json) => _$HTTPMessageFromJson(json);
Map<String, dynamic> toJson() => _$HTTPMessageToJson(this);
}

View 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/organization.dart';
part 'organization.g.dart';
@JsonSerializable(explicitToJson: true)
class OrganizationResponse extends BaseAuthorizedResponse {
final List<OrganizationDTO> organizations;
const OrganizationResponse({required super.accessToken, required this.organizations});
factory OrganizationResponse.fromJson(Map<String, dynamic> json) => _$OrganizationResponseFromJson(json);
@override
Map<String, dynamic> toJson() => _$OrganizationResponseToJson(this);
}

View 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/permissions/data/permissions.dart';
import 'package:pshared/data/dto/permissions/description/description.dart';
part 'policies.g.dart';
@JsonSerializable(explicitToJson: true)
class PoliciesResponse extends BaseAuthorizedResponse {
final PermissionsDescriptionDTO descriptions;
final PermissionsDataDTO permissions;
const PoliciesResponse({required this.descriptions, required this.permissions, required super.accessToken});
factory PoliciesResponse.fromJson(Map<String, dynamic> json) => _$PoliciesResponseFromJson(json);
@override
Map<String, dynamic> toJson() => _$PoliciesResponseToJson(this);
}

View File

@@ -0,0 +1,15 @@
import 'package:json_annotation/json_annotation.dart';
part 'token.g.dart';
@JsonSerializable()
class TokenData {
final String token;
final DateTime expiration;
const TokenData({required this.token, required this.expiration});
factory TokenData.fromJson(Map<String, dynamic> json) => _$TokenDataFromJson(json);
Map<String, dynamic> toJson() => _$TokenDataToJson(this);
}

View File

@@ -0,0 +1,32 @@
enum MessageType {
success,
error,
request
}
extension MessageTypeExtension on MessageType {
static String toJson(MessageType value) {
switch (value) {
case MessageType.success:
return 'success';
case MessageType.error:
return 'error';
case MessageType.request:
return 'request';
}
}
static MessageType fromJson(String json) {
switch (json) {
case 'success':
return MessageType.success;
case 'error':
return MessageType.error;
case 'request':
return MessageType.request;
default:
throw ArgumentError('Unknown HTTPMType string: $json');
}
}
}

View File

@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
class CommonConstants {
static String apiProto = const String.fromEnvironment('API_PROTO', defaultValue: 'http');
static String apiHost = const String.fromEnvironment('API_HOST', defaultValue: 'localhost');
// static String apiHost = 'localhost';
// static String apiHost = '10.0.2.2';
static String apiEndpoint = '/api/v1';
static String amplitudeSecret = 'c3d75b3e2520d708440acbb16b923e79';
static String amplitudeServerZone = 'EU';
static Locale defaultLocale = const Locale('en');
static String defaultCurrency = 'EUR';
static int defaultDimensionLength = 500;
static String clientId = '';
static String wsProto = 'ws';
static String wsEndpoint = '/ws';
static Color themeColor = Color.fromARGB(255, 80, 63, 224);
static String nilObjectRef = '000000000000000000000000';
// Public getters for shared properties
static String get serviceUrl => '$apiProto://$apiHost';
static String get apiUrl => '$serviceUrl$apiEndpoint';
static String get wsUrl => '$wsProto://$apiHost$apiEndpoint$wsEndpoint';
static const String accessTokenStorageKey = 'access_token';
static const String refreshTokenStorageKey = 'refresh_token';
static const String currentOrgKey = 'current_org';
static const String deviceIdStorageKey = 'device_id';
// Method to apply the configuration, called by platform-specific implementations
static void applyConfiguration(Map<String, dynamic> configJson) {
apiProto = configJson['apiProto'] ?? apiProto;
apiHost = configJson['apiHost'] ?? apiHost;
apiEndpoint = configJson['apiEndpoint'] ?? apiEndpoint;
amplitudeSecret = configJson['amplitudeSecret'] ?? amplitudeSecret;
amplitudeServerZone = configJson['amplitudeServerZone'] ?? amplitudeServerZone;
defaultLocale = Locale(configJson['defaultLocale'] ?? defaultLocale.languageCode);
defaultCurrency = configJson['defaultCurrency'] ?? defaultCurrency;
wsProto = configJson['wsProto'] ?? wsProto;
wsEndpoint = configJson['wsEndpoint'] ?? wsEndpoint;
defaultDimensionLength = configJson['defaultDimensionLength'] ?? defaultDimensionLength;
clientId = configJson['clientId'] ?? clientId;
if (configJson.containsKey('themeColor')) {
themeColor = Color(int.parse(configJson['themeColor']));
}
}
}

View File

@@ -0,0 +1,2 @@
export 'mobile.dart'
if (dart.library.html) 'web.dart';

View File

@@ -0,0 +1,35 @@
import 'dart:convert';
import 'dart:io';
import 'dart:ui';
import 'package:pshared/config/common.dart';
class Constants extends CommonConstants {
static const String _clientIdMobile = 'com.profee.pay.mobile-3f9c3b76-2f89-4e9e-95a2-1a5b705b7a1d';
static Locale get defaultLocale => CommonConstants.defaultLocale;
static String get clientId => _clientIdMobile;
static String get accessTokenStorageKey => CommonConstants.accessTokenStorageKey;
static String get refreshTokenStorageKey => CommonConstants.refreshTokenStorageKey;
static String get currentOrgKey => CommonConstants.currentOrgKey;
static String get apiUrl => CommonConstants.apiUrl;
static String get serviceUrl => CommonConstants.serviceUrl;
static int get defaultDimensionLength => CommonConstants.defaultDimensionLength;
static String get deviceIdStorageKey => CommonConstants.deviceIdStorageKey;
static String get nilObjectRef => CommonConstants.nilObjectRef;
static Color get themeColor => CommonConstants.themeColor;
static Future<void> initialize() async {
var configFile = File('./config/config.json');
if (await configFile.exists()) {
var configJson = jsonDecode(await configFile.readAsString());
CommonConstants.applyConfiguration({
...configJson,
'clientId': configJson['clientId'] ?? _clientIdMobile,
});
} else {
CommonConstants.clientId = _clientIdMobile;
}
}
}

View File

@@ -0,0 +1,75 @@
import 'dart:ui';
import 'dart:js_interop';
import 'package:pshared/config/common.dart';
/// Bind to the global JS `appConfig` (if it exists).
@JS()
external AppConfig? get appConfig;
/// A staticInterop class for the JS object returned by `appConfig`.
@JS()
@staticInterop
class AppConfig {}
/// Extension methods to expose each property on `AppConfig`.
extension AppConfigExtension on AppConfig {
external String? get apiProto;
external String? get apiHost;
external String? get apiEndpoint;
external String? get amplitudeSecret;
external String? get amplitudeServerZone;
external String? get defaultLocale;
external String? get wsProto;
external String? get wsEndpoint;
external int? get defaultDimensionLength;
external String? get themeColor;
external String? get clientId;
}
class Constants extends CommonConstants {
static const String _clientIdWeb = 'com.profee.pay.web-4b6e8a0f-9b5c-4f57-b3a6-3c456e9bb2cd';
// Just re-expose these from CommonConstants:
static Locale get defaultLocale => CommonConstants.defaultLocale;
static String get clientId => _clientIdWeb;
static String get accessTokenStorageKey => CommonConstants.accessTokenStorageKey;
static String get refreshTokenStorageKey => CommonConstants.refreshTokenStorageKey;
static String get currentOrgKey => CommonConstants.currentOrgKey;
static String get apiUrl => CommonConstants.apiUrl;
static String get serviceUrl => CommonConstants.serviceUrl;
static int get defaultDimensionLength => CommonConstants.defaultDimensionLength;
static String get deviceIdStorageKey => CommonConstants.deviceIdStorageKey;
static String get nilObjectRef => CommonConstants.nilObjectRef;
static Color get themeColor => CommonConstants.themeColor;
static Future<void> initialize() async {
// Try to grab the JS `appConfig` if it exists:
final config = appConfig;
if (config != null) {
// Build a Dart Map from the JS objects properties:
final configJson = <String, dynamic>{
'apiProto': config.apiProto,
'apiHost': config.apiHost,
'apiEndpoint': config.apiEndpoint,
'amplitudeSecret': config.amplitudeSecret,
'amplitudeServerZone': config.amplitudeServerZone,
'defaultLocale': config.defaultLocale,
'wsProto': config.wsProto,
'wsEndpoint': config.wsEndpoint,
'defaultDimensionLength': config.defaultDimensionLength,
'themeColor': config.themeColor,
// If the JS side didnt supply a clientId, fall back to our Dart constant
'clientId': config.clientId ?? _clientIdWeb,
};
CommonConstants.applyConfiguration(configJson);
} else {
// No appConfig on JS side → just use the default Dart client ID.
CommonConstants.clientId = _clientIdWeb;
}
}
}

View File

@@ -0,0 +1,25 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/account/base.dart';
part 'account.g.dart';
@JsonSerializable()
class AccountDTO extends AccountBaseDTO {
final String login;
const AccountDTO({
required super.id,
required super.createdAt,
required super.updatedAt,
required super.name,
required super.avatarUrl,
required super.locale,
required this.login,
});
factory AccountDTO.fromJson(Map<String, dynamic> json) => _$AccountDTOFromJson(json);
@override
Map<String, dynamic> toJson() => _$AccountDTOToJson(this);
}

View File

@@ -0,0 +1,27 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/storable.dart';
part 'base.g.dart';
@JsonSerializable()
class AccountBaseDTO extends StorableDTO {
final String name;
final String locale;
final String? avatarUrl;
const AccountBaseDTO({
required super.id,
required super.createdAt,
required super.updatedAt,
required this.name,
required this.avatarUrl,
required this.locale,
});
factory AccountBaseDTO.fromJson(Map<String, dynamic> json) => _$AccountBaseDTOFromJson(json);
@override
Map<String, dynamic> toJson() => _$AccountBaseDTOToJson(this);
}

View File

@@ -0,0 +1,23 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/storable.dart';
part 'organization.g.dart';
@JsonSerializable()
class OrganizationDTO extends StorableDTO {
final String timeZone;
final String? logoUrl;
const OrganizationDTO({
required super.id,
required super.createdAt,
required super.updatedAt,
required this.timeZone,
this.logoUrl,
});
factory OrganizationDTO.fromJson(Map<String, dynamic> json) => _$OrganizationDTOFromJson(json);
@override
Map<String, dynamic> toJson() => _$OrganizationDTOToJson(this);
}

View File

@@ -0,0 +1,16 @@
import 'package:json_annotation/json_annotation.dart';
part 'description.g.dart';
@JsonSerializable()
class OrganizationDescriptionDTO {
final String? logoUrl;
const OrganizationDescriptionDTO({
this.logoUrl,
});
factory OrganizationDescriptionDTO.fromJson(Map<String, dynamic> json) => _$OrganizationDescriptionDTOFromJson(json);
Map<String, dynamic> toJson() => _$OrganizationDescriptionDTOToJson(this);
}

View File

@@ -0,0 +1,22 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/permissions/data/permissions.dart';
import 'package:pshared/data/dto/permissions/description/description.dart';
part 'access.g.dart';
@JsonSerializable()
class UserAccessDTO {
final PermissionsDescriptionDTO descriptions;
final PermissionsDataDTO permissions;
const UserAccessDTO({
required this.descriptions,
required this.permissions,
});
factory UserAccessDTO.fromJson(Map<String, dynamic> json) => _$UserAccessDTOFromJson(json);
Map<String, dynamic> toJson() => _$UserAccessDTOToJson(this);
}

View File

@@ -0,0 +1,19 @@
// data/action_effect_dto.dart
import 'package:json_annotation/json_annotation.dart';
part 'action_effect.g.dart';
@JsonSerializable()
class ActionEffectDTO {
final String action;
final String effect;
const ActionEffectDTO({
required this.action,
required this.effect,
});
factory ActionEffectDTO.fromJson(Map<String, dynamic> json) => _$ActionEffectDTOFromJson(json);
Map<String, dynamic> toJson() => _$ActionEffectDTOToJson(this);
}

View File

@@ -0,0 +1,28 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/permissions/action_effect.dart';
part 'permission.g.dart';
@JsonSerializable()
class PermissionDTO {
final String roleDescriptionRef;
final String organizationRef;
final String descriptionRef;
final String? objectRef;
final ActionEffectDTO effect;
final String accountRef;
const PermissionDTO({
required this.roleDescriptionRef,
required this.organizationRef,
required this.descriptionRef,
required this.objectRef,
required this.effect,
required this.accountRef,
});
factory PermissionDTO.fromJson(Map<String, dynamic> json) => _$PermissionDTOFromJson(json);
Map<String, dynamic> toJson() => _$PermissionDTOToJson(this);
}

View File

@@ -0,0 +1,24 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/permissions/data/permission.dart';
import 'package:pshared/data/dto/permissions/data/policy.dart';
import 'package:pshared/data/dto/permissions/data/role.dart';
part 'permissions.g.dart';
@JsonSerializable()
class PermissionsDataDTO {
final List<RoleDTO> roles;
final List<PolicyDTO> policies;
final List<PermissionDTO> permissions;
const PermissionsDataDTO({
required this.roles,
required this.policies,
required this.permissions,
});
factory PermissionsDataDTO.fromJson(Map<String, dynamic> json) => _$PermissionsDataDTOFromJson(json);
Map<String, dynamic> toJson() => _$PermissionsDataDTOToJson(this);
}

View File

@@ -0,0 +1,26 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/permissions/action_effect.dart';
part 'policy.g.dart';
@JsonSerializable()
class PolicyDTO {
final String roleDescriptionRef;
final String organizationRef;
final String descriptionRef;
final String? objectRef;
final ActionEffectDTO effect;
const PolicyDTO({
required this.roleDescriptionRef,
required this.organizationRef,
required this.descriptionRef,
required this.objectRef,
required this.effect,
});
factory PolicyDTO.fromJson(Map<String, dynamic> json) => _$PolicyDTOFromJson(json);
Map<String, dynamic> toJson() => _$PolicyDTOToJson(this);
}

View File

@@ -0,0 +1,20 @@
import 'package:json_annotation/json_annotation.dart';
part 'role.g.dart';
@JsonSerializable()
class RoleDTO {
final String accountRef;
final String organizationRef;
final String descriptionRef;
const RoleDTO({
required this.accountRef,
required this.descriptionRef,
required this.organizationRef,
});
factory RoleDTO.fromJson(Map<String, dynamic> json) => _$RoleDTOFromJson(json);
Map<String, dynamic> toJson() => _$RoleDTOToJson(this);
}

View File

@@ -0,0 +1,21 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/permissions/description/policy.dart';
import 'package:pshared/data/dto/permissions/description/role.dart';
part 'description.g.dart';
@JsonSerializable()
class PermissionsDescriptionDTO {
final List<RoleDescriptionDTO> roles;
final List<PolicyDescriptionDTO> policies;
const PermissionsDescriptionDTO({
required this.roles,
required this.policies,
});
factory PermissionsDescriptionDTO.fromJson(Map<String, dynamic> json) => _$PermissionsDescriptionDTOFromJson(json);
Map<String, dynamic> toJson() => _$PermissionsDescriptionDTOToJson(this);
}

View File

@@ -0,0 +1,25 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/storable.dart';
import 'package:pshared/models/resources.dart';
part 'policy.g.dart';
@JsonSerializable()
class PolicyDescriptionDTO extends StorableDTO {
final List<ResourceType>? resourceTypes;
final String? organizationRef;
const PolicyDescriptionDTO({
required super.id,
required super.createdAt,
required super.updatedAt,
required this.resourceTypes,
required this.organizationRef,
});
factory PolicyDescriptionDTO.fromJson(Map<String, dynamic> json) => _$PolicyDescriptionDTOFromJson(json);
@override
Map<String, dynamic> toJson() => _$PolicyDescriptionDTOToJson(this);
}

View File

@@ -0,0 +1,22 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/storable.dart';
part 'role.g.dart';
@JsonSerializable()
class RoleDescriptionDTO extends StorableDTO {
final String organizationRef;
const RoleDescriptionDTO({
required super.id,
required super.createdAt,
required super.updatedAt,
required this.organizationRef,
});
factory RoleDescriptionDTO.fromJson(Map<String, dynamic> json) => _$RoleDescriptionDTOFromJson(json);
@override
Map<String, dynamic> toJson() => _$RoleDescriptionDTOToJson(this);
}

View File

@@ -0,0 +1,20 @@
import 'package:json_annotation/json_annotation.dart';
part 'storable.g.dart';
@JsonSerializable()
class StorableDTO {
final String id;
final DateTime createdAt;
final DateTime updatedAt;
const StorableDTO({
required this.id,
required this.createdAt,
required this.updatedAt,
});
factory StorableDTO.fromJson(Map<String, dynamic> json) => _$StorableDTOFromJson(json);
Map<String, dynamic> toJson() => _$StorableDTOToJson(this);
}

Binary file not shown.

View File

@@ -0,0 +1,26 @@
import 'package:pshared/data/dto/account/account.dart';
import 'package:pshared/models/account/account.dart';
import 'package:pshared/models/storable.dart';
extension AccountMapper on Account {
AccountDTO toDTO() => AccountDTO(
id: id,
createdAt: createdAt,
updatedAt: updatedAt,
name: name,
avatarUrl: avatarUrl,
locale: locale,
login: login,
);
}
extension AccountDTOMapper on AccountDTO {
Account toDomain() => Account(
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
avatarUrl: avatarUrl,
locale: locale,
login: login,
name: name,
);
}

View File

@@ -0,0 +1,24 @@
import 'package:pshared/data/dto/account/base.dart';
import 'package:pshared/models/account/base.dart';
import 'package:pshared/models/storable.dart';
extension AccountBaseMapper on AccountBase {
AccountBaseDTO toDTO() => AccountBaseDTO(
id: storable.id,
createdAt: storable.createdAt,
updatedAt: storable.updatedAt,
avatarUrl: avatarUrl,
name: name,
locale: locale,
);
}
extension AccountDTOMapper on AccountBaseDTO {
AccountBase toDomain() => AccountBase(
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
avatarUrl: avatarUrl,
name: name,
locale: locale,
);
}

View File

@@ -0,0 +1,164 @@
import 'package:flutter/material.dart';
final Map<String, IconData> iconMapping = {
// General Project Management Icons
'dashboard': Icons.dashboard, // Overview screen
'project': Icons.work, // Represents a project
'tasks': Icons.check_box, // Task list
'calendar': Icons.calendar_today, // Calendar view
'team': Icons.group, // Team collaboration
'kanban': Icons.view_column, // Kanban board
'timeline': Icons.timeline, // Project timeline
'milestone': Icons.flag, // Milestones
'priority': Icons.priority_high, // General priority indicator
'settings': Icons.settings, // Settings or configurations
'chat': Icons.chat, // Communication/chat
'files': Icons.insert_drive_file, // File management
'notes': Icons.note, // Notes or documentation
'report': Icons.insert_chart, // Reporting and analytics
// Priority Related Icons
'to_do': Icons.assignment, // To do tasks
'in_progress': Icons.autorenew, // Tasks in progress
'complete': Icons.check_circle, // Completed tasks
'archived': Icons.archive, // Archived tasks
// Deadline Related Icons
'deadline': Icons.timer, // Deadline indicator
'reminder': Icons.alarm, // Deadline reminder
'due_today': Icons.today, // Tasks due today
'upcoming': Icons.event_available, // Upcoming deadlines
// Additional
'overdue': Icons.warning, // Overdue tasks or deadlines
'budget': Icons.account_balance_wallet, // Budget and finance
'resource': Icons.perm_contact_calendar, // Resource allocation
'risk': Icons.warning_amber, // Risk management
'feedback': Icons.feedback, // Feedback and reviews
'timeline_edit': Icons.edit_calendar, // Edit project timeline
'workflow': Icons.shuffle, // Workflow management
'dependencies': Icons.link, // Task dependencies
'progress': Icons.show_chart, // Project progress
'schedule': Icons.schedule, // Scheduling
'support': Icons.support, // Support/help
'permissions': Icons.lock, // Access permissions
'backup': Icons.backup, // Data backup
'integration': Icons.extension, // Integrations
'search': Icons.search, // Search functionality
'announcement': Icons.announcement, // Announcements or updates
'analytics': Icons.analytics, // Project analytics
'assignment': Icons.assignment_turned_in, // Task assignments
'discussions': Icons.forum, // Discussions/threads
'timeline_view': Icons.view_timeline, // Detailed timeline view
'board': Icons.dashboard_customize, // Custom project boards
'approval': Icons.how_to_vote, // Approvals
'review': Icons.rate_review, // Reviews and feedback
'objective': Icons.golf_course, // Objectives/goals
'settings_advanced': Icons.tune, // Advanced settings
'time_tracking': Icons.timer_outlined, // Time tracking
'checklist': Icons.checklist, // Checklists
'sync': Icons.sync, // Syncing data
'upload': Icons.cloud_upload, // Upload files
'download': Icons.cloud_download, // Download files
'share': Icons.share, // Sharing options
'tag': Icons.label, // Tags/labels
'notifications': Icons.notifications, // Notifications
'user_roles': Icons.manage_accounts, // User roles and permissions
'logout': Icons.logout, // Logout
'automation': Icons.auto_awesome, // Automation
'history': Icons.history, // Project history/logs
'estimate': Icons.calculate, // Estimates and costing
'quality': Icons.verified, // Quality assurance
'strategy': Icons.lightbulb, // Strategy planning
'feedback_form': Icons.comment, // Feedback forms
'presentation': Icons.slideshow, // Project presentations
};
class ProjectIcons {
static const IconData dashboard = Icons.dashboard;
static const IconData project = Icons.work;
static const IconData tasks = Icons.check_box;
static const IconData calendar = Icons.calendar_today;
static const IconData team = Icons.group;
static const IconData kanban = Icons.view_column;
static const IconData timeline = Icons.timeline;
static const IconData milestone = Icons.flag;
static const IconData priority = Icons.priority_high;
static const IconData settings = Icons.settings;
static const IconData chat = Icons.chat;
static const IconData files = Icons.insert_drive_file;
static const IconData notes = Icons.note;
static const IconData report = Icons.insert_chart;
static const IconData todo = Icons.assignment;
static const IconData inProgress = Icons.autorenew;
static const IconData complete = Icons.check_circle;
static const IconData archived = Icons.archive;
static const IconData deadline = Icons.timer;
static const IconData reminder = Icons.alarm;
static const IconData dueToday = Icons.today;
static const IconData upcoming = Icons.event_available;
static const IconData overdue = Icons.warning;
static const IconData budget = Icons.account_balance_wallet;
static const IconData resource = Icons.perm_contact_calendar;
static const IconData risk = Icons.warning_amber;
static const IconData feedback = Icons.feedback;
static const IconData timelineEdit = Icons.edit_calendar;
static const IconData workflow = Icons.shuffle;
static const IconData dependencies = Icons.link;
static const IconData progress = Icons.show_chart;
static const IconData schedule = Icons.schedule;
static const IconData support = Icons.support;
static const IconData permissions = Icons.lock;
static const IconData backup = Icons.backup;
static const IconData integration = Icons.extension;
static const IconData search = Icons.search;
static const IconData announcement = Icons.announcement;
static const IconData analytics = Icons.analytics;
static const IconData assignment = Icons.assignment_turned_in;
static const IconData discussions = Icons.forum;
static const IconData timelineView = Icons.view_timeline;
static const IconData board = Icons.dashboard_customize;
static const IconData approval = Icons.how_to_vote;
static const IconData review = Icons.rate_review;
static const IconData objective = Icons.golf_course;
static const IconData settingsAdvanced = Icons.tune;
static const IconData timeTracking = Icons.timer_outlined;
static const IconData checklist = Icons.checklist;
static const IconData sync = Icons.sync;
static const IconData upload = Icons.cloud_upload;
static const IconData download = Icons.cloud_download;
static const IconData share = Icons.share;
static const IconData tag = Icons.label;
static const IconData notifications = Icons.notifications;
static const IconData userRoles = Icons.manage_accounts;
static const IconData logout = Icons.logout;
static const IconData automation = Icons.auto_awesome;
static const IconData history = Icons.history;
static const IconData estimate = Icons.calculate;
static const IconData quality = Icons.verified;
static const IconData strategy = Icons.lightbulb;
static const IconData feedbackForm = Icons.comment;
static const IconData presentation = Icons.slideshow;
}
extension IconDataKeyExtension on IconData {
String get toIconKey {
return iconMapping.entries.firstWhere(
(entry) => entry.value == this,
orElse: () => throw Exception('IconData not found in mapping.'),
).key;
}
}
extension IconDataFromKeyExtension on String {
IconData get toIconData {
final iconData = iconMapping[this];
if (iconData == null) {
throw Exception('No IconData found for key: $this');
}
return iconData;
}
}

View File

@@ -0,0 +1,22 @@
import 'package:pshared/data/dto/organization.dart';
import 'package:pshared/models/organization/organization.dart';
import 'package:pshared/models/storable.dart';
extension OrganizationMapper on Organization {
OrganizationDTO toDTO() => OrganizationDTO(
id: storable.id,
createdAt: storable.createdAt,
updatedAt: storable.updatedAt,
timeZone: timeZone,
logoUrl: logoUrl,
);
}
extension OrganizationDTOMapper on OrganizationDTO {
Organization toDomain() => Organization(
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
timeZone: timeZone,
logoUrl: logoUrl,
);
}

View File

@@ -0,0 +1,15 @@
import 'package:pshared/data/dto/organization/description.dart';
import 'package:pshared/models/organization/description.dart';
extension OrganizationDescriptionMapper on OrganizationDescription {
OrganizationDescriptionDTO toDTO() => OrganizationDescriptionDTO(
logoUrl: logoUrl,
);
}
extension AccountDescriptionDTOMapper on OrganizationDescriptionDTO {
OrganizationDescription toDomain() => OrganizationDescription(
logoUrl: logoUrl,
);
}

View File

@@ -0,0 +1,23 @@
import 'package:pshared/data/dto/permissions/action_effect.dart';
import 'package:pshared/models/permissions/action_effect.dart';
import 'package:pshared/models/permissions/action.dart';
import 'package:pshared/models/permissions/effect.dart';
extension ActionEffectMapper on ActionEffect {
ActionEffectDTO toDTO() {
return ActionEffectDTO(
action: action.toShortString(),
effect: effect.toShortString(),
);
}
}
extension ActionEffectDTOMapper on ActionEffectDTO {
ActionEffect toDomain() {
return ActionEffect(
action: ActionExtension.fromString(action),
effect: EffectExtension.fromString(effect),
);
}
}

View File

@@ -0,0 +1,33 @@
import 'package:pshared/data/dto/permissions/data/permission.dart';
import 'package:pshared/data/mapper/permissions/action_effect.dart';
import 'package:pshared/models/permissions/data/permission.dart';
import 'package:pshared/models/permissions/data/policy.dart';
extension PermissionMapper on Permission {
PermissionDTO toDTO() {
return PermissionDTO(
roleDescriptionRef: policy.roleDescriptionRef,
organizationRef: policy.organizationRef,
descriptionRef: policy.descriptionRef,
objectRef: policy.objectRef,
effect: policy.effect.toDTO(),
accountRef: accountRef,
);
}
}
extension PermissionDTOMapper on PermissionDTO {
Permission toDomain() {
return Permission(
policy: Policy(
roleDescriptionRef: roleDescriptionRef,
organizationRef: organizationRef,
descriptionRef: descriptionRef,
objectRef: objectRef,
effect: effect.toDomain(),
),
accountRef: accountRef,
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:pshared/data/dto/permissions/data/permissions.dart';
import 'package:pshared/data/mapper/permissions/data/permission.dart';
import 'package:pshared/data/mapper/permissions/data/policy.dart';
import 'package:pshared/data/mapper/permissions/data/role.dart';
import 'package:pshared/models/permissions/data/permissions.dart';
extension PermissionsDataMapper on PermissionsData {
PermissionsDataDTO toDTO() {
return PermissionsDataDTO(
roles: roles.map((role) => role.toDTO()).toList(),
policies: policies.map((policy) => policy.toDTO()).toList(),
permissions: permissions.map((permission) => permission.toDTO()).toList(),
);
}
}
extension PermissionsDataDTOMapper on PermissionsDataDTO {
PermissionsData toDomain() {
return PermissionsData(
roles: roles.map((role) => role.toDomain()).toList(),
policies: policies.map((policy) => policy.toDomain()).toList(),
permissions: permissions.map((permission) => permission.toDomain()).toList(),
);
}
}

View File

@@ -0,0 +1,28 @@
import 'package:pshared/data/dto/permissions/data/policy.dart';
import 'package:pshared/data/mapper/permissions/action_effect.dart';
import 'package:pshared/models/permissions/data/policy.dart';
extension PolicyMapper on Policy {
PolicyDTO toDTO() {
return PolicyDTO(
roleDescriptionRef: roleDescriptionRef,
organizationRef: organizationRef,
descriptionRef: descriptionRef,
objectRef: objectRef,
effect: effect.toDTO(),
);
}
}
extension PolicyDTOMapper on PolicyDTO {
Policy toDomain() {
return Policy(
roleDescriptionRef: roleDescriptionRef,
organizationRef: organizationRef,
descriptionRef: descriptionRef,
objectRef: objectRef,
effect: effect.toDomain(),
);
}
}

View File

@@ -0,0 +1,25 @@
import 'package:pshared/data/dto/permissions/data/role.dart';
import 'package:pshared/models/permissions/data/role.dart';
extension RoleMapper on Role {
/// Converts a `Role` domain model to a `RoleDTO`.
RoleDTO toDTO() {
return RoleDTO(
accountRef: accountRef,
descriptionRef: descriptionRef,
organizationRef: organizationRef,
);
}
}
extension RoleDTOMapper on RoleDTO {
/// Converts a `RoleDTO` to a `Role` domain model.
Role toDomain() {
return Role(
accountRef: accountRef,
descriptionRef: descriptionRef,
organizationRef: organizationRef,
);
}
}

View File

@@ -0,0 +1,23 @@
import 'package:pshared/data/dto/permissions/description/description.dart';
import 'package:pshared/data/mapper/permissions/descriptions/policy.dart';
import 'package:pshared/data/mapper/permissions/descriptions/role.dart';
import 'package:pshared/models/permissions/descriptions/permissions.dart';
extension PermissionsDescriptionMapper on PermissionsDescription {
PermissionsDescriptionDTO toDTO() {
return PermissionsDescriptionDTO(
roles: roles.map((role) => role.toDTO()).toList(),
policies: policies.map((policy) => policy.toDTO()).toList(),
);
}
}
extension PermissionsDescriptionDTOMapper on PermissionsDescriptionDTO {
PermissionsDescription toDomain() {
return PermissionsDescription(
roles: roles.map((role) => role.toDomain()).toList(),
policies: policies.map((policy) => policy.toDomain()).toList(),
);
}
}

View File

@@ -0,0 +1,22 @@
import 'package:pshared/data/dto/permissions/description/policy.dart';
import 'package:pshared/models/permissions/descriptions/policy.dart';
import 'package:pshared/models/storable.dart';
extension PolicyDescriptionMapper on PolicyDescription {
PolicyDescriptionDTO toDTO() => PolicyDescriptionDTO(
id: storable.id,
createdAt: storable.createdAt,
updatedAt: storable.updatedAt,
resourceTypes: resourceTypes,
organizationRef: organizationRef,
);
}
extension PolicyDescriptionDTOMapper on PolicyDescriptionDTO {
PolicyDescription toDomain() => PolicyDescription(
storable: newStorable(id: id, createdAt: createdAt, updatedAt: createdAt),
resourceTypes: resourceTypes,
organizationRef: organizationRef,
);
}

View File

@@ -0,0 +1,20 @@
import 'package:pshared/data/dto/permissions/description/role.dart';
import 'package:pshared/models/permissions/descriptions/role.dart';
import 'package:pshared/models/storable.dart';
extension RoleDescriptionMapper on RoleDescription {
RoleDescriptionDTO toDTO() => RoleDescriptionDTO(
id: storable.id,
createdAt: storable.createdAt,
updatedAt: storable.updatedAt,
organizationRef: organizationRef,
);
}
extension RoleDescriptionDTOMapper on RoleDescriptionDTO {
RoleDescription toDomain() => RoleDescription(
storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt),
organizationRef: organizationRef,
);
}

View File

@@ -0,0 +1,11 @@
import 'package:pshared/data/dto/storable.dart';
import 'package:pshared/models/storable.dart';
extension StorableMapper on Storable {
StorableDTO toDTO() => StorableDTO(id: id, createdAt: createdAt, updatedAt: updatedAt);
}
extension StorableDTOMapper on StorableDTO {
Storable toDomain() => newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt);
}

View File

@@ -0,0 +1,23 @@
{
"@@locale": "en",
"statusReady": "Ready",
"statusRegistered": "Registered",
"statusNotRegistered": "Not Registered",
"typeInternal": "Internal",
"typeExternal": "External",
"operationStatusProcessing": "Processing",
"@operationStatusProcessing": {
"description": "Label for the “processing” operation status"
},
"operationStatusSuccess": "Success",
"@operationStatusSuccess": {
"description": "Label for the “success” operation status"
},
"operationStatusError": "Error",
"@operationStatusError": {
"description": "Label for the “error” operation status"
}
}

View File

@@ -0,0 +1,36 @@
import 'package:pshared/models/account/base.dart';
class Account extends AccountBase {
final String login;
const Account({
required super.storable,
required super.avatarUrl,
required this.login,
required super.locale,
required super.name,
});
factory Account.fromBase(AccountBase accountBase, String login) => Account(
storable: accountBase.storable,
avatarUrl: accountBase.avatarUrl,
locale: accountBase.locale,
name: accountBase.name,
login: login,
);
@override
Account copyWith({
String? Function()? avatarUrl,
String? name,
String? locale,
}) {
final updatedBase = super.copyWith(
avatarUrl: avatarUrl,
name: name,
locale: locale,
);
return Account.fromBase(updatedBase, login);
}
}

View File

@@ -0,0 +1,35 @@
import 'package:pshared/models/storable.dart';
class AccountBase implements Storable {
final Storable storable;
@override
String get id => storable.id;
@override
DateTime get createdAt => storable.createdAt;
@override
DateTime get updatedAt => storable.updatedAt;
final String? avatarUrl;
final String name;
final String locale;
const AccountBase({
required this.storable,
required this.name,
required this.locale,
required this.avatarUrl,
});
AccountBase copyWith({
String? Function()? avatarUrl,
String? name,
String? locale,
}) => AccountBase(
storable: storable,
avatarUrl: avatarUrl != null ? avatarUrl() : this.avatarUrl,
locale: locale ?? this.locale,
name: name ?? this.name,
);
}

View File

@@ -0,0 +1,7 @@
class OrganizationDescription {
final String? logoUrl;
const OrganizationDescription({
this.logoUrl,
});
}

View File

@@ -0,0 +1,4 @@
import 'package:pshared/models/account/account.dart';
typedef Employee = Account;

View File

@@ -0,0 +1,34 @@
import 'package:pshared/models/storable.dart';
class Organization implements Storable {
final Storable storable;
@override
String get id => storable.id;
@override
DateTime get createdAt => storable.createdAt;
@override
DateTime get updatedAt => storable.updatedAt;
final String timeZone;
final String? logoUrl;
const Organization({
required this.storable,
required this.timeZone,
this.logoUrl,
});
Organization copyWith({
String? name,
String? Function()? description,
String? timeZone,
String? Function()? logoUrl,
}) => Organization(
storable: storable, // Same Storable, same id
timeZone: timeZone ?? this.timeZone,
logoUrl: logoUrl != null ? logoUrl() : this.logoUrl,
);
}

View File

@@ -0,0 +1,18 @@
import 'package:pshared/models/payment/methods/data.dart';
import 'package:pshared/models/payment/type.dart';
class CardPaymentMethod extends PaymentMethodData {
@override
final PaymentType type = PaymentType.card;
final String pan;
final String firstName;
final String lastName;
CardPaymentMethod({
required this.pan,
required this.firstName,
required this.lastName,
});
}

View File

@@ -0,0 +1,6 @@
import 'package:pshared/models/payment/type.dart';
abstract class PaymentMethodData {
PaymentType get type;
}

View File

@@ -0,0 +1,20 @@
import 'package:pshared/models/payment/methods/data.dart';
import 'package:pshared/models/payment/type.dart';
class IbanPaymentMethod extends PaymentMethodData {
@override
final PaymentType type = PaymentType.iban;
final String iban; // e.g. DE89 3704 0044 0532 0130 00
final String accountHolder; // Full name of the recipient
final String? bic; // Optional: for cross-border transfers
final String? bankName; // Optional: for UI clarity
IbanPaymentMethod({
required this.iban,
required this.accountHolder,
this.bic,
this.bankName,
});
}

View File

@@ -0,0 +1,26 @@
import 'package:pshared/models/payment/methods/data.dart';
import 'package:pshared/models/payment/type.dart';
class RussianBankAccountPaymentMethod extends PaymentMethodData {
@override
final PaymentType type = PaymentType.bankAccount;
final String recipientName;
final String inn;
final String kpp;
final String bankName;
final String bik;
final String accountNumber;
final String correspondentAccount;
RussianBankAccountPaymentMethod({
required this.recipientName,
required this.inn,
required this.kpp,
required this.bankName,
required this.bik,
required this.accountNumber,
required this.correspondentAccount,
});
}

View File

@@ -0,0 +1,21 @@
import 'package:pshared/models/payment/type.dart';
class PaymentMethod {
PaymentMethod({
required this.id,
required this.label,
required this.details,
required this.type,
this.isEnabled = true,
this.isMain = false,
});
final String id;
final String label;
final String details;
final PaymentType type;
bool isEnabled;
bool isMain;
}

View File

@@ -0,0 +1,12 @@
import 'package:pshared/models/payment/methods/data.dart';
import 'package:pshared/models/payment/type.dart';
class WalletPaymentMethod extends PaymentMethodData {
@override
final PaymentType type = PaymentType.wallet;
final String walletId;
WalletPaymentMethod({required this.walletId});
}

View File

@@ -0,0 +1,30 @@
import 'package:pshared/models/payment/status.dart';
class OperationItem {
final OperationStatus status;
final String? fileName;
final double amount;
final String currency;
final double toAmount;
final String toCurrency;
final String payId;
final String? cardNumber;
final String name;
final DateTime date;
final String comment;
OperationItem({
required this.status,
this.fileName,
required this.amount,
required this.currency,
required this.toAmount,
required this.toCurrency,
required this.payId,
this.cardNumber,
required this.name,
required this.date,
required this.comment,
});
}

View File

@@ -0,0 +1,25 @@
import 'package:flutter/widgets.dart';
import 'package:pshared/generated/i18n/ps_localizations.dart';
enum OperationStatus {
processing,
success,
error,
}
extension OperationStatusX on OperationStatus {
/// Returns the localized string for this status,
/// e.g. “Processing”, “Success”, “Error”.
String localized(BuildContext context) {
final loc = PSLocalizations.of(context)!;
switch (this) {
case OperationStatus.processing:
return loc.operationStatusProcessing;
case OperationStatus.success:
return loc.operationStatusSuccess;
case OperationStatus.error:
return loc.operationStatusError;
}
}
}

View File

@@ -0,0 +1,6 @@
enum PaymentType {
bankAccount,
iban,
wallet,
card,
}

View File

@@ -0,0 +1,11 @@
class UploadHistoryItem {
final String name;
final String status;
final String time;
UploadHistoryItem({
required this.name,
required this.status,
required this.time,
});
}

View File

@@ -0,0 +1,22 @@
import 'package:pshared/config/constants.dart';
abstract class PermissionBound {
String get permissionRef;
String get organizationRef;
}
class _PermissionBoundImp implements PermissionBound {
@override
final String permissionRef;
@override
final String organizationRef;
const _PermissionBoundImp({
required this.permissionRef,
required this.organizationRef,
});
}
PermissionBound newPermissionBound({ required String organizationRef, String? permissionRef}) =>
_PermissionBoundImp(permissionRef: permissionRef ?? Constants.nilObjectRef, organizationRef: organizationRef);

View File

@@ -0,0 +1,6 @@
import 'package:pshared/models/permission_bound.dart';
import 'package:pshared/models/storable.dart';
abstract class PermissionBoundStorable implements PermissionBound, Storable {
}

View File

@@ -0,0 +1,13 @@
import 'package:pshared/models/permissions/data/permissions.dart';
import 'package:pshared/models/permissions/descriptions/permissions.dart';
class UserAccess {
final PermissionsDescription descriptions;
final PermissionsData permissions;
const UserAccess({
required this.descriptions,
required this.permissions,
});
}

View File

@@ -0,0 +1,15 @@
enum Action {
create,
read,
update,
delete,
}
extension ActionExtension on Action {
String toShortString() => toString().split('.').last;
static Action fromString(String value) => Action.values.firstWhere(
(e) => e.toShortString() == value,
orElse: () => throw ArgumentError('Invalid action: $value'),
);
}

View File

@@ -0,0 +1,13 @@
import 'package:pshared/models/permissions/action.dart';
import 'package:pshared/models/permissions/effect.dart';
class ActionEffect {
final Action action; // The action allowed or denied
final Effect effect; // The effect of the policy ("allow" or "deny")
const ActionEffect({
required this.action,
required this.effect,
});
}

View File

@@ -0,0 +1,12 @@
import 'package:pshared/models/permissions/data/policy.dart';
class Permission {
final Policy policy;
final String accountRef;
const Permission({
required this.policy,
required this.accountRef,
});
}

View File

@@ -0,0 +1,16 @@
import 'package:pshared/models/permissions/data/permission.dart';
import 'package:pshared/models/permissions/data/policy.dart';
import 'package:pshared/models/permissions/data/role.dart';
class PermissionsData {
final List<Role> roles;
final List<Policy> policies;
final List<Permission> permissions;
const PermissionsData({
required this.roles,
required this.policies,
required this.permissions,
});
}

View File

@@ -0,0 +1,18 @@
import 'package:pshared/models/permissions/action_effect.dart';
class Policy {
final String roleDescriptionRef;
final String organizationRef;
final String descriptionRef;
final String? objectRef;
final ActionEffect effect;
const Policy({
required this.roleDescriptionRef,
required this.organizationRef,
required this.descriptionRef,
required this.objectRef,
required this.effect,
});
}

View File

@@ -0,0 +1,11 @@
class Role {
final String accountRef;
final String organizationRef;
final String descriptionRef;
const Role({
required this.accountRef,
required this.descriptionRef,
required this.organizationRef,
});
}

View File

@@ -0,0 +1,13 @@
import 'package:pshared/models/permissions/descriptions/policy.dart';
import 'package:pshared/models/permissions/descriptions/role.dart';
class PermissionsDescription {
final List<RoleDescription> roles;
final List<PolicyDescription> policies;
const PermissionsDescription({
required this.roles,
required this.policies,
});
}

View File

@@ -0,0 +1,22 @@
import 'package:pshared/models/resources.dart';
import 'package:pshared/models/storable.dart';
class PolicyDescription implements Storable {
final Storable storable;
final List<ResourceType>? resourceTypes;
final String? organizationRef;
@override
String get id => storable.id;
@override
DateTime get createdAt => storable.createdAt;
@override
DateTime get updatedAt => storable.updatedAt;
const PolicyDescription({
required this.storable,
required this.resourceTypes,
required this.organizationRef,
});
}

View File

@@ -0,0 +1,27 @@
import 'package:pshared/models/storable.dart';
class RoleDescription implements Storable {
final Storable storable;
@override
String get id => storable.id;
@override
DateTime get createdAt => storable.createdAt;
@override
DateTime get updatedAt => storable.updatedAt;
final String organizationRef;
const RoleDescription({
required this.storable,
required this.organizationRef,
});
factory RoleDescription.build({
required String organizationRef,
}) => RoleDescription(
storable: newStorable(),
organizationRef: organizationRef
);
}

View File

@@ -0,0 +1,13 @@
enum Effect {
allow,
deny,
}
extension EffectExtension on Effect {
String toShortString() => toString().split('.').last;
static Effect fromString(String value) => Effect.values.firstWhere(
(e) => e.toShortString() == value,
orElse: () => throw ArgumentError('Invalid effect: $value'),
);
}

View File

@@ -0,0 +1 @@
enum RecipientFilter { all, ready, registered, notRegistered }

View File

@@ -0,0 +1,78 @@
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 Recipient {
final String? avatarUrl; // network URL / local asset
final String name;
final String email;
final RecipientStatus status;
final RecipientType type;
final CardPaymentMethod? card;
final IbanPaymentMethod? iban;
final RussianBankAccountPaymentMethod? bank;
final WalletPaymentMethod? wallet;
const Recipient({
this.avatarUrl,
required this.name,
required this.email,
required this.status,
required this.type,
this.card,
this.iban,
this.bank,
this.wallet,
});
/// 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,
}) =>
Recipient(
avatarUrl: null,
name: name,
email: email,
status: status,
type: type,
card: card,
iban: iban,
bank: bank,
wallet: wallet,
);
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,
];
return searchable.any((field) => field?.toLowerCase().contains(q) ?? false);
}
}

View File

@@ -0,0 +1,22 @@
import 'package:flutter/widgets.dart';
import 'package:pshared/generated/i18n/ps_localizations.dart';
/// Possible payout readiness states.
enum RecipientStatus { ready, registered, notRegistered }
extension RecipientStatusExtension on RecipientStatus {
/// Human-readable, **localized** label for display in the UI.
String label(BuildContext context) {
final l10n = PSLocalizations.of(context)!;
switch (this) {
case RecipientStatus.ready:
return l10n.statusReady;
case RecipientStatus.registered:
return l10n.statusRegistered;
case RecipientStatus.notRegistered:
return l10n.statusNotRegistered;
}
}
}

View File

@@ -0,0 +1,15 @@
import 'package:flutter/widgets.dart';
import 'package:pshared/generated/i18n/ps_localizations.dart';
/// Indicates whether you (internal) or the other party (external) manage payout data.
enum RecipientType { internal, external }
extension RecipientTypeExtension on RecipientType {
/// Localized label no opaque abbreviations.
String label(BuildContext context) =>
this == RecipientType.internal
? PSLocalizations.of(context)!.typeInternal
: PSLocalizations.of(context)!.typeExternal;
}

Some files were not shown because too many files have changed in this diff Show More