devKA #4
16
frontend/pshared/.gitignore
vendored
Normal file
16
frontend/pshared/.gitignore
vendored
Normal 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
|
||||
3
frontend/pshared/CHANGELOG.md
Normal file
3
frontend/pshared/CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.0.0
|
||||
|
||||
- Initial version.
|
||||
39
frontend/pshared/README.md
Normal file
39
frontend/pshared/README.md
Normal 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.
|
||||
30
frontend/pshared/analysis_options.yaml
Normal file
30
frontend/pshared/analysis_options.yaml
Normal 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
|
||||
BIN
frontend/pshared/assets/flag_of_catalonia.si
Normal file
BIN
frontend/pshared/assets/flag_of_catalonia.si
Normal file
Binary file not shown.
7
frontend/pshared/l10n.yaml
Normal file
7
frontend/pshared/l10n.yaml
Normal 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
|
||||
@@ -0,0 +1,2 @@
|
||||
class AuthorizationFailed implements Exception {
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
class ErrorFailedToReadImage implements Exception {
|
||||
}
|
||||
2
frontend/pshared/lib/api/errors/unauthorized.dart
Normal file
2
frontend/pshared/lib/api/errors/unauthorized.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
class ErrorUnauthorized implements Exception {
|
||||
}
|
||||
2
frontend/pshared/lib/api/errors/upload_failed.dart
Normal file
2
frontend/pshared/lib/api/errors/upload_failed.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
class ErrorUploadFailed implements Exception {
|
||||
}
|
||||
18
frontend/pshared/lib/api/requests/change_password.dart
Normal file
18
frontend/pshared/lib/api/requests/change_password.dart
Normal 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);
|
||||
}
|
||||
18
frontend/pshared/lib/api/requests/change_role.dart
Normal file
18
frontend/pshared/lib/api/requests/change_role.dart
Normal 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);
|
||||
}
|
||||
15
frontend/pshared/lib/api/requests/file_upload.dart
Normal file
15
frontend/pshared/lib/api/requests/file_upload.dart
Normal 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);
|
||||
}
|
||||
24
frontend/pshared/lib/api/requests/login.dart
Normal file
24
frontend/pshared/lib/api/requests/login.dart
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
42
frontend/pshared/lib/api/requests/signup.dart
Normal file
42
frontend/pshared/lib/api/requests/signup.dart
Normal 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);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import 'package:pshared/api/requests/tokens/refresh_rotate.dart';
|
||||
|
||||
|
||||
typedef AccessTokenRefreshRequest = RotateRefreshTokenRequest;
|
||||
21
frontend/pshared/lib/api/requests/tokens/refresh_rotate.dart
Normal file
21
frontend/pshared/lib/api/requests/tokens/refresh_rotate.dart
Normal 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);
|
||||
}
|
||||
18
frontend/pshared/lib/api/requests/tokens/session_id.dart
Normal file
18
frontend/pshared/lib/api/requests/tokens/session_id.dart
Normal 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);
|
||||
}
|
||||
18
frontend/pshared/lib/api/responses/account.dart
Normal file
18
frontend/pshared/lib/api/responses/account.dart
Normal 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);
|
||||
}
|
||||
16
frontend/pshared/lib/api/responses/base.dart
Normal file
16
frontend/pshared/lib/api/responses/base.dart
Normal 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);
|
||||
}
|
||||
18
frontend/pshared/lib/api/responses/employees.dart
Normal file
18
frontend/pshared/lib/api/responses/employees.dart
Normal 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);
|
||||
}
|
||||
13
frontend/pshared/lib/api/responses/error/connectivity.dart
Normal file
13
frontend/pshared/lib/api/responses/error/connectivity.dart
Normal 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)';
|
||||
}
|
||||
}
|
||||
43
frontend/pshared/lib/api/responses/error/server.dart
Normal file
43
frontend/pshared/lib/api/responses/error/server.dart
Normal 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);
|
||||
}
|
||||
15
frontend/pshared/lib/api/responses/file_uploaded.dart
Normal file
15
frontend/pshared/lib/api/responses/file_uploaded.dart
Normal 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);
|
||||
}
|
||||
19
frontend/pshared/lib/api/responses/login.dart
Normal file
19
frontend/pshared/lib/api/responses/login.dart
Normal 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);
|
||||
}
|
||||
19
frontend/pshared/lib/api/responses/message.dart
Normal file
19
frontend/pshared/lib/api/responses/message.dart
Normal 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);
|
||||
}
|
||||
19
frontend/pshared/lib/api/responses/organization.dart
Normal file
19
frontend/pshared/lib/api/responses/organization.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/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);
|
||||
}
|
||||
21
frontend/pshared/lib/api/responses/policies.dart
Normal file
21
frontend/pshared/lib/api/responses/policies.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/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);
|
||||
}
|
||||
15
frontend/pshared/lib/api/responses/token.dart
Normal file
15
frontend/pshared/lib/api/responses/token.dart
Normal 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);
|
||||
}
|
||||
32
frontend/pshared/lib/api/responses/type.dart
Normal file
32
frontend/pshared/lib/api/responses/type.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
47
frontend/pshared/lib/config/common.dart
Normal file
47
frontend/pshared/lib/config/common.dart
Normal 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']));
|
||||
}
|
||||
}
|
||||
}
|
||||
2
frontend/pshared/lib/config/constants.dart
Normal file
2
frontend/pshared/lib/config/constants.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
export 'mobile.dart'
|
||||
if (dart.library.html) 'web.dart';
|
||||
35
frontend/pshared/lib/config/mobile.dart
Normal file
35
frontend/pshared/lib/config/mobile.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
frontend/pshared/lib/config/web.dart
Normal file
75
frontend/pshared/lib/config/web.dart
Normal 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 object’s 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 didn’t 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
frontend/pshared/lib/data/dto/account/account.dart
Normal file
25
frontend/pshared/lib/data/dto/account/account.dart
Normal 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);
|
||||
}
|
||||
27
frontend/pshared/lib/data/dto/account/base.dart
Normal file
27
frontend/pshared/lib/data/dto/account/base.dart
Normal 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);
|
||||
}
|
||||
23
frontend/pshared/lib/data/dto/organization.dart
Normal file
23
frontend/pshared/lib/data/dto/organization.dart
Normal 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);
|
||||
}
|
||||
16
frontend/pshared/lib/data/dto/organization/description.dart
Normal file
16
frontend/pshared/lib/data/dto/organization/description.dart
Normal 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);
|
||||
}
|
||||
22
frontend/pshared/lib/data/dto/permissions/access.dart
Normal file
22
frontend/pshared/lib/data/dto/permissions/access.dart
Normal 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);
|
||||
}
|
||||
|
||||
19
frontend/pshared/lib/data/dto/permissions/action_effect.dart
Normal file
19
frontend/pshared/lib/data/dto/permissions/action_effect.dart
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
26
frontend/pshared/lib/data/dto/permissions/data/policy.dart
Normal file
26
frontend/pshared/lib/data/dto/permissions/data/policy.dart
Normal 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);
|
||||
}
|
||||
20
frontend/pshared/lib/data/dto/permissions/data/role.dart
Normal file
20
frontend/pshared/lib/data/dto/permissions/data/role.dart
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
0
frontend/pshared/lib/data/dto/pfe/services.dart
Normal file
0
frontend/pshared/lib/data/dto/pfe/services.dart
Normal file
20
frontend/pshared/lib/data/dto/storable.dart
Normal file
20
frontend/pshared/lib/data/dto/storable.dart
Normal 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);
|
||||
}
|
||||
BIN
frontend/pshared/lib/data/mapper/.DS_Store
vendored
Normal file
BIN
frontend/pshared/lib/data/mapper/.DS_Store
vendored
Normal file
Binary file not shown.
26
frontend/pshared/lib/data/mapper/account/account.dart
Normal file
26
frontend/pshared/lib/data/mapper/account/account.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
24
frontend/pshared/lib/data/mapper/account/base.dart
Normal file
24
frontend/pshared/lib/data/mapper/account/base.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
164
frontend/pshared/lib/data/mapper/icon.dart
Normal file
164
frontend/pshared/lib/data/mapper/icon.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
22
frontend/pshared/lib/data/mapper/organization.dart
Normal file
22
frontend/pshared/lib/data/mapper/organization.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
25
frontend/pshared/lib/data/mapper/permissions/data/role.dart
Normal file
25
frontend/pshared/lib/data/mapper/permissions/data/role.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
11
frontend/pshared/lib/data/mapper/storable.dart
Normal file
11
frontend/pshared/lib/data/mapper/storable.dart
Normal 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);
|
||||
}
|
||||
23
frontend/pshared/lib/l10n/ps_en.arb
Normal file
23
frontend/pshared/lib/l10n/ps_en.arb
Normal 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"
|
||||
}
|
||||
}
|
||||
36
frontend/pshared/lib/models/account/account.dart
Normal file
36
frontend/pshared/lib/models/account/account.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
35
frontend/pshared/lib/models/account/base.dart
Normal file
35
frontend/pshared/lib/models/account/base.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class OrganizationDescription {
|
||||
final String? logoUrl;
|
||||
|
||||
const OrganizationDescription({
|
||||
this.logoUrl,
|
||||
});
|
||||
}
|
||||
4
frontend/pshared/lib/models/organization/employee.dart
Normal file
4
frontend/pshared/lib/models/organization/employee.dart
Normal file
@@ -0,0 +1,4 @@
|
||||
import 'package:pshared/models/account/account.dart';
|
||||
|
||||
|
||||
typedef Employee = Account;
|
||||
34
frontend/pshared/lib/models/organization/organization.dart
Normal file
34
frontend/pshared/lib/models/organization/organization.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
18
frontend/pshared/lib/models/payment/methods/card.dart
Normal file
18
frontend/pshared/lib/models/payment/methods/card.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
6
frontend/pshared/lib/models/payment/methods/data.dart
Normal file
6
frontend/pshared/lib/models/payment/methods/data.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'package:pshared/models/payment/type.dart';
|
||||
|
||||
|
||||
abstract class PaymentMethodData {
|
||||
PaymentType get type;
|
||||
}
|
||||
20
frontend/pshared/lib/models/payment/methods/iban.dart
Normal file
20
frontend/pshared/lib/models/payment/methods/iban.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
21
frontend/pshared/lib/models/payment/methods/type.dart
Normal file
21
frontend/pshared/lib/models/payment/methods/type.dart
Normal 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;
|
||||
}
|
||||
12
frontend/pshared/lib/models/payment/methods/wallet.dart
Normal file
12
frontend/pshared/lib/models/payment/methods/wallet.dart
Normal 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});
|
||||
}
|
||||
30
frontend/pshared/lib/models/payment/operation.dart
Normal file
30
frontend/pshared/lib/models/payment/operation.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
25
frontend/pshared/lib/models/payment/status.dart
Normal file
25
frontend/pshared/lib/models/payment/status.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
frontend/pshared/lib/models/payment/type.dart
Normal file
6
frontend/pshared/lib/models/payment/type.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
enum PaymentType {
|
||||
bankAccount,
|
||||
iban,
|
||||
wallet,
|
||||
card,
|
||||
}
|
||||
11
frontend/pshared/lib/models/payment/upload_history_item.dart
Normal file
11
frontend/pshared/lib/models/payment/upload_history_item.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
22
frontend/pshared/lib/models/permission_bound.dart
Normal file
22
frontend/pshared/lib/models/permission_bound.dart
Normal 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);
|
||||
@@ -0,0 +1,6 @@
|
||||
import 'package:pshared/models/permission_bound.dart';
|
||||
import 'package:pshared/models/storable.dart';
|
||||
|
||||
|
||||
abstract class PermissionBoundStorable implements PermissionBound, Storable {
|
||||
}
|
||||
13
frontend/pshared/lib/models/permissions/access.dart
Normal file
13
frontend/pshared/lib/models/permissions/access.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
15
frontend/pshared/lib/models/permissions/action.dart
Normal file
15
frontend/pshared/lib/models/permissions/action.dart
Normal 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'),
|
||||
);
|
||||
}
|
||||
13
frontend/pshared/lib/models/permissions/action_effect.dart
Normal file
13
frontend/pshared/lib/models/permissions/action_effect.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
12
frontend/pshared/lib/models/permissions/data/permission.dart
Normal file
12
frontend/pshared/lib/models/permissions/data/permission.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
18
frontend/pshared/lib/models/permissions/data/policy.dart
Normal file
18
frontend/pshared/lib/models/permissions/data/policy.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
11
frontend/pshared/lib/models/permissions/data/role.dart
Normal file
11
frontend/pshared/lib/models/permissions/data/role.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
13
frontend/pshared/lib/models/permissions/effect.dart
Normal file
13
frontend/pshared/lib/models/permissions/effect.dart
Normal 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'),
|
||||
);
|
||||
}
|
||||
0
frontend/pshared/lib/models/pfe/services.dart
Normal file
0
frontend/pshared/lib/models/pfe/services.dart
Normal file
1
frontend/pshared/lib/models/recipient/filter.dart
Normal file
1
frontend/pshared/lib/models/recipient/filter.dart
Normal file
@@ -0,0 +1 @@
|
||||
enum RecipientFilter { all, ready, registered, notRegistered }
|
||||
78
frontend/pshared/lib/models/recipient/recipient.dart
Normal file
78
frontend/pshared/lib/models/recipient/recipient.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
22
frontend/pshared/lib/models/recipient/status.dart
Normal file
22
frontend/pshared/lib/models/recipient/status.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
frontend/pshared/lib/models/recipient/type.dart
Normal file
15
frontend/pshared/lib/models/recipient/type.dart
Normal 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
Reference in New Issue
Block a user