diff --git a/.DS_Store b/.DS_Store index cd92a4c..db2565e 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/api/chain/gateway/go.mod b/api/chain/gateway/go.mod index 0aa5d97..91cf4f5 100644 --- a/api/chain/gateway/go.mod +++ b/api/chain/gateway/go.mod @@ -22,9 +22,9 @@ require ( require ( github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251110112254-48a6e677648f // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251117160429-c598d23eddcf // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.24.3 // indirect + github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect github.com/casbin/casbin/v2 v2.134.0 // indirect github.com/casbin/govaluate v1.10.0 // indirect diff --git a/api/chain/gateway/go.sum b/api/chain/gateway/go.sum index 5654a7d..9fdd6f2 100644 --- a/api/chain/gateway/go.sum +++ b/api/chain/gateway/go.sum @@ -8,12 +8,16 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251110112254-48a6e677648f h1:B/TfTw73mVqWKDzJZhU9Qi9wQyYfmiCz9FnmpQsyv5M= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251110112254-48a6e677648f/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251117160429-c598d23eddcf h1:aZI2VRIP0LAI6Rw934WEAxxL0SNYSVt9vR9h/cP5Pbo= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251117160429-c598d23eddcf/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.24.3 h1:Bte86SlO3lwPQqww+7BE9ZuUCKIjfqnG5jtEyqA9y9Y= github.com/bits-and-blooms/bitset v1.24.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= +github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= diff --git a/api/pkg/model/account.go b/api/pkg/model/account.go index 09cae4f..271f843 100755 --- a/api/pkg/model/account.go +++ b/api/pkg/model/account.go @@ -13,6 +13,7 @@ type AccountBase struct { storable.Base `bson:",inline" json:",inline"` ArchivableBase `bson:",inline" json:",inline"` Describable `bson:",inline" json:",inline"` + LastName string `bson:"lastName" json:"lastName"` AvatarURL *string `bson:"avatarUrl,omitempty" json:"avatarUrl,omitempty"` } diff --git a/api/pkg/model/userdata.go b/api/pkg/model/userdata.go index d06095e..761d5d0 100644 --- a/api/pkg/model/userdata.go +++ b/api/pkg/model/userdata.go @@ -13,6 +13,7 @@ type LoginData struct { type AccountData struct { LoginData `bson:",inline" json:",inline"` Describable `bson:",inline" json:",inline"` + LastName string `bson:"lastName" json:"lastName"` } func (ad *AccountData) ToAccount() *Account { diff --git a/api/server/go.mod b/api/server/go.mod index fd249c5..b1fcb5a 100644 --- a/api/server/go.mod +++ b/api/server/go.mod @@ -8,9 +8,9 @@ replace github.com/tech/sendico/chain/gateway => ../chain/gateway require ( github.com/aws/aws-sdk-go-v2 v1.39.6 - github.com/aws/aws-sdk-go-v2/config v1.31.18 - github.com/aws/aws-sdk-go-v2/credentials v1.18.22 - github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0 + github.com/aws/aws-sdk-go-v2/config v1.31.20 + github.com/aws/aws-sdk-go-v2/credentials v1.18.24 + github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 github.com/go-chi/chi/v5 v5.2.3 github.com/go-chi/cors v1.2.2 github.com/go-chi/jwtauth/v5 v5.3.3 @@ -49,9 +49,9 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.40.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 // indirect github.com/aws/smithy-go v1.23.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/casbin/mongodb-adapter/v3 v3.7.0 // indirect diff --git a/api/server/go.sum b/api/server/go.sum index 8752498..5377dae 100644 --- a/api/server/go.sum +++ b/api/server/go.sum @@ -12,8 +12,12 @@ github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y= github.com/aws/aws-sdk-go-v2/config v1.31.18 h1:RouG3AcF2fLFhw+Z0qbnuIl9HZ0Kh4E/U9sKwTMRpMI= github.com/aws/aws-sdk-go-v2/config v1.31.18/go.mod h1:aXZ13mSQC8S2VEHwGfL1COMuJ1Zty6pX5xU7hyqjvCg= +github.com/aws/aws-sdk-go-v2/config v1.31.20 h1:/jWF4Wu90EhKCgjTdy1DGxcbcbNrjfBHvksEL79tfQc= +github.com/aws/aws-sdk-go-v2/config v1.31.20/go.mod h1:95Hh1Tc5VYKL9NJ7tAkDcqeKt+MCXQB1hQZaRdJIZE0= github.com/aws/aws-sdk-go-v2/credentials v1.18.22 h1:hyIVGBHhQPaNP9D4BaVRwpjLMCwMMdAkHqB3gGMiykU= github.com/aws/aws-sdk-go-v2/credentials v1.18.22/go.mod h1:B9E2qHs3/YGfeQZ4jrIE/nPvqxtyafZrJ5EQiZBG6pk= +github.com/aws/aws-sdk-go-v2/credentials v1.18.24 h1:iJ2FmPT35EaIB0+kMa6TnQ+PwG5A1prEdAw+PsMzfHg= +github.com/aws/aws-sdk-go-v2/credentials v1.18.24/go.mod h1:U91+DrfjAiXPDEGYhh/x29o4p0qHX5HDqG7y5VViv64= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= @@ -34,12 +38,20 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1a github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8= github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0 h1:ef6gIJR+xv/JQWwpa5FYirzoQctfSJm7tuDe3SZsUf8= github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw= +github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 h1:DhdbtDl4FdNlj31+xiRXANxEE+eC7n8JQz+/ilwQ8Uc= +github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw= github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0= github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 h1:NjShtS1t8r5LUfFVtFeI8xLAHQNTa7UI0VawXlrBMFQ= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.3/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 h1:gTsnx0xXNQ6SBbymoDvcoRHL+q4l/dAFsQuKfDWSaGc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= github.com/aws/aws-sdk-go-v2/service/sts v1.40.0 h1:ZGDJVmlpPFiNFCb/I42nYVKUanJAdFUiSmUo/32AqPQ= github.com/aws/aws-sdk-go-v2/service/sts v1.40.0/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= +github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 h1:HK5ON3KmQV2HcAunnx4sKLB9aPf3gKGwVAf7xnx0QT0= +github.com/aws/aws-sdk-go-v2/service/sts v1.40.2/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= diff --git a/api/server/interface/api/srequest/signup.go b/api/server/interface/api/srequest/signup.go index baac565..208666d 100644 --- a/api/server/interface/api/srequest/signup.go +++ b/api/server/interface/api/srequest/signup.go @@ -6,7 +6,5 @@ type Signup struct { Account model.AccountData `json:"account"` Organization model.Describable `json:"organization"` OrganizationTimeZone string `json:"organizationTimeZone"` - AnonymousUser model.Describable `json:"anonymousUser"` OwnerRole model.Describable `json:"ownerRole"` - AnonymousRole model.Describable `json:"anonymousRole"` } diff --git a/api/server/interface/api/srequest/signup_test.go b/api/server/interface/api/srequest/signup_test.go index 71107b7..f24d66a 100644 --- a/api/server/interface/api/srequest/signup_test.go +++ b/api/server/interface/api/srequest/signup_test.go @@ -28,15 +28,9 @@ func TestSignupRequest_JSONSerialization(t *testing.T) { Name: "Test Organization", }, OrganizationTimeZone: "UTC", - AnonymousUser: model.Describable{ - Name: "Anonymous User", - }, OwnerRole: model.Describable{ Name: "Owner", }, - AnonymousRole: model.Describable{ - Name: "Anonymous", - }, } // Test JSON marshaling @@ -55,9 +49,7 @@ func TestSignupRequest_JSONSerialization(t *testing.T) { assert.Equal(t, signup.Account.Password, unmarshaled.Account.Password) assert.Equal(t, signup.Organization.Name, unmarshaled.Organization.Name) assert.Equal(t, signup.OrganizationTimeZone, unmarshaled.OrganizationTimeZone) - assert.Equal(t, signup.AnonymousUser.Name, unmarshaled.AnonymousUser.Name) assert.Equal(t, signup.OwnerRole.Name, unmarshaled.OwnerRole.Name) - assert.Equal(t, signup.AnonymousRole.Name, unmarshaled.AnonymousRole.Name) } func TestSignupRequest_MinimalValidRequest(t *testing.T) { @@ -77,15 +69,9 @@ func TestSignupRequest_MinimalValidRequest(t *testing.T) { Name: "Test Organization", }, OrganizationTimeZone: "UTC", - AnonymousUser: model.Describable{ - Name: "Anonymous", - }, OwnerRole: model.Describable{ Name: "Owner", }, - AnonymousRole: model.Describable{ - Name: "Anonymous", - }, } // Test JSON marshaling @@ -141,15 +127,9 @@ func TestSignupRequest_UnicodeCharacters(t *testing.T) { Name: "测试 Organization", }, OrganizationTimeZone: "UTC", - AnonymousUser: model.Describable{ - Name: "匿名 User", - }, OwnerRole: model.Describable{ Name: "所有者", }, - AnonymousRole: model.Describable{ - Name: "匿名", - }, } // Test JSON marshaling @@ -166,7 +146,5 @@ func TestSignupRequest_UnicodeCharacters(t *testing.T) { assert.Equal(t, "测试@example.com", unmarshaled.Account.Login) assert.Equal(t, "Test 用户 Üser", unmarshaled.Account.Name) assert.Equal(t, "测试 Organization", unmarshaled.Organization.Name) - assert.Equal(t, "匿名 User", unmarshaled.AnonymousUser.Name) assert.Equal(t, "所有者", unmarshaled.OwnerRole.Name) - assert.Equal(t, "匿名", unmarshaled.AnonymousRole.Name) } diff --git a/api/server/interface/api/sresponse/signup_availability.go b/api/server/interface/api/sresponse/signupavailability.go similarity index 100% rename from api/server/interface/api/sresponse/signup_availability.go rename to api/server/interface/api/sresponse/signupavailability.go diff --git a/api/server/internal/server/accountapiimp/signup.go b/api/server/internal/server/accountapiimp/signup.go index c57df70..2f1474b 100644 --- a/api/server/internal/server/accountapiimp/signup.go +++ b/api/server/internal/server/accountapiimp/signup.go @@ -22,27 +22,6 @@ import ( "go.uber.org/zap" ) -func (a *AccountAPI) createAnonymousAccount(ctx context.Context, org *model.Organization, sr *srequest.Signup) error { - anonymousUser := &model.Account{ - AccountPublic: model.AccountPublic{ - AccountBase: model.AccountBase{ - Describable: sr.AnonymousUser, - }, - UserDataBase: sr.Account.UserDataBase, - }, - } - r, err := a.pmanager.Role().Create(ctx, org.ID, &sr.AnonymousRole) - if err != nil { - a.logger.Warn("Failed to create anonymous role", zap.Error(err)) - return err - } - if err := a.accService.CreateAccount(ctx, org, anonymousUser, r.ID); err != nil { - a.logger.Warn("Failed to create account", zap.Error(err), zap.String("login", anonymousUser.Login)) - return err - } - return nil -} - func (a *AccountAPI) createOrg(ctx context.Context, sr *srequest.Signup, permissionRef primitive.ObjectID) (*model.Organization, error) { name := strings.TrimSpace(sr.Organization.Name) if name == "" { @@ -175,10 +154,6 @@ func (a *AccountAPI) signupTransactionBody(ctx context.Context, sr *srequest.Sig return nil, err } - if err := a.createAnonymousAccount(ctx, org, sr); err != nil { - return nil, err - } - return nil, nil } diff --git a/api/server/internal/server/accountapiimp/signup_integration_test.go b/api/server/internal/server/accountapiimp/signup_integration_test.go index 3337eb8..664162d 100644 --- a/api/server/internal/server/accountapiimp/signup_integration_test.go +++ b/api/server/internal/server/accountapiimp/signup_integration_test.go @@ -73,15 +73,9 @@ func TestSignupRequestSerialization(t *testing.T) { Name: "Test Organization", }, OrganizationTimeZone: "UTC", - AnonymousUser: model.Describable{ - Name: "Anonymous User", - }, OwnerRole: model.Describable{ Name: "Owner", }, - AnonymousRole: model.Describable{ - Name: "Anonymous", - }, } // Store in MongoDB @@ -121,15 +115,9 @@ func TestSignupHTTPSerialization(t *testing.T) { Name: "Test Organization", }, OrganizationTimeZone: "UTC", - AnonymousUser: model.Describable{ - Name: "Anonymous User", - }, OwnerRole: model.Describable{ Name: "Owner", }, - AnonymousRole: model.Describable{ - Name: "Anonymous", - }, } t.Run("ValidJSONRequest", func(t *testing.T) { diff --git a/api/server/internal/server/accountapiimp/signup_test.go b/api/server/internal/server/accountapiimp/signup_test.go index 87687ce..9a366c6 100644 --- a/api/server/internal/server/accountapiimp/signup_test.go +++ b/api/server/internal/server/accountapiimp/signup_test.go @@ -74,15 +74,9 @@ func TestCreateValidSignupRequest(t *testing.T) { Name: "Test Organization", }, OrganizationTimeZone: "UTC", - AnonymousUser: model.Describable{ - Name: "Anonymous User", - }, OwnerRole: model.Describable{ Name: "Owner", }, - AnonymousRole: model.Describable{ - Name: "Anonymous", - }, } // Validate the request structure diff --git a/ci/scripts/common/bump_version.sh b/ci/scripts/common/bump_version.sh index 720d641..50b1656 100755 --- a/ci/scripts/common/bump_version.sh +++ b/ci/scripts/common/bump_version.sh @@ -21,9 +21,21 @@ fi CURRENT_VERSION="$(cat "${VERSION_FILE}")" NEXT_VERSION="$(printf '%s' "${CURRENT_VERSION}" | awk -F. -v OFS=. ' + function pad(value, width, result, i) { + result=value "" + if (length(result) >= width) { + return result + } + i = width - length(result) + while (i-- > 0) { + result = "0" result + } + return result + } NF==1 { print ++$NF; next } { - $NF=sprintf("%0*d", length($NF), ($NF+1)) + last = $NF + 1 + $NF = pad(last, length($NF)) print }')" diff --git a/frontend/.DS_Store b/frontend/.DS_Store new file mode 100644 index 0000000..5f303ac Binary files /dev/null and b/frontend/.DS_Store differ diff --git a/frontend/pshared/lib/.DS_Store b/frontend/pshared/lib/.DS_Store new file mode 100644 index 0000000..14a15a2 Binary files /dev/null and b/frontend/pshared/lib/.DS_Store differ diff --git a/frontend/pshared/lib/api/.DS_Store b/frontend/pshared/lib/api/.DS_Store new file mode 100644 index 0000000..9598a72 Binary files /dev/null and b/frontend/pshared/lib/api/.DS_Store differ diff --git a/frontend/pshared/lib/api/errors/authorization_failed.dart b/frontend/pshared/lib/api/errors/authorization_failed.dart index 7acc439..841ab8d 100644 --- a/frontend/pshared/lib/api/errors/authorization_failed.dart +++ b/frontend/pshared/lib/api/errors/authorization_failed.dart @@ -1,2 +1,22 @@ -class AuthorizationFailed implements Exception { +class AuthenticationFailedException implements Exception { + final String message; + final Exception? originalError; + + const AuthenticationFailedException(this.message, [this.originalError]); + + @override + String toString() { + return 'AuthenticationFailedException: $message${originalError != null ? ' (caused by: $originalError)' : ''}'; + } +} + +class CircuitBreakerOpenException implements Exception { + final String message; + + const CircuitBreakerOpenException(this.message); + + @override + String toString() { + return 'CircuitBreakerOpenException: $message'; + } } \ No newline at end of file diff --git a/frontend/pshared/lib/api/requests/.DS_Store b/frontend/pshared/lib/api/requests/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/frontend/pshared/lib/api/requests/.DS_Store differ diff --git a/frontend/pshared/lib/api/requests/login.dart b/frontend/pshared/lib/api/requests/login.dart index 532d132..57b4cce 100644 --- a/frontend/pshared/lib/api/requests/login.dart +++ b/frontend/pshared/lib/api/requests/login.dart @@ -1,20 +1,18 @@ import 'package:json_annotation/json_annotation.dart'; +import 'package:pshared/api/requests/login_data.dart'; + part 'login.g.dart'; @JsonSerializable(explicitToJson: true) class LoginRequest { - final String login; - final String password; - final String locale; + final LoginData login; final String clientId; final String deviceId; const LoginRequest({ required this.login, - required this.password, - required this.locale, required this.clientId, required this.deviceId, }); diff --git a/frontend/pshared/lib/api/requests/login_data.dart b/frontend/pshared/lib/api/requests/login_data.dart new file mode 100644 index 0000000..ec2bc35 --- /dev/null +++ b/frontend/pshared/lib/api/requests/login_data.dart @@ -0,0 +1,76 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'login_data.g.dart'; + + +@JsonSerializable(explicitToJson: true, constructor: 'build') +class LoginData { + final String login; + final String password; + final String locale; + + const LoginData._({ + required this.login, + required this.password, + required this.locale, + }); + + factory LoginData.build({ + required String login, + required String password, + required String locale, + }) => LoginData._( + login: login.trim().toLowerCase(), + password: password, + locale: locale, + ); + + factory LoginData.fromJson(Map json) => _$LoginDataFromJson(json); + Map toJson() => _$LoginDataToJson(this); +} + +@JsonSerializable(explicitToJson: true, constructor: 'buildIstance') +class AccountData extends LoginData { + final String name; + final String lastName; + + const AccountData._({ + required super.login, + required super.password, + required super.locale, + required this.name, + required this.lastName, + }) : super._(); + + factory AccountData.buildIstance({ + required String login, + required String password, + required String locale, + required String name, + required String lastName, + }) => AccountData._( + login: login, + password: password, + locale: locale, + name: name.trim(), + lastName: lastName.trim(), + ); + + factory AccountData.build({ + required LoginData login, + required String name, + required String lastName, + }) => AccountData.buildIstance( + login: login.login, + password: login.password, + locale: login.locale, + name: name, + lastName: lastName, + ); + + + factory AccountData.fromJson(Map json) => _$AccountDataFromJson(json); + + @override + Map toJson() => _$AccountDataToJson(this); +} diff --git a/frontend/pshared/lib/api/requests/change_password.dart b/frontend/pshared/lib/api/requests/password/change.dart similarity index 93% rename from frontend/pshared/lib/api/requests/change_password.dart rename to frontend/pshared/lib/api/requests/password/change.dart index 5bdb21f..f4da065 100644 --- a/frontend/pshared/lib/api/requests/change_password.dart +++ b/frontend/pshared/lib/api/requests/password/change.dart @@ -1,6 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; -part 'change_password.g.dart'; +part 'change.g.dart'; @JsonSerializable(explicitToJson: true) diff --git a/frontend/pshared/lib/api/requests/password/forgot.dart b/frontend/pshared/lib/api/requests/password/forgot.dart new file mode 100644 index 0000000..53eb53e --- /dev/null +++ b/frontend/pshared/lib/api/requests/password/forgot.dart @@ -0,0 +1,20 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'forgot.g.dart'; + + +@JsonSerializable() +class ForgotPasswordRequest { + final String login; + + const ForgotPasswordRequest({ + required this.login, + }); + + factory ForgotPasswordRequest.fromJson(Map json) => _$ForgotPasswordRequestFromJson(json); + Map toJson() => _$ForgotPasswordRequestToJson(this); + + static ForgotPasswordRequest build({ + required String login, + }) => ForgotPasswordRequest(login: login); +} diff --git a/frontend/pshared/lib/api/requests/password/reset.dart b/frontend/pshared/lib/api/requests/password/reset.dart new file mode 100644 index 0000000..701a3ba --- /dev/null +++ b/frontend/pshared/lib/api/requests/password/reset.dart @@ -0,0 +1,20 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'reset.g.dart'; + + +@JsonSerializable() +class ResetPasswordRequest { + final String password; + + const ResetPasswordRequest({ + required this.password, + }); + + factory ResetPasswordRequest.fromJson(Map json) => _$ResetPasswordRequestFromJson(json); + Map toJson() => _$ResetPasswordRequestToJson(this); + + static ResetPasswordRequest build({ + required String password, + }) => ResetPasswordRequest(password: password); +} \ No newline at end of file diff --git a/frontend/pshared/lib/api/requests/signup.dart b/frontend/pshared/lib/api/requests/signup.dart index 6af1cc8..64d5c2a 100644 --- a/frontend/pshared/lib/api/requests/signup.dart +++ b/frontend/pshared/lib/api/requests/signup.dart @@ -1,82 +1,39 @@ import 'package:json_annotation/json_annotation.dart'; +import 'package:pshared/api/requests/login_data.dart'; +import 'package:pshared/data/dto/describable.dart'; +import 'package:pshared/data/mapper/describable.dart'; +import 'package:pshared/models/describable.dart'; + part 'signup.g.dart'; @JsonSerializable(explicitToJson: true) class SignupRequest { - final SignupAccount account; - final DescribableRequest organization; + final AccountData account; + final DescribableDTO organization; final String organizationTimeZone; - final DescribableRequest anonymousUser; - final DescribableRequest ownerRole; - final DescribableRequest anonymousRole; + final DescribableDTO ownerRole; const SignupRequest({ required this.account, required this.organization, required this.organizationTimeZone, - required this.anonymousUser, required this.ownerRole, - required this.anonymousRole, }); factory SignupRequest.build({ - required String name, - required String login, - required String password, - required String locale, - required String organizationName, + required AccountData account, + required Describable organization, required String organizationTimeZone, - }) => - SignupRequest( - account: SignupAccount( - name: name, - login: login, - password: password, - locale: locale, - ), - organization: DescribableRequest(name: organizationName), - organizationTimeZone: organizationTimeZone, - anonymousUser: const DescribableRequest(name: 'Anonymous'), - ownerRole: const DescribableRequest(name: 'Owner'), - anonymousRole: const DescribableRequest(name: 'Anonymous'), - ); + required Describable ownerRole, + }) => SignupRequest( + account: account, + organization: organization.toDTO(), + organizationTimeZone: organizationTimeZone, + ownerRole: ownerRole.toDTO(), + ); - factory SignupRequest.fromJson(Map json) => - _$SignupRequestFromJson(json); + factory SignupRequest.fromJson(Map json) => _$SignupRequestFromJson(json); Map toJson() => _$SignupRequestToJson(this); } - -@JsonSerializable() -class SignupAccount { - final String name; - final String login; - final String password; - final String locale; - final String? description; - - const SignupAccount({ - required this.name, - required this.login, - required this.password, - required this.locale, - this.description, - }); - - factory SignupAccount.fromJson(Map json) => - _$SignupAccountFromJson(json); - Map toJson() => _$SignupAccountToJson(this); -} - -@JsonSerializable() -class DescribableRequest { - final String name; - final String? description; - - const DescribableRequest({required this.name, this.description}); - - factory DescribableRequest.fromJson(Map json) => - _$DescribableRequestFromJson(json); - Map toJson() => _$DescribableRequestToJson(this); -} diff --git a/frontend/pshared/lib/data/dto/account/account.dart b/frontend/pshared/lib/data/dto/account/account.dart index b1a080b..f89de61 100644 --- a/frontend/pshared/lib/data/dto/account/account.dart +++ b/frontend/pshared/lib/data/dto/account/account.dart @@ -1,6 +1,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:pshared/data/dto/account/base.dart'; +import 'package:pshared/data/dto/date_time.dart'; part 'account.g.dart'; @@ -8,14 +9,17 @@ part 'account.g.dart'; @JsonSerializable() class AccountDTO extends AccountBaseDTO { final String login; + final String locale; const AccountDTO({ required super.id, required super.createdAt, required super.updatedAt, required super.name, + required super.lastName, + required super.description, required super.avatarUrl, - required super.locale, + required this.locale, required this.login, }); diff --git a/frontend/pshared/lib/data/dto/account/base.dart b/frontend/pshared/lib/data/dto/account/base.dart index 0ae451d..453b70e 100644 --- a/frontend/pshared/lib/data/dto/account/base.dart +++ b/frontend/pshared/lib/data/dto/account/base.dart @@ -1,6 +1,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:pshared/data/dto/storable.dart'; +import 'package:pshared/data/dto/date_time.dart'; part 'base.g.dart'; @@ -8,7 +9,8 @@ part 'base.g.dart'; @JsonSerializable() class AccountBaseDTO extends StorableDTO { final String name; - final String locale; + final String lastName; + final String? description; final String? avatarUrl; const AccountBaseDTO({ @@ -16,8 +18,9 @@ class AccountBaseDTO extends StorableDTO { required super.createdAt, required super.updatedAt, required this.name, + required this.description, required this.avatarUrl, - required this.locale, + required this.lastName, }); factory AccountBaseDTO.fromJson(Map json) => _$AccountBaseDTOFromJson(json); diff --git a/frontend/pshared/lib/data/dto/date_time.dart b/frontend/pshared/lib/data/dto/date_time.dart new file mode 100644 index 0000000..f92e62f --- /dev/null +++ b/frontend/pshared/lib/data/dto/date_time.dart @@ -0,0 +1,12 @@ +import 'package:json_annotation/json_annotation.dart'; + + +class UtcIso8601Converter implements JsonConverter { + const UtcIso8601Converter(); + + @override + DateTime fromJson(String json) => DateTime.parse(json).toUtc(); + + @override + String toJson(DateTime value) => value.toUtc().toIso8601String(); +} diff --git a/frontend/pshared/lib/data/dto/describable.dart b/frontend/pshared/lib/data/dto/describable.dart new file mode 100644 index 0000000..39e912d --- /dev/null +++ b/frontend/pshared/lib/data/dto/describable.dart @@ -0,0 +1,18 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'describable.g.dart'; + + +@JsonSerializable() +class DescribableDTO { + final String name; + final String? description; + + const DescribableDTO({ + required this.name, + this.description, + }); + + factory DescribableDTO.fromJson(Map json) => _$DescribableDTOFromJson(json); + Map toJson() => _$DescribableDTOToJson(this); +} diff --git a/frontend/pshared/lib/data/dto/organization.dart b/frontend/pshared/lib/data/dto/organization.dart index 9e5cc76..d482a0c 100644 --- a/frontend/pshared/lib/data/dto/organization.dart +++ b/frontend/pshared/lib/data/dto/organization.dart @@ -1,18 +1,28 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:pshared/data/dto/storable.dart'; + +import 'package:pshared/data/dto/date_time.dart'; +import 'package:pshared/data/dto/permissions/bound.dart'; part 'organization.g.dart'; @JsonSerializable() -class OrganizationDTO extends StorableDTO { +class OrganizationDTO extends PermissionBoundDTO { + final String name; + final String? description; final String timeZone; final String? logoUrl; + final String tenantRef; const OrganizationDTO({ required super.id, required super.createdAt, required super.updatedAt, + required super.permissionRef, + required super.organizationRef, + required this.name, + required this.tenantRef, + this.description, required this.timeZone, this.logoUrl, }); diff --git a/frontend/pshared/lib/data/dto/organization/bound.dart b/frontend/pshared/lib/data/dto/organization/bound.dart new file mode 100644 index 0000000..8c5bd07 --- /dev/null +++ b/frontend/pshared/lib/data/dto/organization/bound.dart @@ -0,0 +1,23 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'package:pshared/data/dto/storable.dart'; +import 'package:pshared/data/dto/date_time.dart'; + +part 'bound.g.dart'; + + +@JsonSerializable() +class OrganizationBoundDTO extends StorableDTO { + final String organizationRef; + + const OrganizationBoundDTO({ + required super.id, + required super.createdAt, + required super.updatedAt, + required this.organizationRef, + }); + + factory OrganizationBoundDTO.fromJson(Map json) => _$OrganizationBoundDTOFromJson(json); + @override + Map toJson() => _$OrganizationBoundDTOToJson(this); +} diff --git a/frontend/pshared/lib/data/dto/organization/description.dart b/frontend/pshared/lib/data/dto/organization/description.dart index 2814f8b..efe2f62 100644 --- a/frontend/pshared/lib/data/dto/organization/description.dart +++ b/frontend/pshared/lib/data/dto/organization/description.dart @@ -1,13 +1,17 @@ import 'package:json_annotation/json_annotation.dart'; +import 'package:pshared/data/dto/describable.dart'; + part 'description.g.dart'; @JsonSerializable() class OrganizationDescriptionDTO { + final DescribableDTO description; final String? logoUrl; const OrganizationDescriptionDTO({ + required this.description, this.logoUrl, }); diff --git a/frontend/pshared/lib/data/dto/permissions/bound.dart b/frontend/pshared/lib/data/dto/permissions/bound.dart new file mode 100644 index 0000000..02eb0d8 --- /dev/null +++ b/frontend/pshared/lib/data/dto/permissions/bound.dart @@ -0,0 +1,26 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'package:pshared/data/dto/date_time.dart'; +import 'package:pshared/data/dto/storable.dart'; + +part 'bound.g.dart'; + + +@JsonSerializable() +class PermissionBoundDTO extends StorableDTO { + final String permissionRef; + final String organizationRef; + + const PermissionBoundDTO({ + required super.id, + required super.createdAt, + required super.updatedAt, + required this.permissionRef, + required this.organizationRef, + }); + + factory PermissionBoundDTO.fromJson(Map json) => _$PermissionBoundDTOFromJson(json); + + @override + Map toJson() => _$PermissionBoundDTOToJson(this); +} \ No newline at end of file diff --git a/frontend/pshared/lib/data/dto/permissions/description/policy.dart b/frontend/pshared/lib/data/dto/permissions/description/policy.dart index 04f4e43..1629e6c 100644 --- a/frontend/pshared/lib/data/dto/permissions/description/policy.dart +++ b/frontend/pshared/lib/data/dto/permissions/description/policy.dart @@ -1,12 +1,14 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:pshared/data/dto/storable.dart'; + +import 'package:pshared/data/dto/storable/describable.dart'; +import 'package:pshared/data/dto/date_time.dart'; import 'package:pshared/models/resources.dart'; part 'policy.g.dart'; @JsonSerializable() -class PolicyDescriptionDTO extends StorableDTO { +class PolicyDescriptionDTO extends StorableDescribabaleDTO { final List? resourceTypes; final String? organizationRef; @@ -14,6 +16,8 @@ class PolicyDescriptionDTO extends StorableDTO { required super.id, required super.createdAt, required super.updatedAt, + required super.name, + required super.description, required this.resourceTypes, required this.organizationRef, }); diff --git a/frontend/pshared/lib/data/dto/permissions/description/role.dart b/frontend/pshared/lib/data/dto/permissions/description/role.dart index 8b92caf..d40e0d3 100644 --- a/frontend/pshared/lib/data/dto/permissions/description/role.dart +++ b/frontend/pshared/lib/data/dto/permissions/description/role.dart @@ -1,17 +1,21 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:pshared/data/dto/storable.dart'; + +import 'package:pshared/data/dto/date_time.dart'; +import 'package:pshared/data/dto/storable/describable.dart'; part 'role.g.dart'; @JsonSerializable() -class RoleDescriptionDTO extends StorableDTO { +class RoleDescriptionDTO extends StorableDescribabaleDTO { final String organizationRef; const RoleDescriptionDTO({ required super.id, required super.createdAt, required super.updatedAt, + required super.name, + required super.description, required this.organizationRef, }); diff --git a/frontend/pshared/lib/data/dto/reference.dart b/frontend/pshared/lib/data/dto/reference.dart new file mode 100644 index 0000000..31e7bf8 --- /dev/null +++ b/frontend/pshared/lib/data/dto/reference.dart @@ -0,0 +1,16 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'reference.g.dart'; + + +@JsonSerializable() +class ReferenceDTO { + final String ref; + + const ReferenceDTO({ + required this.ref, + }); + + factory ReferenceDTO.fromJson(Map json) => _$ReferenceDTOFromJson(json); + Map toJson() => _$ReferenceDTOToJson(this); +} diff --git a/frontend/pshared/lib/data/dto/storable.dart b/frontend/pshared/lib/data/dto/storable.dart index b189dff..d9ea76e 100644 --- a/frontend/pshared/lib/data/dto/storable.dart +++ b/frontend/pshared/lib/data/dto/storable.dart @@ -1,12 +1,18 @@ import 'package:json_annotation/json_annotation.dart'; +import 'package:pshared/data/dto/date_time.dart'; + part 'storable.g.dart'; @JsonSerializable() class StorableDTO { final String id; + + @UtcIso8601Converter() final DateTime createdAt; + + @UtcIso8601Converter() final DateTime updatedAt; const StorableDTO({ diff --git a/frontend/pshared/lib/data/dto/storable/describable.dart b/frontend/pshared/lib/data/dto/storable/describable.dart new file mode 100644 index 0000000..42af849 --- /dev/null +++ b/frontend/pshared/lib/data/dto/storable/describable.dart @@ -0,0 +1,26 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'package:pshared/data/dto/date_time.dart'; +import 'package:pshared/data/dto/storable.dart'; + +part 'describable.g.dart'; + + +@JsonSerializable() +class StorableDescribabaleDTO extends StorableDTO { + final String name; + final String? description; + + const StorableDescribabaleDTO({ + required super.id, + required super.createdAt, + required super.updatedAt, + required this.name, + this.description, + }); + + factory StorableDescribabaleDTO.fromJson(Map json) => _$StorableDescribabaleDTOFromJson(json); + + @override + Map toJson() => _$StorableDescribabaleDTOToJson(this); +} \ No newline at end of file diff --git a/frontend/pshared/lib/data/mapper/account/account.dart b/frontend/pshared/lib/data/mapper/account/account.dart index fd13dca..6ab6523 100644 --- a/frontend/pshared/lib/data/mapper/account/account.dart +++ b/frontend/pshared/lib/data/mapper/account/account.dart @@ -1,5 +1,6 @@ import 'package:pshared/data/dto/account/account.dart'; import 'package:pshared/models/account/account.dart'; +import 'package:pshared/models/describable.dart'; import 'package:pshared/models/storable.dart'; @@ -9,6 +10,8 @@ extension AccountMapper on Account { createdAt: createdAt, updatedAt: updatedAt, name: name, + lastName: lastName, + description: description, avatarUrl: avatarUrl, locale: locale, login: login, @@ -19,8 +22,9 @@ extension AccountDTOMapper on AccountDTO { Account toDomain() => Account( storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt), avatarUrl: avatarUrl, + describable: newDescribable(name: name, description: description), + lastName: lastName, locale: locale, login: login, - name: name, ); } diff --git a/frontend/pshared/lib/data/mapper/account/base.dart b/frontend/pshared/lib/data/mapper/account/base.dart index f0241ef..430bed7 100644 --- a/frontend/pshared/lib/data/mapper/account/base.dart +++ b/frontend/pshared/lib/data/mapper/account/base.dart @@ -1,5 +1,6 @@ import 'package:pshared/data/dto/account/base.dart'; import 'package:pshared/models/account/base.dart'; +import 'package:pshared/models/describable.dart'; import 'package:pshared/models/storable.dart'; @@ -8,17 +9,18 @@ extension AccountBaseMapper on AccountBase { id: storable.id, createdAt: storable.createdAt, updatedAt: storable.updatedAt, + name: describable.name, + description: describable.description, + lastName: lastName, avatarUrl: avatarUrl, - name: name, - locale: locale, ); } extension AccountDTOMapper on AccountBaseDTO { AccountBase toDomain() => AccountBase( storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt), + describable: newDescribable(name: name, description: description), + lastName: lastName, avatarUrl: avatarUrl, - name: name, - locale: locale, ); } diff --git a/frontend/pshared/lib/data/mapper/describable.dart b/frontend/pshared/lib/data/mapper/describable.dart new file mode 100644 index 0000000..d9ab58e --- /dev/null +++ b/frontend/pshared/lib/data/mapper/describable.dart @@ -0,0 +1,17 @@ +import 'package:pshared/data/dto/describable.dart'; +import 'package:pshared/models/describable.dart'; + + +extension DescribableMapper on Describable { + DescribableDTO toDTO() => DescribableDTO( + name: name, + description: description, + ); +} + +extension DescribableDTOMapper on DescribableDTO { + Describable toDomain() => newDescribable( + name: name, + description: description, + ); +} \ No newline at end of file diff --git a/frontend/pshared/lib/data/mapper/organization.dart b/frontend/pshared/lib/data/mapper/organization.dart index 41970d5..71c2c70 100644 --- a/frontend/pshared/lib/data/mapper/organization.dart +++ b/frontend/pshared/lib/data/mapper/organization.dart @@ -1,5 +1,8 @@ import 'package:pshared/data/dto/organization.dart'; +import 'package:pshared/models/describable.dart'; +import 'package:pshared/models/organization/bound.dart'; import 'package:pshared/models/organization/organization.dart'; +import 'package:pshared/models/permissions/bound.dart'; import 'package:pshared/models/storable.dart'; @@ -8,15 +11,26 @@ extension OrganizationMapper on Organization { id: storable.id, createdAt: storable.createdAt, updatedAt: storable.updatedAt, + name: describable.name, + description: describable.description, timeZone: timeZone, logoUrl: logoUrl, + organizationRef: permissionBound.organizationRef, + permissionRef: permissionBound.permissionRef, + tenantRef: tenantRef, ); } extension OrganizationDTOMapper on OrganizationDTO { Organization toDomain() => Organization( storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt), + describable: newDescribable(name: name, description: description), timeZone: timeZone, logoUrl: logoUrl, + permissionBound: newPermissionBound( + organizationBound: newOrganizationBound(organizationRef: organizationRef), + permissionRef: permissionRef, + ), + tenantRef: tenantRef, ); } diff --git a/frontend/pshared/lib/data/mapper/organization/bound.dart b/frontend/pshared/lib/data/mapper/organization/bound.dart new file mode 100644 index 0000000..6b81615 --- /dev/null +++ b/frontend/pshared/lib/data/mapper/organization/bound.dart @@ -0,0 +1,18 @@ +import 'package:pshared/data/dto/organization/bound.dart'; +import 'package:pshared/models/organization/bound.dart'; + + +extension OrganizationBoundMapper on OrganizationBound { + OrganizationBoundDTO toDTO() => OrganizationBoundDTO( + id: '', // OrganizationBound doesn't have storable fields, so we need to provide defaults + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + organizationRef: organizationRef, + ); +} + +extension OrganizationBoundDTOMapper on OrganizationBoundDTO { + OrganizationBound toDomain() => newOrganizationBound( + organizationRef: organizationRef, + ); +} diff --git a/frontend/pshared/lib/data/mapper/organization/description.dart b/frontend/pshared/lib/data/mapper/organization/description.dart index d80483f..7e9c753 100644 --- a/frontend/pshared/lib/data/mapper/organization/description.dart +++ b/frontend/pshared/lib/data/mapper/organization/description.dart @@ -1,9 +1,11 @@ import 'package:pshared/data/dto/organization/description.dart'; +import 'package:pshared/data/mapper/describable.dart'; import 'package:pshared/models/organization/description.dart'; extension OrganizationDescriptionMapper on OrganizationDescription { OrganizationDescriptionDTO toDTO() => OrganizationDescriptionDTO( + description: description.toDTO(), logoUrl: logoUrl, ); } @@ -11,5 +13,6 @@ extension OrganizationDescriptionMapper on OrganizationDescription { extension AccountDescriptionDTOMapper on OrganizationDescriptionDTO { OrganizationDescription toDomain() => OrganizationDescription( logoUrl: logoUrl, + description: description.toDomain(), ); } diff --git a/frontend/pshared/lib/data/mapper/permissions/descriptions/policy.dart b/frontend/pshared/lib/data/mapper/permissions/descriptions/policy.dart index e444a9d..92268ba 100644 --- a/frontend/pshared/lib/data/mapper/permissions/descriptions/policy.dart +++ b/frontend/pshared/lib/data/mapper/permissions/descriptions/policy.dart @@ -1,4 +1,5 @@ import 'package:pshared/data/dto/permissions/description/policy.dart'; +import 'package:pshared/models/describable.dart'; import 'package:pshared/models/permissions/descriptions/policy.dart'; import 'package:pshared/models/storable.dart'; @@ -8,6 +9,8 @@ extension PolicyDescriptionMapper on PolicyDescription { id: storable.id, createdAt: storable.createdAt, updatedAt: storable.updatedAt, + name: describable.name, + description: describable.description, resourceTypes: resourceTypes, organizationRef: organizationRef, ); @@ -16,6 +19,7 @@ extension PolicyDescriptionMapper on PolicyDescription { extension PolicyDescriptionDTOMapper on PolicyDescriptionDTO { PolicyDescription toDomain() => PolicyDescription( storable: newStorable(id: id, createdAt: createdAt, updatedAt: createdAt), + describable: newDescribable(name: name, description: description), resourceTypes: resourceTypes, organizationRef: organizationRef, ); diff --git a/frontend/pshared/lib/data/mapper/permissions/descriptions/role.dart b/frontend/pshared/lib/data/mapper/permissions/descriptions/role.dart index d0596cb..b3b8e2a 100644 --- a/frontend/pshared/lib/data/mapper/permissions/descriptions/role.dart +++ b/frontend/pshared/lib/data/mapper/permissions/descriptions/role.dart @@ -1,4 +1,5 @@ import 'package:pshared/data/dto/permissions/description/role.dart'; +import 'package:pshared/models/describable.dart'; import 'package:pshared/models/permissions/descriptions/role.dart'; import 'package:pshared/models/storable.dart'; @@ -8,6 +9,8 @@ extension RoleDescriptionMapper on RoleDescription { id: storable.id, createdAt: storable.createdAt, updatedAt: storable.updatedAt, + name: describable.name, + description: describable.description, organizationRef: organizationRef, ); } @@ -15,6 +18,7 @@ extension RoleDescriptionMapper on RoleDescription { extension RoleDescriptionDTOMapper on RoleDescriptionDTO { RoleDescription toDomain() => RoleDescription( storable: newStorable(id: id, createdAt: createdAt, updatedAt: updatedAt), + describable: newDescribable(name: name, description: description), organizationRef: organizationRef, ); } diff --git a/frontend/pshared/lib/data/mapper/reference.dart b/frontend/pshared/lib/data/mapper/reference.dart new file mode 100644 index 0000000..b6fbe5f --- /dev/null +++ b/frontend/pshared/lib/data/mapper/reference.dart @@ -0,0 +1,11 @@ +import 'package:pshared/data/dto/reference.dart'; +import 'package:pshared/models/reference.dart'; + + +extension ReferenceMapper on Reference { + ReferenceDTO toDTO() => ReferenceDTO(ref: ref); +} + +extension ReferenceDTOMapper on ReferenceDTO { + Reference toDomain() => newReference(ref: ref); +} diff --git a/frontend/pshared/lib/data/mapper/storable.dart b/frontend/pshared/lib/data/mapper/storable.dart index 8990947..1696d92 100644 --- a/frontend/pshared/lib/data/mapper/storable.dart +++ b/frontend/pshared/lib/data/mapper/storable.dart @@ -3,7 +3,7 @@ import 'package:pshared/models/storable.dart'; extension StorableMapper on Storable { - StorableDTO toDTO() => StorableDTO(id: id, createdAt: createdAt, updatedAt: updatedAt); + StorableDTO toDTO() => StorableDTO(id: id, createdAt: createdAt.toUtc(), updatedAt: updatedAt.toUtc()); } extension StorableDTOMapper on StorableDTO { diff --git a/frontend/pshared/lib/models/.DS_Store b/frontend/pshared/lib/models/.DS_Store new file mode 100644 index 0000000..f469e24 Binary files /dev/null and b/frontend/pshared/lib/models/.DS_Store differ diff --git a/frontend/pshared/lib/models/account/account.dart b/frontend/pshared/lib/models/account/account.dart index 2d41b0a..8e97466 100644 --- a/frontend/pshared/lib/models/account/account.dart +++ b/frontend/pshared/lib/models/account/account.dart @@ -1,36 +1,35 @@ +import 'package:flutter/foundation.dart'; + import 'package:pshared/models/account/base.dart'; +import 'package:pshared/models/describable.dart'; +@immutable class Account extends AccountBase { final String login; + final String locale; const Account({ required super.storable, + required super.describable, required super.avatarUrl, + required super.lastName, required this.login, - required super.locale, - required super.name, + required this.locale, }); - 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({ + Describable? describable, + String? lastName, String? Function()? avatarUrl, - String? name, String? locale, - }) { - final updatedBase = super.copyWith( - avatarUrl: avatarUrl, - name: name, - locale: locale, - ); - return Account.fromBase(updatedBase, login); - } + }) => Account( + storable: storable, + describable: describableCopyWithOther(this.describable, describable), + lastName: lastName ?? this.lastName, + avatarUrl: avatarUrl != null ? avatarUrl() : this.avatarUrl, + login: login, + locale: locale ?? this.locale, + ); } \ No newline at end of file diff --git a/frontend/pshared/lib/models/account/base.dart b/frontend/pshared/lib/models/account/base.dart index 5fdd112..fd5722e 100644 --- a/frontend/pshared/lib/models/account/base.dart +++ b/frontend/pshared/lib/models/account/base.dart @@ -1,8 +1,16 @@ +import 'package:flutter/foundation.dart'; + +import 'package:pshared/models/describable.dart'; import 'package:pshared/models/storable.dart'; +import 'package:pshared/models/storable/describable.dart'; +import 'package:pshared/utils/name_initials.dart'; -class AccountBase implements Storable { +@immutable +class AccountBase implements StorableDescribable { final Storable storable; + final Describable describable; + final String lastName; @override String get id => storable.id; @@ -10,26 +18,30 @@ class AccountBase implements Storable { DateTime get createdAt => storable.createdAt; @override DateTime get updatedAt => storable.updatedAt; + @override + String get name => describable.name; + @override + String? get description => describable.description; final String? avatarUrl; - final String name; - final String locale; const AccountBase({ required this.storable, - required this.name, - required this.locale, + required this.describable, required this.avatarUrl, + required this.lastName, }); + String get nameInitials => getNameInitials(describable.name); + AccountBase copyWith({ + Describable? describable, + String? lastName, String? Function()? avatarUrl, - String? name, - String? locale, }) => AccountBase( storable: storable, avatarUrl: avatarUrl != null ? avatarUrl() : this.avatarUrl, - locale: locale ?? this.locale, - name: name ?? this.name, + describable: describable ?? this.describable, + lastName: lastName ?? this.lastName, ); } diff --git a/frontend/pshared/lib/models/describable.dart b/frontend/pshared/lib/models/describable.dart new file mode 100644 index 0000000..22a7f52 --- /dev/null +++ b/frontend/pshared/lib/models/describable.dart @@ -0,0 +1,39 @@ +import 'package:flutter/foundation.dart'; + + +abstract class Describable { + String get name; + String? get description; +} + +@immutable +class _DescribableImp implements Describable { + @override + final String name; + @override + final String? description; + + const _DescribableImp({ + required this.name, + required this.description, + }); +} + +Describable newDescribable({required String name, String? description}) => + _DescribableImp(name: name, description: description); + + +extension DescribableCopier on Describable { + Describable copyWith({ + String? name, + String? Function()? description, + }) => newDescribable( + name: name ?? this.name, + description: description != null ? description() : this.description, + ); +} + +Describable describableCopyWithOther(Describable current, Describable? other) => current.copyWith( + name: other?.name, + description: () => other?.description, +); \ No newline at end of file diff --git a/frontend/pshared/lib/models/organization/bound.dart b/frontend/pshared/lib/models/organization/bound.dart new file mode 100644 index 0000000..acce7fb --- /dev/null +++ b/frontend/pshared/lib/models/organization/bound.dart @@ -0,0 +1,18 @@ +import 'package:flutter/foundation.dart'; + + +abstract class OrganizationBound { + String get organizationRef; +} + +@immutable +class _OrganizationBoundImp implements OrganizationBound { + @override + final String organizationRef; + + const _OrganizationBoundImp({ + required this.organizationRef, + }); +} + +OrganizationBound newOrganizationBound({ required String organizationRef }) => _OrganizationBoundImp(organizationRef: organizationRef); \ No newline at end of file diff --git a/frontend/pshared/lib/models/organization/description.dart b/frontend/pshared/lib/models/organization/description.dart index 7e6f5f9..81d199f 100644 --- a/frontend/pshared/lib/models/organization/description.dart +++ b/frontend/pshared/lib/models/organization/description.dart @@ -1,7 +1,12 @@ +import 'package:pshared/models/describable.dart'; + + class OrganizationDescription { + final Describable description; final String? logoUrl; const OrganizationDescription({ + required this.description, this.logoUrl, }); } \ No newline at end of file diff --git a/frontend/pshared/lib/models/organization/organization.dart b/frontend/pshared/lib/models/organization/organization.dart index b04182a..1150e8b 100644 --- a/frontend/pshared/lib/models/organization/organization.dart +++ b/frontend/pshared/lib/models/organization/organization.dart @@ -1,8 +1,13 @@ +import 'package:pshared/models/describable.dart'; +import 'package:pshared/models/permissions/bound.dart'; +import 'package:pshared/models/permissions/bound/describable.dart'; import 'package:pshared/models/storable.dart'; -class Organization implements Storable { +class Organization implements PermissionBoundStorableDescribable { final Storable storable; + final PermissionBound permissionBound; + final Describable describable; @override String get id => storable.id; @@ -10,25 +15,39 @@ class Organization implements Storable { DateTime get createdAt => storable.createdAt; @override DateTime get updatedAt => storable.updatedAt; + @override + String get organizationRef => permissionBound.organizationRef; + @override + String get permissionRef => permissionBound.permissionRef; + @override + String get name => describable.name; + @override + String? get description => describable.description; final String timeZone; final String? logoUrl; + final String tenantRef; const Organization({ required this.storable, + required this.describable, required this.timeZone, + required this.permissionBound, + required this.tenantRef, this.logoUrl, }); Organization copyWith({ - String? name, - String? Function()? description, + Describable? describable, String? timeZone, String? Function()? logoUrl, }) => Organization( storable: storable, // Same Storable, same id + describable: describableCopyWithOther(this.describable, describable), timeZone: timeZone ?? this.timeZone, logoUrl: logoUrl != null ? logoUrl() : this.logoUrl, + permissionBound: permissionBound, + tenantRef: tenantRef, ); } diff --git a/frontend/pshared/lib/models/permission_bound.dart b/frontend/pshared/lib/models/permission_bound.dart deleted file mode 100644 index 6618f26..0000000 --- a/frontend/pshared/lib/models/permission_bound.dart +++ /dev/null @@ -1,22 +0,0 @@ -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); \ No newline at end of file diff --git a/frontend/pshared/lib/models/permissions/bound.dart b/frontend/pshared/lib/models/permissions/bound.dart new file mode 100644 index 0000000..476b2a4 --- /dev/null +++ b/frontend/pshared/lib/models/permissions/bound.dart @@ -0,0 +1,32 @@ +import 'package:flutter/foundation.dart'; + +import 'package:pshared/config/constants.dart'; +import 'package:pshared/models/organization/bound.dart'; + + +abstract class PermissionBound extends OrganizationBound { + String get permissionRef; +} + +@immutable +class _PermissionBoundImp implements PermissionBound { + @override + final String permissionRef; + final OrganizationBound organizationBound; + + @override + get organizationRef => organizationBound.organizationRef; + + const _PermissionBoundImp({ + required this.permissionRef, + required this.organizationBound, + }); +} + +PermissionBound newPermissionBound({ + required OrganizationBound organizationBound, + String? permissionRef, +}) => _PermissionBoundImp( + permissionRef: permissionRef ?? Constants.nilObjectRef, + organizationBound: organizationBound, +); \ No newline at end of file diff --git a/frontend/pshared/lib/models/permissions/bound/describable.dart b/frontend/pshared/lib/models/permissions/bound/describable.dart new file mode 100644 index 0000000..3b141ba --- /dev/null +++ b/frontend/pshared/lib/models/permissions/bound/describable.dart @@ -0,0 +1,6 @@ +import 'package:pshared/models/permissions/bound/storable.dart'; +import 'package:pshared/models/storable/describable.dart'; + + +abstract class PermissionBoundStorableDescribable implements PermissionBoundStorable, StorableDescribable { +} diff --git a/frontend/pshared/lib/models/permission_bound_storable.dart b/frontend/pshared/lib/models/permissions/bound/storable.dart similarity index 69% rename from frontend/pshared/lib/models/permission_bound_storable.dart rename to frontend/pshared/lib/models/permissions/bound/storable.dart index 4ee0d63..47d783c 100644 --- a/frontend/pshared/lib/models/permission_bound_storable.dart +++ b/frontend/pshared/lib/models/permissions/bound/storable.dart @@ -1,4 +1,4 @@ -import 'package:pshared/models/permission_bound.dart'; +import 'package:pshared/models/permissions/bound.dart'; import 'package:pshared/models/storable.dart'; diff --git a/frontend/pshared/lib/models/permissions/descriptions/policy.dart b/frontend/pshared/lib/models/permissions/descriptions/policy.dart index 6aecef0..ac746ce 100644 --- a/frontend/pshared/lib/models/permissions/descriptions/policy.dart +++ b/frontend/pshared/lib/models/permissions/descriptions/policy.dart @@ -1,9 +1,12 @@ +import 'package:pshared/models/describable.dart'; import 'package:pshared/models/resources.dart'; import 'package:pshared/models/storable.dart'; +import 'package:pshared/models/storable/describable.dart'; -class PolicyDescription implements Storable { +class PolicyDescription implements StorableDescribable { final Storable storable; + final Describable describable; final List? resourceTypes; final String? organizationRef; @@ -13,9 +16,14 @@ class PolicyDescription implements Storable { DateTime get createdAt => storable.createdAt; @override DateTime get updatedAt => storable.updatedAt; + @override + String get name => describable.name; + @override + String? get description => describable.description; const PolicyDescription({ required this.storable, + required this.describable, required this.resourceTypes, required this.organizationRef, }); diff --git a/frontend/pshared/lib/models/permissions/descriptions/role.dart b/frontend/pshared/lib/models/permissions/descriptions/role.dart index ecde4fb..1e04a27 100644 --- a/frontend/pshared/lib/models/permissions/descriptions/role.dart +++ b/frontend/pshared/lib/models/permissions/descriptions/role.dart @@ -1,8 +1,11 @@ +import 'package:pshared/models/describable.dart'; import 'package:pshared/models/storable.dart'; +import 'package:pshared/models/storable/describable.dart'; -class RoleDescription implements Storable { +class RoleDescription implements StorableDescribable { final Storable storable; + final Describable describable; @override String get id => storable.id; @@ -10,18 +13,25 @@ class RoleDescription implements Storable { DateTime get createdAt => storable.createdAt; @override DateTime get updatedAt => storable.updatedAt; + @override + String get name => describable.name; + @override + String? get description => describable.description; final String organizationRef; const RoleDescription({ required this.storable, + required this.describable, required this.organizationRef, }); factory RoleDescription.build({ + required Describable roleDescription, required String organizationRef, }) => RoleDescription( storable: newStorable(), + describable: roleDescription, organizationRef: organizationRef ); } diff --git a/frontend/pshared/lib/models/reference.dart b/frontend/pshared/lib/models/reference.dart new file mode 100644 index 0000000..3bcc422 --- /dev/null +++ b/frontend/pshared/lib/models/reference.dart @@ -0,0 +1,22 @@ +abstract class Reference { + String get ref; +} + +class _ReferenceImp implements Reference { + @override + final String ref; + + const _ReferenceImp({ + required this.ref, + }); +} + +Reference newReference({required String ref}) => _ReferenceImp(ref: ref); + +extension ReferenceCopier on Reference { + Reference copyWith({ + String? ref, + }) => newReference( + ref: ref ?? this.ref, + ); +} diff --git a/frontend/pshared/lib/models/storable.dart b/frontend/pshared/lib/models/storable.dart index ce95482..dfd59e3 100644 --- a/frontend/pshared/lib/models/storable.dart +++ b/frontend/pshared/lib/models/storable.dart @@ -1,4 +1,6 @@ -import 'package:pshared/config/constants.dart'; +import 'package:flutter/foundation.dart'; + +import 'package:pshared/config/web.dart'; abstract class Storable { @@ -7,6 +9,7 @@ abstract class Storable { DateTime get updatedAt; } +@immutable class _StorableImp implements Storable { @override final String id; diff --git a/frontend/pshared/lib/models/storable/.DS_Store b/frontend/pshared/lib/models/storable/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/frontend/pshared/lib/models/storable/.DS_Store differ diff --git a/frontend/pshared/lib/models/storable/describable.dart b/frontend/pshared/lib/models/storable/describable.dart new file mode 100644 index 0000000..6d792e6 --- /dev/null +++ b/frontend/pshared/lib/models/storable/describable.dart @@ -0,0 +1,6 @@ +import 'package:pshared/models/describable.dart'; +import 'package:pshared/models/storable.dart'; + + +abstract class StorableDescribable implements Storable, Describable { +} \ No newline at end of file diff --git a/frontend/pshared/lib/provider/account.dart b/frontend/pshared/lib/provider/account.dart index 111ae4b..f44906d 100644 --- a/frontend/pshared/lib/provider/account.dart +++ b/frontend/pshared/lib/provider/account.dart @@ -4,21 +4,48 @@ import 'package:share_plus/share_plus.dart'; import 'package:pshared/api/errors/unauthorized.dart'; import 'package:pshared/api/requests/signup.dart'; +import 'package:pshared/api/requests/login_data.dart'; +import 'package:pshared/config/constants.dart'; import 'package:pshared/models/account/account.dart'; -import 'package:pshared/provider/exception.dart'; +import 'package:pshared/models/describable.dart'; +import 'package:pshared/models/storable.dart'; +import 'package:pshared/provider/locale.dart'; import 'package:pshared/provider/resource.dart'; import 'package:pshared/service/account.dart'; +import 'package:pshared/service/authorization/service.dart'; +import 'package:pshared/utils/exception.dart'; class AccountProvider extends ChangeNotifier { + static String get currentUserRef => Constants.nilObjectRef; + // The resource now wraps our Account? state along with its loading/error state. Resource _resource = Resource(data: null); Resource get resource => _resource; + late LocaleProvider _localeProvider; Account? get account => _resource.data; bool get isLoggedIn => account != null; bool get isLoading => _resource.isLoading; Object? get error => _resource.error; + bool get isReady => (!isLoading) && (account != null); + + Account? currentUser() { + final acc = account; + if (acc == null) return null; + return Account( + storable: newStorable( + id: currentUserRef, + createdAt: acc.createdAt, + updatedAt: acc.updatedAt, + ), + describable: acc.describable, + lastName: acc.lastName, + avatarUrl: acc.avatarUrl, + login: acc.login, + locale: acc.locale, + ); + } // Private helper to update the resource and notify listeners. void _setResource(Resource newResource) { @@ -26,16 +53,24 @@ class AccountProvider extends ChangeNotifier { notifyListeners(); } + void updateProvider(LocaleProvider localeProvider) => _localeProvider = localeProvider; - Future login({ + void _pickupLocale(String locale) => _localeProvider.setLocale(Locale(locale)); + + Future login({ required String email, required String password, required String locale, }) async { _setResource(_resource.copyWith(isLoading: true, error: null)); try { - final acc = await AccountService.login(email, password, locale); + final acc = await AccountService.login(LoginData.build( + login: email, + password: password, + locale: locale, + )); _setResource(Resource(data: acc, isLoading: false)); + _pickupLocale(acc.locale); return acc; } catch (e) { _setResource(_resource.copyWith(isLoading: false, error: toException(e))); @@ -43,11 +78,14 @@ class AccountProvider extends ChangeNotifier { } } + Future isAuthorizationStored() async => AuthorizationService.isAuthorizationStored(); + Future restore() async { _setResource(_resource.copyWith(isLoading: true, error: null)); try { final acc = await AccountService.restore(); _setResource(Resource(data: acc, isLoading: false)); + _pickupLocale(acc.locale); return acc; } catch (e) { _setResource(_resource.copyWith(isLoading: false, error: toException(e))); @@ -55,24 +93,20 @@ class AccountProvider extends ChangeNotifier { } } - Future signup( - String name, - String login, - String password, - String locale, - String organizationName, - String timezone, - ) async { + Future signup({ + required AccountData account, + required Describable organization, + required String timezone, + required Describable ownerRole, + }) async { _setResource(_resource.copyWith(isLoading: true, error: null)); try { await AccountService.signup( SignupRequest.build( - name: name, - login: login.trim().toLowerCase(), - password: password, - locale: locale, - organizationName: organizationName, + account: account, + organization: organization, organizationTimeZone: timezone, + ownerRole: ownerRole, ), ); // Signup might not automatically log in the user, @@ -96,6 +130,7 @@ class AccountProvider extends ChangeNotifier { } Future update({ + Describable? describable, String? locale, String? avatarUrl, String? notificationFrequency, @@ -105,6 +140,7 @@ class AccountProvider extends ChangeNotifier { try { final updated = await AccountService.update( account!.copyWith( + describable: describable, avatarUrl: () => avatarUrl ?? account!.avatarUrl, locale: locale ?? account!.locale, ), @@ -141,4 +177,26 @@ class AccountProvider extends ChangeNotifier { rethrow; } } + + Future forgotPassword(String email) async { + _setResource(_resource.copyWith(isLoading: true, error: null)); + try { + await AccountService.forgotPassword(email); + _setResource(_resource.copyWith(isLoading: false)); + } catch (e) { + _setResource(_resource.copyWith(isLoading: false, error: toException(e))); + rethrow; + } + } + + Future resetPassword(String accountId, String token, String newPassword) async { + _setResource(_resource.copyWith(isLoading: true, error: null)); + try { + await AccountService.resetPassword(accountId, token, newPassword); + _setResource(_resource.copyWith(isLoading: false)); + } catch (e) { + _setResource(_resource.copyWith(isLoading: false, error: toException(e))); + rethrow; + } + } } diff --git a/frontend/pshared/lib/provider/organizations.dart b/frontend/pshared/lib/provider/organizations.dart index c338885..2a06728 100644 --- a/frontend/pshared/lib/provider/organizations.dart +++ b/frontend/pshared/lib/provider/organizations.dart @@ -5,9 +5,9 @@ import 'package:collection/collection.dart'; import 'package:pshared/config/constants.dart'; import 'package:pshared/models/organization/organization.dart'; import 'package:pshared/provider/resource.dart'; -import 'package:pshared/provider/exception.dart'; import 'package:pshared/service/organization.dart'; import 'package:pshared/service/secure_storage.dart'; +import 'package:pshared/utils/exception.dart'; class OrganizationsProvider extends ChangeNotifier { diff --git a/frontend/pshared/lib/provider/pfe/provider.dart b/frontend/pshared/lib/provider/pfe/provider.dart deleted file mode 100644 index 6fff15b..0000000 --- a/frontend/pshared/lib/provider/pfe/provider.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:pshared/service/pfe/service.dart'; -import 'package:pshared/provider/exception.dart'; -import 'package:pshared/provider/resource.dart'; -import 'package:pshared/service/account.dart'; - - -class PfeProvider extends ChangeNotifier { - // The resource now wraps our Account? state along with its loading/error state. - Resource _resource = Resource(data: null); - Resource get resource => _resource; - - String? get session => _resource.data; - bool get isLoggedIn => session != null; - bool get isLoading => _resource.isLoading; - Object? get error => _resource.error; - - // Private helper to update the resource and notify listeners. - void _setResource(Resource newResource) { - _resource = newResource; - notifyListeners(); - } - - - Future login({ - required String email, - required String password, - }) async { - _setResource(_resource.copyWith(isLoading: true, error: null)); - try { - final acc = await PfeService.login(email, password); - _setResource(Resource(data: acc, isLoading: false)); - return acc; - } catch (e) { - _setResource(_resource.copyWith(isLoading: false, error: toException(e))); - rethrow; - } - } - - Future logout() async { - _setResource(_resource.copyWith(isLoading: true, error: null)); - try { - await AccountService.logout(); - _setResource(Resource(data: null, isLoading: false)); - } catch (e) { - _setResource(_resource.copyWith(isLoading: false, error: toException(e))); - rethrow; - } - } -} diff --git a/frontend/pshared/lib/provider/template.dart b/frontend/pshared/lib/provider/template.dart index 0a8d819..c26e89c 100644 --- a/frontend/pshared/lib/provider/template.dart +++ b/frontend/pshared/lib/provider/template.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; -import 'package:pshared/models/permission_bound_storable.dart'; -import 'package:pshared/provider/exception.dart'; +import 'package:pshared/models/permissions/bound/storable.dart'; import 'package:pshared/provider/resource.dart'; import 'package:pshared/service/template.dart'; +import 'package:pshared/utils/exception.dart'; List mergeLists({ diff --git a/frontend/pshared/lib/service/account.dart b/frontend/pshared/lib/service/account.dart index a51ccff..a935dc2 100644 --- a/frontend/pshared/lib/service/account.dart +++ b/frontend/pshared/lib/service/account.dart @@ -4,7 +4,10 @@ import 'package:share_plus/share_plus.dart'; import 'package:pshared/api/requests/signup.dart'; import 'package:pshared/api/responses/account.dart'; -import 'package:pshared/api/requests/change_password.dart'; +import 'package:pshared/api/requests/login_data.dart'; +import 'package:pshared/api/requests/password/change.dart'; +import 'package:pshared/api/requests/password/forgot.dart'; +import 'package:pshared/api/requests/password/reset.dart'; import 'package:pshared/data/mapper/account/account.dart'; import 'package:pshared/models/account/account.dart'; import 'package:pshared/service/authorization/service.dart'; @@ -17,9 +20,9 @@ class AccountService { static final _logger = Logger('service.account'); static const String _objectType = Services.account; - static Future login(String email, String password, String locale) async { + static Future login(LoginData login) async { _logger.fine('Logging in'); - return AuthorizationService.login(_objectType, email, password, locale); + return AuthorizationService.login(_objectType, login); } static Future restore() async { @@ -27,6 +30,7 @@ class AccountService { } static Future signup(SignupRequest request) async { + // Use regular HTTP for public signup endpoint (no auth needed) await getPOSTResponse(_objectType, 'signup', request.toJson()); } @@ -42,9 +46,20 @@ class AccountService { static Future update(Account account) async { _logger.fine('Patching account ${account.id}'); + // Use AuthorizationService for authenticated operations return _getAccount(AuthorizationService.getPUTResponse(_objectType, '', account.toDTO().toJson())); } + static Future forgotPassword(String email) async { + _logger.fine('Requesting password reset for email: $email'); + await getPUTResponse(_objectType, 'password', ForgotPasswordRequest.build(login: email).toJson()); + } + + static Future resetPassword(String accountRef, String token, String newPassword) async { + _logger.fine('Resetting password for account: $accountRef'); + await getPOSTResponse(_objectType, 'password/reset/$accountRef/$token', ResetPasswordRequest.build(password: newPassword).toJson()); + } + static Future changePassword(String oldPassword, String newPassword) async { _logger.fine('Changing password'); return _getAccount(AuthorizationService.getPATCHResponse( diff --git a/frontend/pshared/lib/service/authorization/circuit_breaker.dart b/frontend/pshared/lib/service/authorization/circuit_breaker.dart new file mode 100644 index 0000000..d64d48b --- /dev/null +++ b/frontend/pshared/lib/service/authorization/circuit_breaker.dart @@ -0,0 +1,92 @@ +import 'package:logging/logging.dart'; + +/// Circuit breaker pattern implementation for authentication service failures +class AuthCircuitBreaker { + static final _logger = Logger('service.auth_circuit_breaker'); + + static int _failureCount = 0; + static DateTime? _lastFailure; + static const int _failureThreshold = 3; + static const Duration _recoveryTime = Duration(minutes: 5); + + /// Returns true if the circuit breaker is open (blocking operations) + static bool get isOpen { + if (_failureCount < _failureThreshold) return false; + if (_lastFailure == null) return false; + + final isOpen = DateTime.now().difference(_lastFailure!) < _recoveryTime; + if (isOpen) { + _logger.warning('Circuit breaker is OPEN. Failure count: $_failureCount, last failure: $_lastFailure'); + } + return isOpen; + } + + /// Returns true if the circuit breaker is in half-open state (allowing test requests) + static bool get isHalfOpen { + if (_failureCount < _failureThreshold) return false; + if (_lastFailure == null) return false; + + return DateTime.now().difference(_lastFailure!) >= _recoveryTime; + } + + /// Executes an operation with circuit breaker protection + static Future execute(Future Function() operation) async { + if (isOpen) { + final timeSinceFailure = _lastFailure != null + ? DateTime.now().difference(_lastFailure!) + : Duration.zero; + final timeUntilRecovery = _recoveryTime - timeSinceFailure; + + _logger.warning('Circuit breaker blocking operation. Recovery in: ${timeUntilRecovery.inSeconds}s'); + throw Exception('Auth service temporarily unavailable. Try again in ${timeUntilRecovery.inMinutes} minutes.'); + } + + try { + _logger.fine('Executing operation through circuit breaker'); + final result = await operation(); + _reset(); + return result; + } catch (e) { + _recordFailure(); + rethrow; + } + } + + /// Records a failure and updates the circuit breaker state + static void _recordFailure() { + _failureCount++; + _lastFailure = DateTime.now(); + _logger.warning('Auth circuit breaker recorded failure #$_failureCount at $_lastFailure'); + + if (_failureCount >= _failureThreshold) { + _logger.severe('Auth circuit breaker OPENED after $_failureCount failures'); + } + } + + /// Resets the circuit breaker to closed state + static void _reset() { + if (_failureCount > 0) { + _logger.info('Auth circuit breaker CLOSED - resetting failure count from $_failureCount to 0'); + } + _failureCount = 0; + _lastFailure = null; + } + + /// Manual reset (for testing or administrative purposes) + static void manualReset() { + _logger.info('Auth circuit breaker manually reset'); + _reset(); + } + + /// Get current status for debugging + static Map getStatus() { + return { + 'failureCount': _failureCount, + 'lastFailure': _lastFailure?.toIso8601String(), + 'isOpen': isOpen, + 'isHalfOpen': isHalfOpen, + 'threshold': _failureThreshold, + 'recoveryTime': _recoveryTime.inSeconds, + }; + } +} \ No newline at end of file diff --git a/frontend/pshared/lib/service/authorization/retry_helper.dart b/frontend/pshared/lib/service/authorization/retry_helper.dart new file mode 100644 index 0000000..6dcb71b --- /dev/null +++ b/frontend/pshared/lib/service/authorization/retry_helper.dart @@ -0,0 +1,87 @@ +import 'dart:math'; + +import 'package:logging/logging.dart'; + +import 'package:pshared/utils/exception.dart'; + + +class RetryHelper { + static final _logger = Logger('auth.retry'); + + /// Executes an operation with exponential backoff retry logic + static Future withExponentialBackoff( + Future Function() operation, { + int maxRetries = 3, + Duration initialDelay = const Duration(milliseconds: 500), + double backoffMultiplier = 2.0, + Duration maxDelay = const Duration(seconds: 30), + bool Function(Exception)? shouldRetry, + }) async { + Exception? lastException; + + // Total attempts = initial attempt + maxRetries + final totalAttempts = maxRetries + 1; + + for (int attempt = 1; attempt <= totalAttempts; attempt++) { + try { + _logger.fine('Attempting operation (attempt $attempt/$totalAttempts)'); + return await operation(); + } catch (e) { + lastException = toException(e); + + // Don't retry if we've reached max attempts + if (attempt == totalAttempts) { + _logger.warning('Operation failed after $totalAttempts attempts: $lastException'); + rethrow; + } + + // Check if we should retry this specific error + if (shouldRetry != null && !shouldRetry(lastException)) { + _logger.fine('Operation failed with non-retryable error: $lastException'); + rethrow; + } + + // Calculate delay with exponential backoff + final delayMs = min( + initialDelay.inMilliseconds * pow(backoffMultiplier, attempt - 1).toInt(), + maxDelay.inMilliseconds, + ); + final delay = Duration(milliseconds: delayMs); + + _logger.fine('Operation failed (attempt $attempt), retrying in ${delay.inMilliseconds}ms: $lastException'); + await Future.delayed(delay); + } + } + + // This should never be reached due to rethrow above, but just in case + throw lastException ?? Exception('Retry logic error'); + } + + /// Determines if an error is retryable (network/temporary errors) + static bool isRetryableError(Exception error) { + final errorString = error.toString().toLowerCase(); + + // Network connectivity issues + if (errorString.contains('socket') || + errorString.contains('connection') || + errorString.contains('timeout') || + errorString.contains('network')) { + return true; + } + + // Server temporary errors (5xx) + if (errorString.contains('500') || + errorString.contains('502') || + errorString.contains('503') || + errorString.contains('504')) { + return true; + } + + // Rate limiting + if (errorString.contains('429')) { + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/frontend/pshared/lib/service/authorization/service.dart b/frontend/pshared/lib/service/authorization/service.dart index 79ba68a..57e615f 100644 --- a/frontend/pshared/lib/service/authorization/service.dart +++ b/frontend/pshared/lib/service/authorization/service.dart @@ -1,20 +1,38 @@ import 'package:logging/logging.dart'; -import 'package:pshared/api/errors/upload_failed.dart'; +import 'package:pshared/api/errors/authorization_failed.dart'; import 'package:pshared/api/requests/login.dart'; +import 'package:pshared/api/requests/login_data.dart'; import 'package:pshared/api/responses/account.dart'; import 'package:pshared/api/responses/login.dart'; -import 'package:pshared/config/constants.dart'; +import 'package:pshared/config/web.dart'; import 'package:pshared/data/mapper/account/account.dart'; import 'package:pshared/models/account/account.dart'; +import 'package:pshared/service/authorization/circuit_breaker.dart'; +import 'package:pshared/service/authorization/retry_helper.dart'; import 'package:pshared/service/authorization/storage.dart'; import 'package:pshared/service/authorization/token.dart'; import 'package:pshared/service/device_id.dart'; +import 'package:pshared/utils/exception.dart'; import 'package:pshared/utils/http/requests.dart' as httpr; +/// AuthorizationService provides centralized authorization management +/// with token refresh, retry logic, and circuit breaker patterns class AuthorizationService { - static final _logger = Logger('service.authorization'); + static final _logger = Logger('service.authorization.auth_service'); + + static Future login(String service, LoginData login) async { + _logger.fine('Logging in ${login.login} with ${login.locale} locale'); + final deviceId = await DeviceIdManager.getDeviceId(); + final response = await httpr.getPOSTResponse( + service, + '/login', + LoginRequest(login: login, deviceId: deviceId, clientId: Constants.clientId).toJson(), + ); + + return (await _completeLogin(response)).account.toDomain(); + } static Future _updateAccessToken(AccountResponse response) async { await AuthorizationStorage.updateToken(response.accessToken); @@ -31,59 +49,86 @@ class AuthorizationService { return lr; } - static Future login(String service, String email, String password, String locale) async { - _logger.fine('Logging in $email with $locale locale'); - final deviceId = await DeviceIdManager.getDeviceId(); - final response = await httpr.getPOSTResponse( - service, - '/login', - LoginRequest( - login: email.toLowerCase(), - password: password, - locale: locale, - deviceId: deviceId, - clientId: Constants.clientId, - ).toJson()); - - return (await _completeLogin(response)).account.toDomain(); - } - static Future restore() async { - return (await TokenService.rotateRefreshToken()).account.toDomain(); + return (await TokenService.refreshAccessToken()).account.toDomain(); } static Future logout() async { return AuthorizationStorage.removeTokens(); } - static Future> _authenticatedRequest( - String service, - String url, - Future> Function(String, String, Map, {String? authToken}) requestType, - {Map? body}) async { - final accessToken = await TokenService.getAccessToken(); - return requestType(service, url, body ?? {}, authToken: accessToken); - } - - static Future> getPOSTResponse(String service, String url, Map body) async => _authenticatedRequest(service, url, httpr.getPOSTResponse, body: body); - + // Original AuthorizationService methods - keeping the interface unchanged static Future> getGETResponse(String service, String url) async { - final accessToken = await TokenService.getAccessToken(); - return httpr.getGETResponse(service, url, authToken: accessToken); + final token = await TokenService.getAccessTokenSafe(); + return httpr.getGETResponse(service, url, authToken: token); } - static Future> getPUTResponse(String service, String url, Map body) async => _authenticatedRequest(service, url, httpr.getPUTResponse, body: body); + static Future> getPOSTResponse(String service, String url, Map body) async { + final token = await TokenService.getAccessTokenSafe(); + return httpr.getPOSTResponse(service, url, body, authToken: token); + } - static Future> getPATCHResponse(String service, String url, Map body) async => _authenticatedRequest(service, url, httpr.getPATCHResponse, body: body); + static Future> getPUTResponse(String service, String url, Map body) async { + final token = await TokenService.getAccessTokenSafe(); + return httpr.getPUTResponse(service, url, body, authToken: token); + } - static Future> getDELETEResponse(String service, String url, Map body) async => _authenticatedRequest(service, url, httpr.getDELETEResponse, body: body); + static Future> getPATCHResponse(String service, String url, Map body) async { + final token = await TokenService.getAccessTokenSafe(); + return httpr.getPATCHResponse(service, url, body, authToken: token); + } + + static Future> getDELETEResponse(String service, String url, Map body) async { + final token = await TokenService.getAccessTokenSafe(); + return httpr.getDELETEResponse(service, url, body, authToken: token); + } static Future getFileUploadResponseAuth(String service, String url, String fileName, String fileType, String mediaType, List bytes) async { - final accessToken = await TokenService.getAccessToken(); - final res = await httpr.getFileUploadResponse(service, url, fileName, fileType, mediaType, bytes, authToken: accessToken); + final token = await TokenService.getAccessTokenSafe(); + final res = await httpr.getFileUploadResponse(service, url, fileName, fileType, mediaType, bytes, authToken: token); if (res == null) { - throw ErrorUploadFailed(); + throw Exception('Upload failed'); } return res.url; } + + static Future isAuthorizationStored() async => AuthorizationStorage.isAuthorizationStored(); + + /// Execute an operation with automatic token management and retry logic + static Future executeWithAuth( + Future Function() operation, + String description, { + int? maxRetries, + }) async => AuthCircuitBreaker.execute(() async => RetryHelper.withExponentialBackoff( + operation, + maxRetries: maxRetries ?? 3, + initialDelay: Duration(milliseconds: 100), + maxDelay: Duration(seconds: 5), + shouldRetry: (error) => RetryHelper.isRetryableError(error), + )); + + + /// Handle 401 unauthorized errors with automatic token recovery + static Future handleUnauthorized( + Future Function() operation, + String description, + ) async { + _logger.warning('Handling unauthorized error with token recovery: $description'); + + return executeWithAuth( + () async { + try { + // Attempt token recovery first + await TokenService.handleUnauthorized(); + + // Retry the original operation + return await operation(); + } catch (e) { + _logger.severe('Token recovery failed', e); + throw AuthenticationFailedException('Token recovery failed', toException(e)); + } + }, + 'unauthorized recovery: $description', + ); + } } diff --git a/frontend/pshared/lib/service/authorization/storage.dart b/frontend/pshared/lib/service/authorization/storage.dart index bb49431..a72cebd 100644 --- a/frontend/pshared/lib/service/authorization/storage.dart +++ b/frontend/pshared/lib/service/authorization/storage.dart @@ -20,6 +20,34 @@ class AuthorizationStorage { return TokenData.fromJson(jsonDecode(tokenJson)); } + + static Future _checkTokenUsable(String keyName) async { + final hasKey = await SecureStorageService.containsKey(keyName); + if (!hasKey) return false; + + try { + final tokenData = await _getTokenData(keyName); + return tokenData.expiration.isAfter(DateTime.now()); + } catch (e, st) { + _logger.warning('Error reading token from $keyName: $e', e, st); + rethrow; + } + } + + + static Future isAuthorizationStored() async { + _logger.fine('Checking if authorization is stored'); + + final accessUsable = await _checkTokenUsable(Constants.accessTokenStorageKey); + if (accessUsable) return true; + + final refreshUsable = await _checkTokenUsable(Constants.refreshTokenStorageKey); + if (refreshUsable) return true; + + return false; + } + + static Future getAccessToken() async { _logger.fine('Getting access token'); return _getTokenData(Constants.accessTokenStorageKey); diff --git a/frontend/pshared/lib/service/authorization/token.dart b/frontend/pshared/lib/service/authorization/token.dart index 0357e98..06ebb49 100644 --- a/frontend/pshared/lib/service/authorization/token.dart +++ b/frontend/pshared/lib/service/authorization/token.dart @@ -1,20 +1,25 @@ - - import 'package:logging/logging.dart'; +import 'package:pshared/api/errors/authorization_failed.dart'; import 'package:pshared/api/errors/unauthorized.dart'; import 'package:pshared/api/requests/tokens/access_refresh.dart'; import 'package:pshared/api/requests/tokens/refresh_rotate.dart'; import 'package:pshared/api/responses/account.dart'; +import 'package:pshared/api/responses/error/server.dart'; import 'package:pshared/api/responses/login.dart'; import 'package:pshared/api/responses/token.dart'; import 'package:pshared/config/constants.dart'; +import 'package:pshared/service/authorization/circuit_breaker.dart'; +import 'package:pshared/service/authorization/retry_helper.dart'; import 'package:pshared/service/authorization/storage.dart'; +import 'package:pshared/service/authorization/token_mutex.dart'; import 'package:pshared/service/device_id.dart'; import 'package:pshared/service/services.dart'; +import 'package:pshared/utils/exception.dart'; import 'package:pshared/utils/http/requests.dart'; + class TokenService { static final _logger = Logger('service.authorization.token'); static const String _objectType = Services.account; @@ -26,7 +31,11 @@ class TokenService { static Future getAccessToken() async { TokenData token = await AuthorizationStorage.getAccessToken(); if (_isTokenExpiringSoon(token, const Duration(hours: 4))) { - token = (await _refreshAccessToken()).accessToken; + // Use mutex to prevent concurrent refresh operations + final refreshedToken = await TokenRefreshMutex().executeRefresh(() async { + return (await refreshAccessToken()).accessToken.token; + }); + return refreshedToken; } return token.token; } @@ -36,13 +45,13 @@ class TokenService { await AuthorizationStorage.updateRefreshToken(response.refreshToken); } - static Future _refreshAccessToken() async { + static Future refreshAccessToken() async { _logger.fine('Refreshing access token...'); final deviceId = await DeviceIdManager.getDeviceId(); final refresh = await AuthorizationStorage.getRefreshToken(); if (_isTokenExpiringSoon(refresh, const Duration(days: 7))) { - return await rotateRefreshToken(); + return await _rotateRefreshToken(); } final response = await getPOSTResponse( @@ -60,7 +69,7 @@ class TokenService { return accountResp; } - static Future rotateRefreshToken() async { + static Future _rotateRefreshToken() async { _logger.fine('Rotating refresh token...'); final refresh = await AuthorizationStorage.getRefreshToken(); @@ -82,4 +91,89 @@ class TokenService { return loginResponse; } + /// Enhanced method to handle unexpected 401 errors with fallback logic + static Future handleUnauthorized() async { + _logger.warning('Handling unexpected 401 unauthorized error'); + + return AuthCircuitBreaker.execute(() async { + return RetryHelper.withExponentialBackoff( + () async { + try { + // Try refresh first (faster) + final currentRefresh = await AuthorizationStorage.getRefreshToken(); + if (!_isTokenExpiringSoon(currentRefresh, const Duration(days: 1))) { + _logger.fine('Attempting access token refresh for 401 recovery'); + await TokenRefreshMutex().executeRefresh(() async { + await refreshAccessToken(); + return 'refreshed'; + }); + return; + } + + // Fallback to rotation if refresh token expiring soon + _logger.fine('Attempting refresh token rotation for 401 recovery'); + await TokenRefreshMutex().executeRotation(() async { + await _rotateRefreshToken(); + }); + } catch (e) { + _logger.severe('Token recovery failed: $e'); + throw AuthenticationFailedException('Token recovery failed', toException(e)); + } + }, + maxRetries: 2, + shouldRetry: (error) { + // Only retry on network errors, not auth errors + return RetryHelper.isRetryableError(error) && !_isAuthError(error); + }, + ); + }); + } + + /// Enhanced getAccessToken with better error handling + static Future getAccessTokenSafe() async { + return AuthCircuitBreaker.execute(() async { + return RetryHelper.withExponentialBackoff( + () async { + TokenData token = await AuthorizationStorage.getAccessToken(); + if (_isTokenExpiringSoon(token, const Duration(hours: 4))) { + // Use mutex to prevent concurrent refresh operations + final refreshedToken = await TokenRefreshMutex().executeRefresh(() async { + return (await refreshAccessToken()).accessToken.token; + }); + return refreshedToken; + } + return token.token; + }, + maxRetries: 2, + shouldRetry: (error) => RetryHelper.isRetryableError(error), + ); + }); + } + + /// Check if error is authentication-related (non-retryable) + static bool _isAuthError(Exception error) { + if (error is ErrorUnauthorized || error is AuthenticationFailedException) { + return true; + } + + if (error is ErrorResponse && error.code == 401) { + return true; + } + + final errorString = error.toString().toLowerCase(); + return errorString.contains('unauthorized') || + errorString.contains('401') || + errorString.contains('authentication') || + errorString.contains('token'); + } + + /// Get circuit breaker status for debugging + static Map getAuthStatus() { + return { + 'circuitBreaker': AuthCircuitBreaker.getStatus(), + 'tokenMutex': TokenRefreshMutex().getStatus(), + 'timestamp': DateTime.now().toIso8601String(), + }; + } + } diff --git a/frontend/pshared/lib/service/authorization/token_mutex.dart b/frontend/pshared/lib/service/authorization/token_mutex.dart new file mode 100644 index 0000000..35ac296 --- /dev/null +++ b/frontend/pshared/lib/service/authorization/token_mutex.dart @@ -0,0 +1,103 @@ +import 'dart:async'; + +import 'package:logging/logging.dart'; + +/// Mutex to prevent concurrent token refresh operations +/// This ensures only one refresh operation happens at a time, +/// preventing race conditions during app startup when multiple +/// providers try to refresh tokens simultaneously. +class TokenRefreshMutex { + static final _instance = TokenRefreshMutex._(); + factory TokenRefreshMutex() => _instance; + TokenRefreshMutex._(); + + static final _logger = Logger('service.authorization.token_mutex'); + + Completer? _currentRefresh; + Completer? _currentRotation; + + /// Execute a token refresh operation with mutex protection + /// If another refresh is in progress, wait for it to complete + Future executeRefresh(Future Function() refreshOperation) async { + if (_currentRefresh != null) { + _logger.fine('Token refresh already in progress, waiting for completion'); + return await _currentRefresh!.future; + } + + _logger.fine('Starting new token refresh operation'); + _currentRefresh = Completer(); + + try { + final result = await refreshOperation(); + if (_currentRefresh != null) { + _currentRefresh!.complete(result); + _logger.fine('Token refresh completed successfully'); + } + return result; + } catch (e, st) { + _logger.warning('Token refresh failed', e, st); + if (_currentRefresh != null) { + _currentRefresh!.completeError(e, st); + } + rethrow; + } finally { + _currentRefresh = null; + } + } + + /// Execute a token rotation operation with mutex protection + /// If another rotation is in progress, wait for it to complete + Future executeRotation(Future Function() rotationOperation) async { + if (_currentRotation != null) { + _logger.fine('Token rotation already in progress, waiting for completion'); + return await _currentRotation!.future; + } + + _logger.fine('Starting new token rotation operation'); + _currentRotation = Completer(); + + try { + await rotationOperation(); + if (_currentRotation != null) { + _currentRotation!.complete(); + _logger.fine('Token rotation completed successfully'); + } + } catch (e, st) { + _logger.warning('Token rotation failed', e, st); + if (_currentRotation != null) { + _currentRotation!.completeError(e, st); + } + rethrow; + } finally { + _currentRotation = null; + } + } + + /// Check if a refresh operation is currently in progress + bool get isRefreshInProgress => _currentRefresh != null; + + /// Check if a rotation operation is currently in progress + bool get isRotationInProgress => _currentRotation != null; + + /// Get current status for debugging + Map getStatus() { + return { + 'refreshInProgress': isRefreshInProgress, + 'rotationInProgress': isRotationInProgress, + 'timestamp': DateTime.now().toIso8601String(), + }; + } + + /// Force reset the mutex (for testing or emergency situations) + void forceReset() { + _logger.warning('Force resetting token refresh mutex'); + if (_currentRefresh != null && !_currentRefresh!.isCompleted) { + _currentRefresh!.completeError(Exception('Mutex force reset')); + } + if (_currentRotation != null && !_currentRotation!.isCompleted) { + _currentRotation!.completeError(Exception('Mutex force reset')); + } + _currentRefresh = null; + _currentRotation = null; + } +} \ No newline at end of file diff --git a/frontend/pshared/lib/service/secure_storage.dart b/frontend/pshared/lib/service/secure_storage.dart index fa0f9e0..90df91b 100644 --- a/frontend/pshared/lib/service/secure_storage.dart +++ b/frontend/pshared/lib/service/secure_storage.dart @@ -1,5 +1,6 @@ import 'package:shared_preferences/shared_preferences.dart'; + class SecureStorageService { static Future get(String key) async { final prefs = await SharedPreferences.getInstance(); @@ -18,6 +19,11 @@ class SecureStorageService { return _setImp(prefs, key, value); } + static Future containsKey(String key) async { + final prefs = await SharedPreferences.getInstance(); + return prefs.containsKey(key); + } + static Future delete(String key) async { final prefs = await SharedPreferences.getInstance(); await prefs.remove(key); diff --git a/frontend/pshared/lib/service/services.dart b/frontend/pshared/lib/service/services.dart index efeb745..f6c9564 100644 --- a/frontend/pshared/lib/service/services.dart +++ b/frontend/pshared/lib/service/services.dart @@ -6,27 +6,13 @@ class Services { static const String invitations = 'invitations'; static const String organization = 'organizations'; static const String permission = 'permissions'; - static const String project = 'projects'; - static const String pgroup = 'priority_groups'; - static const String priorities = 'priorities'; - static const String reactions = 'reactions'; static const String storage = 'storage'; - static const String taskStatus = 'statuses'; - static const String tasks = 'tasks'; static const String amplitude = 'amplitude'; - static const String automations = 'automation'; - static const String changes = 'changes'; static const String clients = 'clients'; - static const String invoices = 'invoices'; static const String logo = 'logo'; static const String notifications = 'notifications'; static const String policies = 'policies'; - static const String properties = 'properties'; static const String refreshTokens = 'refresh_tokens'; static const String roles = 'roles'; - static const String steps = 'steps'; - static const String teams = 'teams'; - static const String workflows = 'workflows'; - static const String workspaces = 'workspaces'; } diff --git a/frontend/pshared/lib/provider/exception.dart b/frontend/pshared/lib/utils/exception.dart similarity index 100% rename from frontend/pshared/lib/provider/exception.dart rename to frontend/pshared/lib/utils/exception.dart diff --git a/frontend/pweb/lib/.gitignore b/frontend/pweb/lib/.gitignore new file mode 100644 index 0000000..86d4c2d --- /dev/null +++ b/frontend/pweb/lib/.gitignore @@ -0,0 +1 @@ +generated diff --git a/frontend/pweb/lib/generated/i18n/app_localizations.dart b/frontend/pweb/lib/generated/i18n/app_localizations.dart deleted file mode 100644 index 91889a2..0000000 --- a/frontend/pweb/lib/generated/i18n/app_localizations.dart +++ /dev/null @@ -1,1562 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:intl/intl.dart' as intl; - -import 'app_localizations_en.dart'; -import 'app_localizations_ru.dart'; - -// ignore_for_file: type=lint - -/// Callers can lookup localized strings with an instance of AppLocalizations -/// returned by `AppLocalizations.of(context)`. -/// -/// Applications need to include `AppLocalizations.delegate()` in their app's -/// `localizationDelegates` list, and the locales they support in the app's -/// `supportedLocales` list. For example: -/// -/// ```dart -/// import 'i18n/app_localizations.dart'; -/// -/// return MaterialApp( -/// localizationsDelegates: AppLocalizations.localizationsDelegates, -/// supportedLocales: AppLocalizations.supportedLocales, -/// home: MyApplicationHome(), -/// ); -/// ``` -/// -/// ## Update pubspec.yaml -/// -/// Please make sure to update your pubspec.yaml to include the following -/// packages: -/// -/// ```yaml -/// dependencies: -/// # Internationalization support. -/// flutter_localizations: -/// sdk: flutter -/// intl: any # Use the pinned version from flutter_localizations -/// -/// # Rest of dependencies -/// ``` -/// -/// ## iOS Applications -/// -/// iOS applications define key application metadata, including supported -/// locales, in an Info.plist file that is built into the application bundle. -/// To configure the locales supported by your app, you’ll need to edit this -/// file. -/// -/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. -/// Then, in the Project Navigator, open the Info.plist file under the Runner -/// project’s Runner folder. -/// -/// Next, select the Information Property List item, select Add Item from the -/// Editor menu, then select Localizations from the pop-up menu. -/// -/// Select and expand the newly-created Localizations item then, for each -/// locale your application supports, add a new item and select the locale -/// you wish to add from the pop-up menu in the Value field. This list should -/// be consistent with the languages listed in the AppLocalizations.supportedLocales -/// property. -abstract class AppLocalizations { - AppLocalizations(String locale) - : localeName = intl.Intl.canonicalizedLocale(locale.toString()); - - final String localeName; - - static AppLocalizations? of(BuildContext context) { - return Localizations.of(context, AppLocalizations); - } - - static const LocalizationsDelegate delegate = - _AppLocalizationsDelegate(); - - /// A list of this localizations delegate along with the default localizations - /// delegates. - /// - /// Returns a list of localizations delegates containing this delegate along with - /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, - /// and GlobalWidgetsLocalizations.delegate. - /// - /// Additional delegates can be added by appending to this list in - /// MaterialApp. This list does not have to be used at all if a custom list - /// of delegates is preferred or required. - static const List> localizationsDelegates = - >[ - delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ]; - - /// A list of this localizations delegate's supported locales. - static const List supportedLocales = [ - Locale('en'), - Locale('ru'), - ]; - - /// No description provided for @login. - /// - /// In en, this message translates to: - /// **'Login'** - String get login; - - /// No description provided for @logout. - /// - /// In en, this message translates to: - /// **'Logout'** - String get logout; - - /// No description provided for @profile. - /// - /// In en, this message translates to: - /// **'Profile'** - String get profile; - - /// No description provided for @signup. - /// - /// In en, this message translates to: - /// **'Sign up'** - String get signup; - - /// No description provided for @username. - /// - /// In en, this message translates to: - /// **'Email'** - String get username; - - /// No description provided for @usernameHint. - /// - /// In en, this message translates to: - /// **'email@example.com'** - String get usernameHint; - - /// No description provided for @usernameErrorInvalid. - /// - /// In en, this message translates to: - /// **'Provide a valid email address'** - String get usernameErrorInvalid; - - /// No description provided for @usernameUnknownTLD. - /// - /// In en, this message translates to: - /// **'Domain .{domain} is not known, please, check it'** - String usernameUnknownTLD(Object domain); - - /// No description provided for @password. - /// - /// In en, this message translates to: - /// **'Password'** - String get password; - - /// No description provided for @confirmPassword. - /// - /// In en, this message translates to: - /// **'Confirm password'** - String get confirmPassword; - - /// No description provided for @passwordValidationRuleDigit. - /// - /// In en, this message translates to: - /// **'has digit'** - String get passwordValidationRuleDigit; - - /// No description provided for @passwordValidationRuleUpperCase. - /// - /// In en, this message translates to: - /// **'has uppercase letter'** - String get passwordValidationRuleUpperCase; - - /// No description provided for @passwordValidationRuleLowerCase. - /// - /// In en, this message translates to: - /// **'has lowercase letter'** - String get passwordValidationRuleLowerCase; - - /// No description provided for @passwordValidationRuleSpecialCharacter. - /// - /// In en, this message translates to: - /// **'has special character letter'** - String get passwordValidationRuleSpecialCharacter; - - /// No description provided for @passwordValidationRuleMinCharacters. - /// - /// In en, this message translates to: - /// **'is {charNum} characters long at least'** - String passwordValidationRuleMinCharacters(Object charNum); - - /// No description provided for @passwordsDoNotMatch. - /// - /// In en, this message translates to: - /// **'Passwords do not match'** - String get passwordsDoNotMatch; - - /// No description provided for @passwordValidationError. - /// - /// In en, this message translates to: - /// **'Check that your password {matchesCriteria}'** - String passwordValidationError(Object matchesCriteria); - - /// No description provided for @notificationError. - /// - /// In en, this message translates to: - /// **'Error occurred: {error}'** - String notificationError(Object error); - - /// No description provided for @loginUserNotFound. - /// - /// In en, this message translates to: - /// **'Account {account} has not been registered in the system'** - String loginUserNotFound(Object account); - - /// No description provided for @loginPasswordIncorrect. - /// - /// In en, this message translates to: - /// **'Authorization failed, please check your password'** - String get loginPasswordIncorrect; - - /// No description provided for @internalErrorOccurred. - /// - /// In en, this message translates to: - /// **'An internal server error occurred: {error}, we already know about it and working hard to fix it'** - String internalErrorOccurred(Object error); - - /// No description provided for @noErrorInformation. - /// - /// In en, this message translates to: - /// **'Some error occurred, but we have not error information. We are already investigating the issue'** - String get noErrorInformation; - - /// No description provided for @yourName. - /// - /// In en, this message translates to: - /// **'Your name'** - String get yourName; - - /// No description provided for @nameHint. - /// - /// In en, this message translates to: - /// **'John Doe'** - String get nameHint; - - /// No description provided for @errorPageNotFoundTitle. - /// - /// In en, this message translates to: - /// **'Page Not Found'** - String get errorPageNotFoundTitle; - - /// No description provided for @errorPageNotFoundMessage. - /// - /// In en, this message translates to: - /// **'Oops! We couldn\'t find that page.'** - String get errorPageNotFoundMessage; - - /// No description provided for @errorPageNotFoundHint. - /// - /// In en, this message translates to: - /// **'The page you\'re looking for doesn\'t exist or has been moved. Please check the URL or return to the home page.'** - String get errorPageNotFoundHint; - - /// No description provided for @errorUnknown. - /// - /// In en, this message translates to: - /// **'Unknown error occurred'** - String get errorUnknown; - - /// No description provided for @unknown. - /// - /// In en, this message translates to: - /// **'unknown'** - String get unknown; - - /// No description provided for @goToLogin. - /// - /// In en, this message translates to: - /// **'Go to Login'** - String get goToLogin; - - /// No description provided for @goBack. - /// - /// In en, this message translates to: - /// **'Go Back'** - String get goBack; - - /// No description provided for @goToMainPage. - /// - /// In en, this message translates to: - /// **'Go to Main Page'** - String get goToMainPage; - - /// No description provided for @goToSignUp. - /// - /// In en, this message translates to: - /// **'Go to Sign Up'** - String get goToSignUp; - - /// No description provided for @signupError. - /// - /// In en, this message translates to: - /// **'Failed to signup: {error}'** - String signupError(Object error); - - /// No description provided for @signupSuccess. - /// - /// In en, this message translates to: - /// **'Email confirmation message has been sent to {email}. Please, open it and click link to activate your account.'** - String signupSuccess(Object email); - - /// No description provided for @connectivityError. - /// - /// In en, this message translates to: - /// **'Cannot reach the server at {serverAddress}. Check your network and try again.'** - String connectivityError(Object serverAddress); - - /// No description provided for @errorAccountExists. - /// - /// In en, this message translates to: - /// **'Account already exists'** - String get errorAccountExists; - - /// No description provided for @errorAccountNotVerified. - /// - /// In en, this message translates to: - /// **'Your account hasn\'t been verified yet. Please check your email to complete the verification'** - String get errorAccountNotVerified; - - /// No description provided for @errorLoginUnauthorized. - /// - /// In en, this message translates to: - /// **'Login or password is incorrect. Please try again'** - String get errorLoginUnauthorized; - - /// No description provided for @errorInternalError. - /// - /// In en, this message translates to: - /// **'An internal error occurred. We\'re aware of the issue and working to resolve it. Please try again later'** - String get errorInternalError; - - /// No description provided for @errorVerificationTokenNotFound. - /// - /// In en, this message translates to: - /// **'Account for verification not found. Sign up again'** - String get errorVerificationTokenNotFound; - - /// No description provided for @created. - /// - /// In en, this message translates to: - /// **'Created'** - String get created; - - /// No description provided for @edited. - /// - /// In en, this message translates to: - /// **'Edited'** - String get edited; - - /// No description provided for @errorDataConflict. - /// - /// In en, this message translates to: - /// **'We can’t process your data because it has conflicting or contradictory information.'** - String get errorDataConflict; - - /// No description provided for @errorAccessDenied. - /// - /// In en, this message translates to: - /// **'You do not have permission to access this resource. If you need access, please contact an administrator.'** - String get errorAccessDenied; - - /// No description provided for @errorBrokenPayload. - /// - /// In en, this message translates to: - /// **'The data you sent is invalid or incomplete. Please check your submission and try again.'** - String get errorBrokenPayload; - - /// No description provided for @errorInvalidArgument. - /// - /// In en, this message translates to: - /// **'One or more arguments are invalid. Verify your input and try again.'** - String get errorInvalidArgument; - - /// No description provided for @errorBrokenReference. - /// - /// In en, this message translates to: - /// **'The resource you\'re trying to access could not be referenced. It may have been moved or deleted.'** - String get errorBrokenReference; - - /// No description provided for @errorInvalidQueryParameter. - /// - /// In en, this message translates to: - /// **'One or more query parameters are missing or incorrect. Check them and try again.'** - String get errorInvalidQueryParameter; - - /// No description provided for @errorNotImplemented. - /// - /// In en, this message translates to: - /// **'This feature is not yet available. Please try again later or contact support.'** - String get errorNotImplemented; - - /// No description provided for @errorLicenseRequired. - /// - /// In en, this message translates to: - /// **'A valid license is required to perform this action. Please contact your administrator.'** - String get errorLicenseRequired; - - /// No description provided for @errorNotFound. - /// - /// In en, this message translates to: - /// **'We couldn\'t find the resource you requested. It may have been removed or is temporarily unavailable.'** - String get errorNotFound; - - /// No description provided for @errorNameMissing. - /// - /// In en, this message translates to: - /// **'Please provide a name before continuing.'** - String get errorNameMissing; - - /// No description provided for @errorEmailMissing. - /// - /// In en, this message translates to: - /// **'Please provide an email address before continuing.'** - String get errorEmailMissing; - - /// No description provided for @errorPasswordMissing. - /// - /// In en, this message translates to: - /// **'Please provide a password before continuing.'** - String get errorPasswordMissing; - - /// No description provided for @errorEmailNotRegistered. - /// - /// In en, this message translates to: - /// **'We could not find an account associated with that email address.'** - String get errorEmailNotRegistered; - - /// No description provided for @errorDuplicateEmail. - /// - /// In en, this message translates to: - /// **'This email address is already in use. Try another one or reset your password.'** - String get errorDuplicateEmail; - - /// No description provided for @showDetailsAction. - /// - /// In en, this message translates to: - /// **'Show Details'** - String get showDetailsAction; - - /// No description provided for @errorLogin. - /// - /// In en, this message translates to: - /// **'Error logging in'** - String get errorLogin; - - /// Error message displayed when invitation creation fails - /// - /// In en, this message translates to: - /// **'Failed to create invitaiton'** - String get errorCreatingInvitation; - - /// No description provided for @footerCompanyName. - /// - /// In en, this message translates to: - /// **'Sibilla Solutions LTD'** - String get footerCompanyName; - - /// No description provided for @footerAddress. - /// - /// In en, this message translates to: - /// **'27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus'** - String get footerAddress; - - /// No description provided for @footerSupport. - /// - /// In en, this message translates to: - /// **'Support'** - String get footerSupport; - - /// No description provided for @footerEmail. - /// - /// In en, this message translates to: - /// **'Email TBD'** - String get footerEmail; - - /// No description provided for @footerPhoneLabel. - /// - /// In en, this message translates to: - /// **'Phone'** - String get footerPhoneLabel; - - /// No description provided for @footerPhone. - /// - /// In en, this message translates to: - /// **'+357 22 000 253'** - String get footerPhone; - - /// No description provided for @footerTermsOfService. - /// - /// In en, this message translates to: - /// **'Terms of Service'** - String get footerTermsOfService; - - /// No description provided for @footerPrivacyPolicy. - /// - /// In en, this message translates to: - /// **'Privacy Policy'** - String get footerPrivacyPolicy; - - /// No description provided for @footerCookiePolicy. - /// - /// In en, this message translates to: - /// **'Cookie Policy'** - String get footerCookiePolicy; - - /// No description provided for @navigationLogout. - /// - /// In en, this message translates to: - /// **'Logout'** - String get navigationLogout; - - /// No description provided for @dashboard. - /// - /// In en, this message translates to: - /// **'Dashboard'** - String get dashboard; - - /// No description provided for @navigationUsersSettings. - /// - /// In en, this message translates to: - /// **'Users'** - String get navigationUsersSettings; - - /// No description provided for @navigationRolesSettings. - /// - /// In en, this message translates to: - /// **'Roles'** - String get navigationRolesSettings; - - /// No description provided for @navigationPermissionsSettings. - /// - /// In en, this message translates to: - /// **'Permissions'** - String get navigationPermissionsSettings; - - /// No description provided for @usersManagement. - /// - /// In en, this message translates to: - /// **'User Management'** - String get usersManagement; - - /// No description provided for @navigationOrganizationSettings. - /// - /// In en, this message translates to: - /// **'Organization settings'** - String get navigationOrganizationSettings; - - /// No description provided for @navigationAccountSettings. - /// - /// In en, this message translates to: - /// **'Profile settings'** - String get navigationAccountSettings; - - /// No description provided for @twoFactorPrompt. - /// - /// In en, this message translates to: - /// **'Enter the 6-digit code we sent to your device'** - String get twoFactorPrompt; - - /// No description provided for @twoFactorResend. - /// - /// In en, this message translates to: - /// **'Didn’t receive a code? Resend'** - String get twoFactorResend; - - /// No description provided for @twoFactorTitle. - /// - /// In en, this message translates to: - /// **'Two-Factor Authentication'** - String get twoFactorTitle; - - /// No description provided for @twoFactorError. - /// - /// In en, this message translates to: - /// **'Invalid code. Please try again.'** - String get twoFactorError; - - /// No description provided for @payoutNavDashboard. - /// - /// In en, this message translates to: - /// **'Dashboard'** - String get payoutNavDashboard; - - /// No description provided for @payoutNavSendPayout. - /// - /// In en, this message translates to: - /// **'Send payout'** - String get payoutNavSendPayout; - - /// No description provided for @payoutNavRecipients. - /// - /// In en, this message translates to: - /// **'Recipients'** - String get payoutNavRecipients; - - /// No description provided for @payoutNavReports. - /// - /// In en, this message translates to: - /// **'Reports'** - String get payoutNavReports; - - /// No description provided for @payoutNavSettings. - /// - /// In en, this message translates to: - /// **'Settings'** - String get payoutNavSettings; - - /// No description provided for @payoutNavLogout. - /// - /// In en, this message translates to: - /// **'Logout'** - String get payoutNavLogout; - - /// No description provided for @payoutNavMethods. - /// - /// In en, this message translates to: - /// **'Payouts'** - String get payoutNavMethods; - - /// No description provided for @expand. - /// - /// In en, this message translates to: - /// **'Expand'** - String get expand; - - /// No description provided for @collapse. - /// - /// In en, this message translates to: - /// **'Collapse'** - String get collapse; - - /// Title of the recipient address book page - /// - /// In en, this message translates to: - /// **'Recipient address book'** - String get pageTitleRecipients; - - /// Tooltip and button label to add a new recipient - /// - /// In en, this message translates to: - /// **'Add new'** - String get actionAddNew; - - /// Column header for who manages the payout data - /// - /// In en, this message translates to: - /// **'Data owner'** - String get colDataOwner; - - /// Column header for recipient avatar - /// - /// In en, this message translates to: - /// **'Avatar'** - String get colAvatar; - - /// Column header for recipient name - /// - /// In en, this message translates to: - /// **'Name'** - String get colName; - - /// Column header for recipient email address - /// - /// In en, this message translates to: - /// **'Email'** - String get colEmail; - - /// Column header for payout readiness status - /// - /// In en, this message translates to: - /// **'Status'** - String get colStatus; - - /// Status indicating payouts can be sent immediately - /// - /// In en, this message translates to: - /// **'Ready'** - String get statusReady; - - /// Status indicating recipient is registered but not yet fully ready - /// - /// In en, this message translates to: - /// **'Registered'** - String get statusRegistered; - - /// Status indicating recipient has not completed registration - /// - /// In en, this message translates to: - /// **'Not registered'** - String get statusNotRegistered; - - /// Label for recipients whose payout data is managed internally by the user/company - /// - /// In en, this message translates to: - /// **'Managed by me'** - String get typeInternal; - - /// Label for recipients who manage their own payout data - /// - /// In en, this message translates to: - /// **'Self‑managed'** - String get typeExternal; - - /// No description provided for @searchHint. - /// - /// In en, this message translates to: - /// **'Search recipients'** - String get searchHint; - - /// No description provided for @colActions. - /// - /// In en, this message translates to: - /// **'Actions'** - String get colActions; - - /// No description provided for @menuEdit. - /// - /// In en, this message translates to: - /// **'Edit'** - String get menuEdit; - - /// No description provided for @menuSendPayout. - /// - /// In en, this message translates to: - /// **'Send payout'** - String get menuSendPayout; - - /// No description provided for @tooltipRowActions. - /// - /// In en, this message translates to: - /// **'More actions'** - String get tooltipRowActions; - - /// No description provided for @accountSettings. - /// - /// In en, this message translates to: - /// **'Account Settings'** - String get accountSettings; - - /// No description provided for @accountNameUpdateError. - /// - /// In en, this message translates to: - /// **'Failed to update account name'** - String get accountNameUpdateError; - - /// No description provided for @settingsSuccessfullyUpdated. - /// - /// In en, this message translates to: - /// **'Settings successfully updated'** - String get settingsSuccessfullyUpdated; - - /// No description provided for @language. - /// - /// In en, this message translates to: - /// **'Language'** - String get language; - - /// No description provided for @failedToUpdateLanguage. - /// - /// In en, this message translates to: - /// **'Failed to update language'** - String get failedToUpdateLanguage; - - /// No description provided for @settingsImageUpdateError. - /// - /// In en, this message translates to: - /// **'Couldn\'t update the image'** - String get settingsImageUpdateError; - - /// No description provided for @settingsImageTitle. - /// - /// In en, this message translates to: - /// **'Image'** - String get settingsImageTitle; - - /// No description provided for @settingsImageHint. - /// - /// In en, this message translates to: - /// **'Tap to change the image'** - String get settingsImageHint; - - /// No description provided for @accountName. - /// - /// In en, this message translates to: - /// **'Name'** - String get accountName; - - /// No description provided for @accountNameHint. - /// - /// In en, this message translates to: - /// **'Specify your name'** - String get accountNameHint; - - /// No description provided for @avatar. - /// - /// In en, this message translates to: - /// **'Profile photo'** - String get avatar; - - /// No description provided for @avatarHint. - /// - /// In en, this message translates to: - /// **'Tap to update'** - String get avatarHint; - - /// No description provided for @avatarUpdateError. - /// - /// In en, this message translates to: - /// **'Failed to update profile photo'** - String get avatarUpdateError; - - /// No description provided for @settings. - /// - /// In en, this message translates to: - /// **'Settings'** - String get settings; - - /// No description provided for @notSet. - /// - /// In en, this message translates to: - /// **'not set'** - String get notSet; - - /// No description provided for @search. - /// - /// In en, this message translates to: - /// **'Search...'** - String get search; - - /// No description provided for @ok. - /// - /// In en, this message translates to: - /// **'Ok'** - String get ok; - - /// No description provided for @cancel. - /// - /// In en, this message translates to: - /// **'Cancel'** - String get cancel; - - /// No description provided for @confirm. - /// - /// In en, this message translates to: - /// **'Confirm'** - String get confirm; - - /// No description provided for @back. - /// - /// In en, this message translates to: - /// **'Back'** - String get back; - - /// Title of the operation history page - /// - /// In en, this message translates to: - /// **'Operation history'** - String get operationfryTitle; - - /// Label for the filters expansion panel - /// - /// In en, this message translates to: - /// **'Filters'** - String get filters; - - /// Label for the date‐range filter - /// - /// In en, this message translates to: - /// **'Period'** - String get period; - - /// Placeholder when no period is selected - /// - /// In en, this message translates to: - /// **'Select period'** - String get selectPeriod; - - /// Button text to apply the filters - /// - /// In en, this message translates to: - /// **'Apply'** - String get apply; - - /// Template for a single status filter chip - /// - /// In en, this message translates to: - /// **'{status}'** - String status(String status); - - /// Status indicating the operation succeeded - /// - /// In en, this message translates to: - /// **'Successful'** - String get operationStatusSuccessful; - - /// Status indicating the operation is pending - /// - /// In en, this message translates to: - /// **'Pending'** - String get operationStatusPending; - - /// Status indicating the operation failed - /// - /// In en, this message translates to: - /// **'Unsuccessful'** - String get operationStatusUnsuccessful; - - /// Table column header for status - /// - /// In en, this message translates to: - /// **'Status'** - String get statusColumn; - - /// Table column header for file name - /// - /// In en, this message translates to: - /// **'File name'** - String get fileNameColumn; - - /// Table column header for the original amount - /// - /// In en, this message translates to: - /// **'Amount'** - String get amountColumn; - - /// Table column header for the converted amount - /// - /// In en, this message translates to: - /// **'To amount'** - String get toAmountColumn; - - /// Table column header for the payment ID - /// - /// In en, this message translates to: - /// **'Pay ID'** - String get payIdColumn; - - /// Table column header for the masked card number - /// - /// In en, this message translates to: - /// **'Card number'** - String get cardNumberColumn; - - /// Table column header for recipient name - /// - /// In en, this message translates to: - /// **'Name'** - String get nameColumn; - - /// Table column header for the date/time - /// - /// In en, this message translates to: - /// **'Date'** - String get dateColumn; - - /// Table column header for any comment - /// - /// In en, this message translates to: - /// **'Comment'** - String get commentColumn; - - /// No description provided for @paymentConfigTitle. - /// - /// In en, this message translates to: - /// **'Where to receive money'** - String get paymentConfigTitle; - - /// No description provided for @paymentConfigSubtitle. - /// - /// In en, this message translates to: - /// **'Add multiple methods and choose your primary one.'** - String get paymentConfigSubtitle; - - /// No description provided for @addPaymentMethod. - /// - /// In en, this message translates to: - /// **'Add payment method'** - String get addPaymentMethod; - - /// No description provided for @makeMain. - /// - /// In en, this message translates to: - /// **'Make primary'** - String get makeMain; - - /// No description provided for @advanced. - /// - /// In en, this message translates to: - /// **'Advanced'** - String get advanced; - - /// No description provided for @fallbackExplanation. - /// - /// In en, this message translates to: - /// **'If the primary method is unavailable, we will try the next enabled one in the list.'** - String get fallbackExplanation; - - /// Button label to delete a payment method - /// - /// In en, this message translates to: - /// **'Delete'** - String get delete; - - /// Confirmation dialog message shown before a payment method is removed - /// - /// In en, this message translates to: - /// **'Are you sure you want to delete this payment method?'** - String get deletePaymentConfirmation; - - /// Button label to edit a payment method - /// - /// In en, this message translates to: - /// **'Edit'** - String get edit; - - /// Tooltip for an overflow menu button that reveals extra actions for a payment method - /// - /// In en, this message translates to: - /// **'More actions'** - String get moreActions; - - /// No description provided for @noPayouts. - /// - /// In en, this message translates to: - /// **'No Payouts'** - String get noPayouts; - - /// No description provided for @enterBankName. - /// - /// In en, this message translates to: - /// **'Enter bank name'** - String get enterBankName; - - /// No description provided for @paymentType. - /// - /// In en, this message translates to: - /// **'Payment Method Type'** - String get paymentType; - - /// No description provided for @selectPaymentType. - /// - /// In en, this message translates to: - /// **'Please select a payment method type'** - String get selectPaymentType; - - /// No description provided for @paymentTypeCard. - /// - /// In en, this message translates to: - /// **'Credit Card'** - String get paymentTypeCard; - - /// No description provided for @paymentTypeBankAccount. - /// - /// In en, this message translates to: - /// **'Russian Bank Account'** - String get paymentTypeBankAccount; - - /// No description provided for @paymentTypeIban. - /// - /// In en, this message translates to: - /// **'IBAN'** - String get paymentTypeIban; - - /// No description provided for @paymentTypeWallet. - /// - /// In en, this message translates to: - /// **'Wallet'** - String get paymentTypeWallet; - - /// No description provided for @cardNumber. - /// - /// In en, this message translates to: - /// **'Card Number'** - String get cardNumber; - - /// No description provided for @enterCardNumber. - /// - /// In en, this message translates to: - /// **'Enter the card number'** - String get enterCardNumber; - - /// No description provided for @cardholderName. - /// - /// In en, this message translates to: - /// **'Cardholder Name'** - String get cardholderName; - - /// No description provided for @iban. - /// - /// In en, this message translates to: - /// **'IBAN'** - String get iban; - - /// No description provided for @enterIban. - /// - /// In en, this message translates to: - /// **'Enter IBAN'** - String get enterIban; - - /// No description provided for @bic. - /// - /// In en, this message translates to: - /// **'BIC'** - String get bic; - - /// No description provided for @bankName. - /// - /// In en, this message translates to: - /// **'Bank Name'** - String get bankName; - - /// No description provided for @accountHolder. - /// - /// In en, this message translates to: - /// **'Account Holder'** - String get accountHolder; - - /// No description provided for @enterAccountHolder. - /// - /// In en, this message translates to: - /// **'Enter account holder'** - String get enterAccountHolder; - - /// No description provided for @enterBic. - /// - /// In en, this message translates to: - /// **'Enter BIC'** - String get enterBic; - - /// No description provided for @walletId. - /// - /// In en, this message translates to: - /// **'Wallet ID'** - String get walletId; - - /// No description provided for @enterWalletId. - /// - /// In en, this message translates to: - /// **'Enter wallet ID'** - String get enterWalletId; - - /// No description provided for @recipients. - /// - /// In en, this message translates to: - /// **'Recipients'** - String get recipients; - - /// No description provided for @recipientName. - /// - /// In en, this message translates to: - /// **'Recipient Name'** - String get recipientName; - - /// No description provided for @enterRecipientName. - /// - /// In en, this message translates to: - /// **'Enter recipient name'** - String get enterRecipientName; - - /// No description provided for @inn. - /// - /// In en, this message translates to: - /// **'INN'** - String get inn; - - /// No description provided for @enterInn. - /// - /// In en, this message translates to: - /// **'Enter INN'** - String get enterInn; - - /// No description provided for @kpp. - /// - /// In en, this message translates to: - /// **'KPP'** - String get kpp; - - /// No description provided for @enterKpp. - /// - /// In en, this message translates to: - /// **'Enter KPP'** - String get enterKpp; - - /// No description provided for @accountNumber. - /// - /// In en, this message translates to: - /// **'Account Number'** - String get accountNumber; - - /// No description provided for @enterAccountNumber. - /// - /// In en, this message translates to: - /// **'Enter account number'** - String get enterAccountNumber; - - /// No description provided for @correspondentAccount. - /// - /// In en, this message translates to: - /// **'Correspondent Account'** - String get correspondentAccount; - - /// No description provided for @enterCorrespondentAccount. - /// - /// In en, this message translates to: - /// **'Enter correspondent account'** - String get enterCorrespondentAccount; - - /// No description provided for @bik. - /// - /// In en, this message translates to: - /// **'BIK'** - String get bik; - - /// No description provided for @enterBik. - /// - /// In en, this message translates to: - /// **'Enter BIK'** - String get enterBik; - - /// No description provided for @add. - /// - /// In en, this message translates to: - /// **'Add'** - String get add; - - /// No description provided for @expiryDate. - /// - /// In en, this message translates to: - /// **'Expiry (MM/YY)'** - String get expiryDate; - - /// No description provided for @firstName. - /// - /// In en, this message translates to: - /// **'First Name'** - String get firstName; - - /// No description provided for @enterFirstName. - /// - /// In en, this message translates to: - /// **'Enter First Name'** - String get enterFirstName; - - /// No description provided for @lastName. - /// - /// In en, this message translates to: - /// **'Last Name'** - String get lastName; - - /// No description provided for @enterLastName. - /// - /// In en, this message translates to: - /// **'Enter Last Name'** - String get enterLastName; - - /// No description provided for @sendSingle. - /// - /// In en, this message translates to: - /// **'Send single transaction'** - String get sendSingle; - - /// No description provided for @sendMultiple. - /// - /// In en, this message translates to: - /// **'Send multiple transactions'** - String get sendMultiple; - - /// No description provided for @addFunds. - /// - /// In en, this message translates to: - /// **'Add Funds'** - String get addFunds; - - /// No description provided for @close. - /// - /// In en, this message translates to: - /// **'Close'** - String get close; - - /// No description provided for @multiplePayout. - /// - /// In en, this message translates to: - /// **'Multiple Payout'** - String get multiplePayout; - - /// No description provided for @howItWorks. - /// - /// In en, this message translates to: - /// **'How it works?'** - String get howItWorks; - - /// No description provided for @exampleTitle. - /// - /// In en, this message translates to: - /// **'File Format & Sample'** - String get exampleTitle; - - /// No description provided for @downloadSampleCSV. - /// - /// In en, this message translates to: - /// **'Download sample.csv'** - String get downloadSampleCSV; - - /// No description provided for @tokenColumn. - /// - /// In en, this message translates to: - /// **'Token (required)'** - String get tokenColumn; - - /// No description provided for @currency. - /// - /// In en, this message translates to: - /// **'Currency'** - String get currency; - - /// No description provided for @amount. - /// - /// In en, this message translates to: - /// **'Amount'** - String get amount; - - /// No description provided for @comment. - /// - /// In en, this message translates to: - /// **'Comment'** - String get comment; - - /// No description provided for @uploadCSV. - /// - /// In en, this message translates to: - /// **'Upload your CSV'** - String get uploadCSV; - - /// No description provided for @upload. - /// - /// In en, this message translates to: - /// **'Upload'** - String get upload; - - /// No description provided for @hintUpload. - /// - /// In en, this message translates to: - /// **'Supported format: .CSV · Max size 1 MB'** - String get hintUpload; - - /// No description provided for @uploadHistory. - /// - /// In en, this message translates to: - /// **'Upload History'** - String get uploadHistory; - - /// No description provided for @payout. - /// - /// In en, this message translates to: - /// **'Payout'** - String get payout; - - /// No description provided for @sendTo. - /// - /// In en, this message translates to: - /// **'Send Payout To'** - String get sendTo; - - /// No description provided for @send. - /// - /// In en, this message translates to: - /// **'Send Payout'** - String get send; - - /// No description provided for @recipientPaysFee. - /// - /// In en, this message translates to: - /// **'Recipient pays the fee'** - String get recipientPaysFee; - - /// Label showing the amount sent - /// - /// In en, this message translates to: - /// **'Sent amount: \${amount}'** - String sentAmount(String amount); - - /// Label showing the transaction fee - /// - /// In en, this message translates to: - /// **'Fee: \${fee}'** - String fee(String fee); - - /// Label showing how much the recipient will receive - /// - /// In en, this message translates to: - /// **'Recipient will receive: \${amount}'** - String recipientWillReceive(String amount); - - /// Label showing the total amount of the transaction - /// - /// In en, this message translates to: - /// **'Total: \${total}'** - String total(String total); - - /// No description provided for @hideDetails. - /// - /// In en, this message translates to: - /// **'Hide Details'** - String get hideDetails; - - /// No description provided for @showDetails. - /// - /// In en, this message translates to: - /// **'Show Details'** - String get showDetails; - - /// No description provided for @whereGetMoney. - /// - /// In en, this message translates to: - /// **'Source of funds for debit'** - String get whereGetMoney; - - /// No description provided for @details. - /// - /// In en, this message translates to: - /// **'Details'** - String get details; - - /// No description provided for @addRecipient. - /// - /// In en, this message translates to: - /// **'Add Recipient'** - String get addRecipient; - - /// No description provided for @editRecipient. - /// - /// In en, this message translates to: - /// **'Edit Recipient'** - String get editRecipient; - - /// No description provided for @saveRecipient. - /// - /// In en, this message translates to: - /// **'Save Recipient'** - String get saveRecipient; - - /// No description provided for @choosePaymentMethod. - /// - /// In en, this message translates to: - /// **'Payment Methods (choose at least 1)'** - String get choosePaymentMethod; - - /// No description provided for @recipientFormRule. - /// - /// In en, this message translates to: - /// **'Recipient must have at least one payment method'** - String get recipientFormRule; - - /// No description provided for @allStatus. - /// - /// In en, this message translates to: - /// **'All'** - String get allStatus; - - /// No description provided for @readyStatus. - /// - /// In en, this message translates to: - /// **'Ready'** - String get readyStatus; - - /// No description provided for @registeredStatus. - /// - /// In en, this message translates to: - /// **'Registered'** - String get registeredStatus; - - /// No description provided for @notRegisteredStatus. - /// - /// In en, this message translates to: - /// **'Not registered'** - String get notRegisteredStatus; - - /// No description provided for @noRecipientSelected. - /// - /// In en, this message translates to: - /// **'No recipient selected'** - String get noRecipientSelected; - - /// No description provided for @companyName. - /// - /// In en, this message translates to: - /// **'Name of your company'** - String get companyName; - - /// No description provided for @companynameRequired. - /// - /// In en, this message translates to: - /// **'Company name required'** - String get companynameRequired; - - /// No description provided for @errorSignUp. - /// - /// In en, this message translates to: - /// **'Error occured while signing up, try again later'** - String get errorSignUp; - - /// No description provided for @companyDescription. - /// - /// In en, this message translates to: - /// **'Company Description'** - String get companyDescription; - - /// No description provided for @companyDescriptionHint. - /// - /// In en, this message translates to: - /// **'Describe any of the fields of the Company\'s business'** - String get companyDescriptionHint; - - /// No description provided for @optional. - /// - /// In en, this message translates to: - /// **'optional'** - String get optional; -} - -class _AppLocalizationsDelegate - extends LocalizationsDelegate { - const _AppLocalizationsDelegate(); - - @override - Future load(Locale locale) { - return SynchronousFuture(lookupAppLocalizations(locale)); - } - - @override - bool isSupported(Locale locale) => - ['en', 'ru'].contains(locale.languageCode); - - @override - bool shouldReload(_AppLocalizationsDelegate old) => false; -} - -AppLocalizations lookupAppLocalizations(Locale locale) { - // Lookup logic when only language code is specified. - switch (locale.languageCode) { - case 'en': - return AppLocalizationsEn(); - case 'ru': - return AppLocalizationsRu(); - } - - throw FlutterError( - 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' - 'an issue with the localizations generation tool. Please file an issue ' - 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.', - ); -} diff --git a/frontend/pweb/lib/generated/i18n/app_localizations_en.dart b/frontend/pweb/lib/generated/i18n/app_localizations_en.dart deleted file mode 100644 index ddcad3b..0000000 --- a/frontend/pweb/lib/generated/i18n/app_localizations_en.dart +++ /dev/null @@ -1,779 +0,0 @@ -// ignore: unused_import -import 'package:intl/intl.dart' as intl; -import 'app_localizations.dart'; - -// ignore_for_file: type=lint - -/// The translations for English (`en`). -class AppLocalizationsEn extends AppLocalizations { - AppLocalizationsEn([String locale = 'en']) : super(locale); - - @override - String get login => 'Login'; - - @override - String get logout => 'Logout'; - - @override - String get profile => 'Profile'; - - @override - String get signup => 'Sign up'; - - @override - String get username => 'Email'; - - @override - String get usernameHint => 'email@example.com'; - - @override - String get usernameErrorInvalid => 'Provide a valid email address'; - - @override - String usernameUnknownTLD(Object domain) { - return 'Domain .$domain is not known, please, check it'; - } - - @override - String get password => 'Password'; - - @override - String get confirmPassword => 'Confirm password'; - - @override - String get passwordValidationRuleDigit => 'has digit'; - - @override - String get passwordValidationRuleUpperCase => 'has uppercase letter'; - - @override - String get passwordValidationRuleLowerCase => 'has lowercase letter'; - - @override - String get passwordValidationRuleSpecialCharacter => - 'has special character letter'; - - @override - String passwordValidationRuleMinCharacters(Object charNum) { - return 'is $charNum characters long at least'; - } - - @override - String get passwordsDoNotMatch => 'Passwords do not match'; - - @override - String passwordValidationError(Object matchesCriteria) { - return 'Check that your password $matchesCriteria'; - } - - @override - String notificationError(Object error) { - return 'Error occurred: $error'; - } - - @override - String loginUserNotFound(Object account) { - return 'Account $account has not been registered in the system'; - } - - @override - String get loginPasswordIncorrect => - 'Authorization failed, please check your password'; - - @override - String internalErrorOccurred(Object error) { - return 'An internal server error occurred: $error, we already know about it and working hard to fix it'; - } - - @override - String get noErrorInformation => - 'Some error occurred, but we have not error information. We are already investigating the issue'; - - @override - String get yourName => 'Your name'; - - @override - String get nameHint => 'John Doe'; - - @override - String get errorPageNotFoundTitle => 'Page Not Found'; - - @override - String get errorPageNotFoundMessage => 'Oops! We couldn\'t find that page.'; - - @override - String get errorPageNotFoundHint => - 'The page you\'re looking for doesn\'t exist or has been moved. Please check the URL or return to the home page.'; - - @override - String get errorUnknown => 'Unknown error occurred'; - - @override - String get unknown => 'unknown'; - - @override - String get goToLogin => 'Go to Login'; - - @override - String get goBack => 'Go Back'; - - @override - String get goToMainPage => 'Go to Main Page'; - - @override - String get goToSignUp => 'Go to Sign Up'; - - @override - String signupError(Object error) { - return 'Failed to signup: $error'; - } - - @override - String signupSuccess(Object email) { - return 'Email confirmation message has been sent to $email. Please, open it and click link to activate your account.'; - } - - @override - String connectivityError(Object serverAddress) { - return 'Cannot reach the server at $serverAddress. Check your network and try again.'; - } - - @override - String get errorAccountExists => 'Account already exists'; - - @override - String get errorAccountNotVerified => - 'Your account hasn\'t been verified yet. Please check your email to complete the verification'; - - @override - String get errorLoginUnauthorized => - 'Login or password is incorrect. Please try again'; - - @override - String get errorInternalError => - 'An internal error occurred. We\'re aware of the issue and working to resolve it. Please try again later'; - - @override - String get errorVerificationTokenNotFound => - 'Account for verification not found. Sign up again'; - - @override - String get created => 'Created'; - - @override - String get edited => 'Edited'; - - @override - String get errorDataConflict => - 'We can’t process your data because it has conflicting or contradictory information.'; - - @override - String get errorAccessDenied => - 'You do not have permission to access this resource. If you need access, please contact an administrator.'; - - @override - String get errorBrokenPayload => - 'The data you sent is invalid or incomplete. Please check your submission and try again.'; - - @override - String get errorInvalidArgument => - 'One or more arguments are invalid. Verify your input and try again.'; - - @override - String get errorBrokenReference => - 'The resource you\'re trying to access could not be referenced. It may have been moved or deleted.'; - - @override - String get errorInvalidQueryParameter => - 'One or more query parameters are missing or incorrect. Check them and try again.'; - - @override - String get errorNotImplemented => - 'This feature is not yet available. Please try again later or contact support.'; - - @override - String get errorLicenseRequired => - 'A valid license is required to perform this action. Please contact your administrator.'; - - @override - String get errorNotFound => - 'We couldn\'t find the resource you requested. It may have been removed or is temporarily unavailable.'; - - @override - String get errorNameMissing => 'Please provide a name before continuing.'; - - @override - String get errorEmailMissing => - 'Please provide an email address before continuing.'; - - @override - String get errorPasswordMissing => - 'Please provide a password before continuing.'; - - @override - String get errorEmailNotRegistered => - 'We could not find an account associated with that email address.'; - - @override - String get errorDuplicateEmail => - 'This email address is already in use. Try another one or reset your password.'; - - @override - String get showDetailsAction => 'Show Details'; - - @override - String get errorLogin => 'Error logging in'; - - @override - String get errorCreatingInvitation => 'Failed to create invitaiton'; - - @override - String get footerCompanyName => 'Sibilla Solutions LTD'; - - @override - String get footerAddress => - '27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus'; - - @override - String get footerSupport => 'Support'; - - @override - String get footerEmail => 'Email TBD'; - - @override - String get footerPhoneLabel => 'Phone'; - - @override - String get footerPhone => '+357 22 000 253'; - - @override - String get footerTermsOfService => 'Terms of Service'; - - @override - String get footerPrivacyPolicy => 'Privacy Policy'; - - @override - String get footerCookiePolicy => 'Cookie Policy'; - - @override - String get navigationLogout => 'Logout'; - - @override - String get dashboard => 'Dashboard'; - - @override - String get navigationUsersSettings => 'Users'; - - @override - String get navigationRolesSettings => 'Roles'; - - @override - String get navigationPermissionsSettings => 'Permissions'; - - @override - String get usersManagement => 'User Management'; - - @override - String get navigationOrganizationSettings => 'Organization settings'; - - @override - String get navigationAccountSettings => 'Profile settings'; - - @override - String get twoFactorPrompt => 'Enter the 6-digit code we sent to your device'; - - @override - String get twoFactorResend => 'Didn’t receive a code? Resend'; - - @override - String get twoFactorTitle => 'Two-Factor Authentication'; - - @override - String get twoFactorError => 'Invalid code. Please try again.'; - - @override - String get payoutNavDashboard => 'Dashboard'; - - @override - String get payoutNavSendPayout => 'Send payout'; - - @override - String get payoutNavRecipients => 'Recipients'; - - @override - String get payoutNavReports => 'Reports'; - - @override - String get payoutNavSettings => 'Settings'; - - @override - String get payoutNavLogout => 'Logout'; - - @override - String get payoutNavMethods => 'Payouts'; - - @override - String get expand => 'Expand'; - - @override - String get collapse => 'Collapse'; - - @override - String get pageTitleRecipients => 'Recipient address book'; - - @override - String get actionAddNew => 'Add new'; - - @override - String get colDataOwner => 'Data owner'; - - @override - String get colAvatar => 'Avatar'; - - @override - String get colName => 'Name'; - - @override - String get colEmail => 'Email'; - - @override - String get colStatus => 'Status'; - - @override - String get statusReady => 'Ready'; - - @override - String get statusRegistered => 'Registered'; - - @override - String get statusNotRegistered => 'Not registered'; - - @override - String get typeInternal => 'Managed by me'; - - @override - String get typeExternal => 'Self‑managed'; - - @override - String get searchHint => 'Search recipients'; - - @override - String get colActions => 'Actions'; - - @override - String get menuEdit => 'Edit'; - - @override - String get menuSendPayout => 'Send payout'; - - @override - String get tooltipRowActions => 'More actions'; - - @override - String get accountSettings => 'Account Settings'; - - @override - String get accountNameUpdateError => 'Failed to update account name'; - - @override - String get settingsSuccessfullyUpdated => 'Settings successfully updated'; - - @override - String get language => 'Language'; - - @override - String get failedToUpdateLanguage => 'Failed to update language'; - - @override - String get settingsImageUpdateError => 'Couldn\'t update the image'; - - @override - String get settingsImageTitle => 'Image'; - - @override - String get settingsImageHint => 'Tap to change the image'; - - @override - String get accountName => 'Name'; - - @override - String get accountNameHint => 'Specify your name'; - - @override - String get avatar => 'Profile photo'; - - @override - String get avatarHint => 'Tap to update'; - - @override - String get avatarUpdateError => 'Failed to update profile photo'; - - @override - String get settings => 'Settings'; - - @override - String get notSet => 'not set'; - - @override - String get search => 'Search...'; - - @override - String get ok => 'Ok'; - - @override - String get cancel => 'Cancel'; - - @override - String get confirm => 'Confirm'; - - @override - String get back => 'Back'; - - @override - String get operationfryTitle => 'Operation history'; - - @override - String get filters => 'Filters'; - - @override - String get period => 'Period'; - - @override - String get selectPeriod => 'Select period'; - - @override - String get apply => 'Apply'; - - @override - String status(String status) { - return '$status'; - } - - @override - String get operationStatusSuccessful => 'Successful'; - - @override - String get operationStatusPending => 'Pending'; - - @override - String get operationStatusUnsuccessful => 'Unsuccessful'; - - @override - String get statusColumn => 'Status'; - - @override - String get fileNameColumn => 'File name'; - - @override - String get amountColumn => 'Amount'; - - @override - String get toAmountColumn => 'To amount'; - - @override - String get payIdColumn => 'Pay ID'; - - @override - String get cardNumberColumn => 'Card number'; - - @override - String get nameColumn => 'Name'; - - @override - String get dateColumn => 'Date'; - - @override - String get commentColumn => 'Comment'; - - @override - String get paymentConfigTitle => 'Where to receive money'; - - @override - String get paymentConfigSubtitle => - 'Add multiple methods and choose your primary one.'; - - @override - String get addPaymentMethod => 'Add payment method'; - - @override - String get makeMain => 'Make primary'; - - @override - String get advanced => 'Advanced'; - - @override - String get fallbackExplanation => - 'If the primary method is unavailable, we will try the next enabled one in the list.'; - - @override - String get delete => 'Delete'; - - @override - String get deletePaymentConfirmation => - 'Are you sure you want to delete this payment method?'; - - @override - String get edit => 'Edit'; - - @override - String get moreActions => 'More actions'; - - @override - String get noPayouts => 'No Payouts'; - - @override - String get enterBankName => 'Enter bank name'; - - @override - String get paymentType => 'Payment Method Type'; - - @override - String get selectPaymentType => 'Please select a payment method type'; - - @override - String get paymentTypeCard => 'Credit Card'; - - @override - String get paymentTypeBankAccount => 'Russian Bank Account'; - - @override - String get paymentTypeIban => 'IBAN'; - - @override - String get paymentTypeWallet => 'Wallet'; - - @override - String get cardNumber => 'Card Number'; - - @override - String get enterCardNumber => 'Enter the card number'; - - @override - String get cardholderName => 'Cardholder Name'; - - @override - String get iban => 'IBAN'; - - @override - String get enterIban => 'Enter IBAN'; - - @override - String get bic => 'BIC'; - - @override - String get bankName => 'Bank Name'; - - @override - String get accountHolder => 'Account Holder'; - - @override - String get enterAccountHolder => 'Enter account holder'; - - @override - String get enterBic => 'Enter BIC'; - - @override - String get walletId => 'Wallet ID'; - - @override - String get enterWalletId => 'Enter wallet ID'; - - @override - String get recipients => 'Recipients'; - - @override - String get recipientName => 'Recipient Name'; - - @override - String get enterRecipientName => 'Enter recipient name'; - - @override - String get inn => 'INN'; - - @override - String get enterInn => 'Enter INN'; - - @override - String get kpp => 'KPP'; - - @override - String get enterKpp => 'Enter KPP'; - - @override - String get accountNumber => 'Account Number'; - - @override - String get enterAccountNumber => 'Enter account number'; - - @override - String get correspondentAccount => 'Correspondent Account'; - - @override - String get enterCorrespondentAccount => 'Enter correspondent account'; - - @override - String get bik => 'BIK'; - - @override - String get enterBik => 'Enter BIK'; - - @override - String get add => 'Add'; - - @override - String get expiryDate => 'Expiry (MM/YY)'; - - @override - String get firstName => 'First Name'; - - @override - String get enterFirstName => 'Enter First Name'; - - @override - String get lastName => 'Last Name'; - - @override - String get enterLastName => 'Enter Last Name'; - - @override - String get sendSingle => 'Send single transaction'; - - @override - String get sendMultiple => 'Send multiple transactions'; - - @override - String get addFunds => 'Add Funds'; - - @override - String get close => 'Close'; - - @override - String get multiplePayout => 'Multiple Payout'; - - @override - String get howItWorks => 'How it works?'; - - @override - String get exampleTitle => 'File Format & Sample'; - - @override - String get downloadSampleCSV => 'Download sample.csv'; - - @override - String get tokenColumn => 'Token (required)'; - - @override - String get currency => 'Currency'; - - @override - String get amount => 'Amount'; - - @override - String get comment => 'Comment'; - - @override - String get uploadCSV => 'Upload your CSV'; - - @override - String get upload => 'Upload'; - - @override - String get hintUpload => 'Supported format: .CSV · Max size 1 MB'; - - @override - String get uploadHistory => 'Upload History'; - - @override - String get payout => 'Payout'; - - @override - String get sendTo => 'Send Payout To'; - - @override - String get send => 'Send Payout'; - - @override - String get recipientPaysFee => 'Recipient pays the fee'; - - @override - String sentAmount(String amount) { - return 'Sent amount: \$$amount'; - } - - @override - String fee(String fee) { - return 'Fee: \$$fee'; - } - - @override - String recipientWillReceive(String amount) { - return 'Recipient will receive: \$$amount'; - } - - @override - String total(String total) { - return 'Total: \$$total'; - } - - @override - String get hideDetails => 'Hide Details'; - - @override - String get showDetails => 'Show Details'; - - @override - String get whereGetMoney => 'Source of funds for debit'; - - @override - String get details => 'Details'; - - @override - String get addRecipient => 'Add Recipient'; - - @override - String get editRecipient => 'Edit Recipient'; - - @override - String get saveRecipient => 'Save Recipient'; - - @override - String get choosePaymentMethod => 'Payment Methods (choose at least 1)'; - - @override - String get recipientFormRule => - 'Recipient must have at least one payment method'; - - @override - String get allStatus => 'All'; - - @override - String get readyStatus => 'Ready'; - - @override - String get registeredStatus => 'Registered'; - - @override - String get notRegisteredStatus => 'Not registered'; - - @override - String get noRecipientSelected => 'No recipient selected'; - - @override - String get companyName => 'Name of your company'; - - @override - String get companynameRequired => 'Company name required'; - - @override - String get errorSignUp => 'Error occured while signing up, try again later'; - - @override - String get companyDescription => 'Company Description'; - - @override - String get companyDescriptionHint => - 'Describe any of the fields of the Company\'s business'; - - @override - String get optional => 'optional'; -} diff --git a/frontend/pweb/lib/generated/i18n/app_localizations_ru.dart b/frontend/pweb/lib/generated/i18n/app_localizations_ru.dart deleted file mode 100644 index caf30fe..0000000 --- a/frontend/pweb/lib/generated/i18n/app_localizations_ru.dart +++ /dev/null @@ -1,782 +0,0 @@ -// ignore: unused_import -import 'package:intl/intl.dart' as intl; -import 'app_localizations.dart'; - -// ignore_for_file: type=lint - -/// The translations for Russian (`ru`). -class AppLocalizationsRu extends AppLocalizations { - AppLocalizationsRu([String locale = 'ru']) : super(locale); - - @override - String get login => 'Войти'; - - @override - String get logout => 'Выйти'; - - @override - String get profile => 'Профиль'; - - @override - String get signup => 'Регистрация'; - - @override - String get username => 'Email'; - - @override - String get usernameHint => 'email@example.com'; - - @override - String get usernameErrorInvalid => - 'Укажите действительный адрес электронной почты'; - - @override - String usernameUnknownTLD(Object domain) { - return 'Домен .$domain неизвестен, пожалуйста, проверьте его'; - } - - @override - String get password => 'Пароль'; - - @override - String get confirmPassword => 'Подтвердите пароль'; - - @override - String get passwordValidationRuleDigit => 'содержит цифру'; - - @override - String get passwordValidationRuleUpperCase => 'содержит заглавную букву'; - - @override - String get passwordValidationRuleLowerCase => 'содержит строчную букву'; - - @override - String get passwordValidationRuleSpecialCharacter => - 'содержит специальный символ'; - - @override - String passwordValidationRuleMinCharacters(Object charNum) { - return 'длина не менее $charNum символов'; - } - - @override - String get passwordsDoNotMatch => 'Пароли не совпадают'; - - @override - String passwordValidationError(Object matchesCriteria) { - return 'Убедитесь, что ваш пароль $matchesCriteria'; - } - - @override - String notificationError(Object error) { - return 'Произошла ошибка: $error'; - } - - @override - String loginUserNotFound(Object account) { - return 'Аккаунт $account не зарегистрирован в системе'; - } - - @override - String get loginPasswordIncorrect => - 'Ошибка авторизации, пожалуйста, проверьте пароль'; - - @override - String internalErrorOccurred(Object error) { - return 'Произошла внутренняя ошибка сервера: $error, мы уже знаем о ней и усердно работаем над исправлением'; - } - - @override - String get noErrorInformation => - 'Произошла ошибка, но у нас нет информации о ней. Мы уже расследуем этот вопрос'; - - @override - String get yourName => 'Ваше имя'; - - @override - String get nameHint => 'Иван Иванов'; - - @override - String get errorPageNotFoundTitle => 'Страница не найдена'; - - @override - String get errorPageNotFoundMessage => - 'Упс! Мы не смогли найти эту страницу.'; - - @override - String get errorPageNotFoundHint => - 'Запрашиваемая страница не существует или была перемещена. Пожалуйста, проверьте URL или вернитесь на главную страницу.'; - - @override - String get errorUnknown => 'Произошла неизвестная ошибка'; - - @override - String get unknown => 'неизвестно'; - - @override - String get goToLogin => 'Перейти к входу'; - - @override - String get goBack => 'Назад'; - - @override - String get goToMainPage => 'На главную'; - - @override - String get goToSignUp => 'Перейти к регистрации'; - - @override - String signupError(Object error) { - return 'Не удалось зарегистрироваться: $error'; - } - - @override - String signupSuccess(Object email) { - return 'Письмо с подтверждением email отправлено на $email. Пожалуйста, откройте его и перейдите по ссылке для активации вашего аккаунта.'; - } - - @override - String connectivityError(Object serverAddress) { - return 'Не удается связаться с сервером $serverAddress. Проверьте ваше интернет-соединение и попробуйте снова.'; - } - - @override - String get errorAccountExists => 'Account already exists'; - - @override - String get errorAccountNotVerified => - 'Ваш аккаунт еще не подтвержден. Пожалуйста, проверьте вашу электронную почту для завершения верификации'; - - @override - String get errorLoginUnauthorized => - 'Неверный логин или пароль. Пожалуйста, попробуйте снова'; - - @override - String get errorInternalError => - 'Произошла внутренняя ошибка. Мы в курсе проблемы и работаем над ее решением. Пожалуйста, попробуйте позже'; - - @override - String get errorVerificationTokenNotFound => - 'Аккаунт для верификации не найден. Зарегистрируйтесь снова'; - - @override - String get created => 'Создано'; - - @override - String get edited => 'Изменено'; - - @override - String get errorDataConflict => - 'Мы не можем обработать ваши данные, так как они содержат конфликтующую или противоречивую информацию.'; - - @override - String get errorAccessDenied => - 'У вас нет разрешения на доступ к этому ресурсу. Если вам нужен доступ, пожалуйста, обратитесь к администратору.'; - - @override - String get errorBrokenPayload => - 'Отправленные данные недействительны или неполны. Пожалуйста, проверьте введенные данные и попробуйте снова.'; - - @override - String get errorInvalidArgument => - 'Один или несколько аргументов недействительны. Проверьте введенные данные и попробуйте снова.'; - - @override - String get errorBrokenReference => - 'Ресурс, к которому вы пытаетесь получить доступ, не может быть найден. Возможно, он был перемещен или удален.'; - - @override - String get errorInvalidQueryParameter => - 'Один или несколько параметров запроса отсутствуют или указаны неверно. Проверьте их и попробуйте снова.'; - - @override - String get errorNotImplemented => - 'Эта функция еще недоступна. Пожалуйста, попробуйте позже или обратитесь в службу поддержки.'; - - @override - String get errorLicenseRequired => - 'Для выполнения этого действия требуется действующая лицензия. Пожалуйста, обратитесь к вашему администратору.'; - - @override - String get errorNotFound => - 'Мы не смогли найти запрошенный ресурс. Возможно, он был удален или временно недоступен.'; - - @override - String get errorNameMissing => 'Пожалуйста, укажите имя для продолжения.'; - - @override - String get errorEmailMissing => - 'Пожалуйста, укажите адрес электронной почты для продолжения.'; - - @override - String get errorPasswordMissing => - 'Пожалуйста, укажите пароль для продолжения.'; - - @override - String get errorEmailNotRegistered => - 'Мы не нашли аккаунт, связанный с этим адресом электронной почты.'; - - @override - String get errorDuplicateEmail => - 'Этот адрес электронной почты уже используется. Попробуйте другой или восстановите пароль.'; - - @override - String get showDetailsAction => 'Показать детали'; - - @override - String get errorLogin => 'Ошибка входа'; - - @override - String get errorCreatingInvitation => 'Не удалось создать приглашение'; - - @override - String get footerCompanyName => 'Sibilla Solutions LTD'; - - @override - String get footerAddress => - '27, Pindarou Street, Alpha Business Centre, Block B 7th Floor, 1060 Nicosia, Cyprus'; - - @override - String get footerSupport => 'Поддержка'; - - @override - String get footerEmail => 'Email TBD'; - - @override - String get footerPhoneLabel => 'Телефон'; - - @override - String get footerPhone => '+357 22 000 253'; - - @override - String get footerTermsOfService => 'Условия обслуживания'; - - @override - String get footerPrivacyPolicy => 'Политика конфиденциальности'; - - @override - String get footerCookiePolicy => 'Политика использования файлов cookie'; - - @override - String get navigationLogout => 'Выйти'; - - @override - String get dashboard => 'Дашборд'; - - @override - String get navigationUsersSettings => 'Пользователи'; - - @override - String get navigationRolesSettings => 'Роли'; - - @override - String get navigationPermissionsSettings => 'Разрешения'; - - @override - String get usersManagement => 'Управление пользователями'; - - @override - String get navigationOrganizationSettings => 'Настройки организации'; - - @override - String get navigationAccountSettings => 'Настройки профиля'; - - @override - String get twoFactorPrompt => - 'Введите 6-значный код, отправленный на ваше устройство'; - - @override - String get twoFactorResend => 'Не получили код? Отправить снова'; - - @override - String get twoFactorTitle => 'Двухфакторная аутентификация'; - - @override - String get twoFactorError => 'Неверный код. Пожалуйста, попробуйте снова.'; - - @override - String get payoutNavDashboard => 'Дашборд'; - - @override - String get payoutNavSendPayout => 'Отправить выплату'; - - @override - String get payoutNavRecipients => 'Получатели'; - - @override - String get payoutNavReports => 'Отчеты'; - - @override - String get payoutNavSettings => 'Настройки'; - - @override - String get payoutNavLogout => 'Выйти'; - - @override - String get payoutNavMethods => 'Выплаты'; - - @override - String get expand => 'Развернуть'; - - @override - String get collapse => 'Свернуть'; - - @override - String get pageTitleRecipients => 'Адресная книга получателей'; - - @override - String get actionAddNew => 'Добавить'; - - @override - String get colDataOwner => 'Владелец данных'; - - @override - String get colAvatar => 'Аватар'; - - @override - String get colName => 'Имя'; - - @override - String get colEmail => 'Email'; - - @override - String get colStatus => 'Статус'; - - @override - String get statusReady => 'Готов'; - - @override - String get statusRegistered => 'Зарегистрирован'; - - @override - String get statusNotRegistered => 'Не зарегистрирован'; - - @override - String get typeInternal => 'Управляется мной'; - - @override - String get typeExternal => 'Самоуправляемый'; - - @override - String get searchHint => 'Поиск получателей'; - - @override - String get colActions => 'Действия'; - - @override - String get menuEdit => 'Редактировать'; - - @override - String get menuSendPayout => 'Отправить выплату'; - - @override - String get tooltipRowActions => 'Другие действия'; - - @override - String get accountSettings => 'Настройки аккаунта'; - - @override - String get accountNameUpdateError => 'Не удалось обновить имя аккаунта'; - - @override - String get settingsSuccessfullyUpdated => 'Настройки успешно обновлены'; - - @override - String get language => 'Язык'; - - @override - String get failedToUpdateLanguage => 'Не удалось обновить язык'; - - @override - String get settingsImageUpdateError => 'Не удалось обновить изображение'; - - @override - String get settingsImageTitle => 'Изображение'; - - @override - String get settingsImageHint => 'Нажмите, чтобы изменить изображение'; - - @override - String get accountName => 'Имя'; - - @override - String get accountNameHint => 'Укажите ваше имя'; - - @override - String get avatar => 'Фото профиля'; - - @override - String get avatarHint => 'Нажмите для обновления'; - - @override - String get avatarUpdateError => 'Не удалось обновить фото профиля'; - - @override - String get settings => 'Настройки'; - - @override - String get notSet => 'не задано'; - - @override - String get search => 'Поиск...'; - - @override - String get ok => 'Ок'; - - @override - String get cancel => 'Отмена'; - - @override - String get confirm => 'Подтвердить'; - - @override - String get back => 'Назад'; - - @override - String get operationfryTitle => 'История операций'; - - @override - String get filters => 'Фильтры'; - - @override - String get period => 'Период'; - - @override - String get selectPeriod => 'Выберите период'; - - @override - String get apply => 'Применить'; - - @override - String status(String status) { - return '$status'; - } - - @override - String get operationStatusSuccessful => 'Успешно'; - - @override - String get operationStatusPending => 'В ожидании'; - - @override - String get operationStatusUnsuccessful => 'Неуспешно'; - - @override - String get statusColumn => 'Статус'; - - @override - String get fileNameColumn => 'Имя файла'; - - @override - String get amountColumn => 'Сумма'; - - @override - String get toAmountColumn => 'На сумму'; - - @override - String get payIdColumn => 'Pay ID'; - - @override - String get cardNumberColumn => 'Номер карты'; - - @override - String get nameColumn => 'Имя'; - - @override - String get dateColumn => 'Дата'; - - @override - String get commentColumn => 'Комментарий'; - - @override - String get paymentConfigTitle => 'Куда получать деньги'; - - @override - String get paymentConfigSubtitle => - 'Добавьте несколько методов и выберите основной.'; - - @override - String get addPaymentMethod => 'Добавить способ оплаты'; - - @override - String get makeMain => 'Сделать основным'; - - @override - String get advanced => 'Дополнительно'; - - @override - String get fallbackExplanation => - 'Если основной метод недоступен, мы попробуем следующий включенный метод в списке.'; - - @override - String get delete => 'Удалить'; - - @override - String get deletePaymentConfirmation => - 'Вы уверены, что хотите удалить этот способ оплаты?'; - - @override - String get edit => 'Редактировать'; - - @override - String get moreActions => 'Еще действия'; - - @override - String get noPayouts => 'Нет выплат'; - - @override - String get enterBankName => 'Введите название банка'; - - @override - String get paymentType => 'Тип способа оплаты'; - - @override - String get selectPaymentType => 'Пожалуйста, выберите тип способа оплаты'; - - @override - String get paymentTypeCard => 'Кредитная карта'; - - @override - String get paymentTypeBankAccount => 'Российский банковский счет'; - - @override - String get paymentTypeIban => 'IBAN'; - - @override - String get paymentTypeWallet => 'Кошелек'; - - @override - String get cardNumber => 'Номер карты'; - - @override - String get enterCardNumber => 'Введите номер карты'; - - @override - String get cardholderName => 'Имя держателя карты'; - - @override - String get iban => 'IBAN'; - - @override - String get enterIban => 'Введите IBAN'; - - @override - String get bic => 'BIC'; - - @override - String get bankName => 'Название банка'; - - @override - String get accountHolder => 'Владелец счета'; - - @override - String get enterAccountHolder => 'Введите владельца счета'; - - @override - String get enterBic => 'Введите BIC'; - - @override - String get walletId => 'ID кошелька'; - - @override - String get enterWalletId => 'Введите ID кошелька'; - - @override - String get recipients => 'Получатели'; - - @override - String get recipientName => 'Имя получателя'; - - @override - String get enterRecipientName => 'Введите имя получателя'; - - @override - String get inn => 'ИНН'; - - @override - String get enterInn => 'Введите ИНН'; - - @override - String get kpp => 'КПП'; - - @override - String get enterKpp => 'Введите КПП'; - - @override - String get accountNumber => 'Номер счета'; - - @override - String get enterAccountNumber => 'Введите номер счета'; - - @override - String get correspondentAccount => 'Корреспондентский счет'; - - @override - String get enterCorrespondentAccount => 'Введите корреспондентский счет'; - - @override - String get bik => 'БИК'; - - @override - String get enterBik => 'Введите БИК'; - - @override - String get add => 'Добавить'; - - @override - String get expiryDate => 'Срок действия (ММ/ГГ)'; - - @override - String get firstName => 'Имя'; - - @override - String get enterFirstName => 'Введите имя'; - - @override - String get lastName => 'Фамилия'; - - @override - String get enterLastName => 'Введите фамилию'; - - @override - String get sendSingle => 'Отправить одну транзакцию'; - - @override - String get sendMultiple => 'Отправить несколько транзакций'; - - @override - String get addFunds => 'Пополнить счет'; - - @override - String get close => 'Закрыть'; - - @override - String get multiplePayout => 'Множественная выплата'; - - @override - String get howItWorks => 'Как это работает?'; - - @override - String get exampleTitle => 'Формат файла и образец'; - - @override - String get downloadSampleCSV => 'Скачать sample.csv'; - - @override - String get tokenColumn => 'Токен (обязательно)'; - - @override - String get currency => 'Валюта'; - - @override - String get amount => 'Сумма'; - - @override - String get comment => 'Комментарий'; - - @override - String get uploadCSV => 'Загрузите ваш CSV'; - - @override - String get upload => 'Загрузить'; - - @override - String get hintUpload => 'Поддерживаемый формат: .CSV · Макс. размер 1 МБ'; - - @override - String get uploadHistory => 'История загрузок'; - - @override - String get payout => 'Выплата'; - - @override - String get sendTo => 'Отправить выплату'; - - @override - String get send => 'Отправить выплату'; - - @override - String get recipientPaysFee => 'Получатель оплачивает комиссию'; - - @override - String sentAmount(String amount) { - return 'Отправленная сумма: \$$amount'; - } - - @override - String fee(String fee) { - return 'Комиссия: \$$fee'; - } - - @override - String recipientWillReceive(String amount) { - return 'Получатель получит: \$$amount'; - } - - @override - String total(String total) { - return 'Итого: \$$total'; - } - - @override - String get hideDetails => 'Скрыть детали'; - - @override - String get showDetails => 'Показать детали'; - - @override - String get whereGetMoney => 'Источник средств для списания'; - - @override - String get details => 'Детали'; - - @override - String get addRecipient => 'Добавить получателя'; - - @override - String get editRecipient => 'Редактировать получателя'; - - @override - String get saveRecipient => 'Сохранить получателя'; - - @override - String get choosePaymentMethod => 'Способы оплаты (выберите хотя бы 1)'; - - @override - String get recipientFormRule => - 'Получатель должен иметь хотя бы один способ оплаты'; - - @override - String get allStatus => 'Все'; - - @override - String get readyStatus => 'Готов'; - - @override - String get registeredStatus => 'Зарегистрирован'; - - @override - String get notRegisteredStatus => 'Не зарегистрирован'; - - @override - String get noRecipientSelected => 'Получатель не выбран'; - - @override - String get companyName => 'Name of your company'; - - @override - String get companynameRequired => 'Company name required'; - - @override - String get errorSignUp => 'Error occured while signing up, try again later'; - - @override - String get companyDescription => 'Company Description'; - - @override - String get companyDescriptionHint => - 'Describe any of the fields of the Company\'s business'; - - @override - String get optional => 'optional'; -} diff --git a/frontend/pweb/lib/l10n/en.arb b/frontend/pweb/lib/l10n/en.arb index b152d02..b5d53d1 100644 --- a/frontend/pweb/lib/l10n/en.arb +++ b/frontend/pweb/lib/l10n/en.arb @@ -431,5 +431,7 @@ "errorSignUp": "Error occured while signing up, try again later", "companyDescription": "Company Description", "companyDescriptionHint": "Describe any of the fields of the Company's business", - "optional": "optional" + "optional": "optional", + "ownerRole": "Organization Owner", + "ownerRoleDescription": "This role is granted to the organization’s creator, providing full administrative privileges" } \ No newline at end of file diff --git a/frontend/pweb/lib/l10n/ru.arb b/frontend/pweb/lib/l10n/ru.arb index def1290..5303005 100644 --- a/frontend/pweb/lib/l10n/ru.arb +++ b/frontend/pweb/lib/l10n/ru.arb @@ -422,5 +422,8 @@ "registeredStatus": "Зарегистрирован", "notRegisteredStatus": "Не зарегистрирован", - "noRecipientSelected": "Получатель не выбран" + "noRecipientSelected": "Получатель не выбран", + + "ownerRole": "Владелец организации", + "ownerRoleDescription": "Эта роль предоставляется создателю организации и даёт ему полные административные права" } \ No newline at end of file diff --git a/frontend/pweb/lib/main.dart b/frontend/pweb/lib/main.dart index 0ee7f60..3eaca66 100644 --- a/frontend/pweb/lib/main.dart +++ b/frontend/pweb/lib/main.dart @@ -11,7 +11,6 @@ import 'package:pshared/config/constants.dart'; import 'package:pshared/provider/account.dart'; import 'package:pshared/provider/locale.dart'; import 'package:pshared/provider/organizations.dart'; -import 'package:pshared/provider/pfe/provider.dart'; import 'package:pweb/app/app.dart'; import 'package:pweb/app/timeago.dart'; @@ -66,7 +65,7 @@ void main() async { ChangeNotifierProvider(create: (_) => LocaleProvider(null)), ChangeNotifierProvider(create: (_) => AccountProvider()), ChangeNotifierProvider(create: (_) => OrganizationsProvider()), - ChangeNotifierProvider(create: (_) => PfeProvider()), + ChangeNotifierProvider(create: (_) => AccountProvider()), ChangeNotifierProvider(create: (_) => CarouselIndexProvider()), ChangeNotifierProvider( diff --git a/frontend/pweb/lib/pages/login/form.dart b/frontend/pweb/lib/pages/login/form.dart index e221ea6..a145cb6 100644 --- a/frontend/pweb/lib/pages/login/form.dart +++ b/frontend/pweb/lib/pages/login/form.dart @@ -2,7 +2,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:pshared/provider/pfe/provider.dart'; +import 'package:pshared/provider/account.dart'; +import 'package:pshared/provider/locale.dart'; import 'package:pweb/app/router/pages.dart'; import 'package:pweb/pages/login/buttons.dart'; @@ -34,17 +35,19 @@ class _LoginFormState extends State { final ValueNotifier _isPasswordAcceptable = ValueNotifier(false); Future _login(BuildContext context, VoidCallback onLogin, void Function(Object e) onError) async { - final pfeProvider = Provider.of(context, listen: false); + final provider = Provider.of(context, listen: false); try { - // final account = await pfeProvider.login( - // email: _usernameController.text, - // password: _passwordController.text, - // ); + //final account = + await provider.login( + email: _usernameController.text, + password: _passwordController.text, + locale: context.read().locale.languageCode, + ); onLogin(); return 'ok'; } catch (e) { - onError(pfeProvider.error == null ? e : pfeProvider.error!); + onError(provider.error ?? e); } return null; } diff --git a/frontend/pweb/lib/pages/signup/form/state.dart b/frontend/pweb/lib/pages/signup/form/state.dart index a25ec84..38f651d 100644 --- a/frontend/pweb/lib/pages/signup/form/state.dart +++ b/frontend/pweb/lib/pages/signup/form/state.dart @@ -1,10 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:flutter_timezone/flutter_timezone.dart'; + import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; -import 'package:pshared/provider/pfe/provider.dart'; +import 'package:pshared/api/requests/login_data.dart'; +import 'package:pshared/models/describable.dart'; +import 'package:pshared/provider/account.dart'; +import 'package:pshared/provider/locale.dart'; import 'package:pweb/app/router/pages.dart'; import 'package:pweb/pages/signup/form/content.dart'; @@ -31,7 +36,7 @@ class SignUpFormState extends State { VoidCallback onSignUp, void Function(Object e) onError, ) async { - final pfeProvider = Provider.of(context, listen: false); + final provider = Provider.of(context, listen: false); setState(() { _autoValidateMode = true; @@ -42,20 +47,34 @@ class SignUpFormState extends State { } try { - // final account = await pfeProvider.signUp( - // companyName: controllers.companyName.text.trim(), - // description: controllers.description.text.trim().isEmpty - // ? null - // : controllers.description.text.trim(), - // firstName: controllers.firstName.text.trim(), - // lastName: controllers.lastName.text.trim(), - // email: controllers.email.text.trim(), - // password: controllers.password.text, - // ); + final orgDescription = controllers.description.text.trim(); + final locs = AppLocalizations.of(context)!; + final locale = context.read().locale; + final timezone = await FlutterTimezone.getLocalTimezone(locale.toString()); + await provider.signup( + account: AccountData.build( + login: LoginData.build( + login: controllers.email.text.trim(), + password: controllers.password.text, + locale: locale.toLanguageTag(), + ), + name: controllers.password.text, + lastName: controllers.lastName.text.trim(), + ), + organization: newDescribable( + name: controllers.companyName.text.trim(), + description: orgDescription.isEmpty ? null : orgDescription, + ), + timezone: timezone.identifier, + ownerRole: newDescribable( + name: locs.ownerRole, + description: locs.ownerRoleDescription, + ), + ); onSignUp(); return 'ok'; } catch (e) { - onError(pfeProvider.error ?? e); + onError(provider.error ?? e); } return null; } diff --git a/version b/version index ac346d4..b621109 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.0.856 \ No newline at end of file +2.0.857 \ No newline at end of file