diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index e63d74323..0b5b75529 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,7 +1,7 @@ name: Main on: push: - branches: ['master', 'main'] + branches: ['master', 'main', 'epic/*'] jobs: deploy: name: Deploy @@ -16,40 +16,41 @@ jobs: with: registry-username: ${{ github.actor }} registry-access-token: ${{ secrets.GITHUB_TOKEN }} - - name: set config - run: | - printf "$APP_ENV_FILE" > dist/apps/control-center/browser/assets/appConfig.json - printf "$AUTH_ENV_FILE" > dist/apps/control-center/browser/assets/authConfig.json - env: - APP_ENV_FILE: ${{secrets.APP_CONF}} - AUTH_ENV_FILE: ${{secrets.AUTH_CONF}} - - name: Publish to CF Pages - uses: cloudflare/pages-action@1 - with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - projectName: 'cc' - directory: 'dist/apps/control-center/browser' - gitHubToken: ${{ secrets.GITHUB_TOKEN }} - branch: main - - name: Publish MatEz Library - uses: valitydev/action-frontend/publish@v1.0 - with: - npm-token: ${{ secrets.NPM_TOKEN }} - directory: ./dist/libs/matez - - name: Publish NgThrift Library - uses: valitydev/action-frontend/publish@v1.0 - with: - npm-token: ${{ secrets.NPM_TOKEN }} - directory: ./dist/libs/ng-thrift + # - name: set config + # run: | + # printf "$APP_ENV_FILE" > dist/apps/control-center/browser/assets/appConfig.json + # printf "$AUTH_ENV_FILE" > dist/apps/control-center/browser/assets/authConfig.json + # env: + # APP_ENV_FILE: ${{secrets.APP_CONF}} + # AUTH_ENV_FILE: ${{secrets.AUTH_CONF}} + # - name: Publish to CF Pages + # uses: cloudflare/pages-action@1 + # with: + # apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + # accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + # projectName: 'cc' + # directory: 'dist/apps/control-center/browser' + # gitHubToken: ${{ secrets.GITHUB_TOKEN }} + # branch: main + # - name: Publish MatEz Library + # uses: valitydev/action-frontend/publish@v1.0 + # with: + # npm-token: ${{ secrets.NPM_TOKEN }} + # directory: ./dist/libs/matez + # - name: Publish NgThrift Library + # uses: valitydev/action-frontend/publish@v1.0 + # with: + # npm-token: ${{ secrets.NPM_TOKEN }} + # directory: ./dist/libs/ng-thrift notify: name: Notify runs-on: ubuntu-latest needs: [deploy] steps: - uses: actions/checkout@v4 - - uses: valitydev/action-mattermost-notify@v0.1.4 + - uses: valitydev/action-mattermost-notify@v0 with: webhook: ${{ secrets.MATTERMOST_WEBHOOK_URL }} - channel: 'control-center-improvements' + channel: 'control-center-new' username: 'Frontend' + branch: 'epic/new-domain' diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index b766bed49..ddf16d64d 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -1,7 +1,6 @@ name: PR on: pull_request: - branches: ['*'] jobs: check: name: Check @@ -21,23 +20,23 @@ jobs: - uses: valitydev/action-frontend/setup-install@v2 - name: Build run: npm run build - - name: Publish MatEz Library - if: > - contains(github.event.pull_request.labels.*.name, 'publish') - || contains(github.event.pull_request.labels.*.name, 'publish matez') - working-directory: ./dist/libs/matez - run: > - npm version prerelease --preid pr-${{ github.event.number }}-${{ env.SHORT_SHA }} --no-git-tag-version - && npm publish --tag pr - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Publish NgThrift Library - if: > - contains(github.event.pull_request.labels.*.name, 'publish') - || contains(github.event.pull_request.labels.*.name, 'publish ng-thrift') - working-directory: ./dist/libs/ng-thrift - run: > - npm version prerelease --preid pr-${{ github.event.number }}-${{ env.SHORT_SHA }} --no-git-tag-version - && npm publish --tag pr - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + # - name: Publish MatEz Library + # if: > + # contains(github.event.pull_request.labels.*.name, 'publish') + # || contains(github.event.pull_request.labels.*.name, 'publish matez') + # working-directory: ./dist/libs/matez + # run: > + # npm version prerelease --preid pr-${{ github.event.number }}-${{ env.SHORT_SHA }} --no-git-tag-version + # && npm publish --tag pr + # env: + # NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + # - name: Publish NgThrift Library + # if: > + # contains(github.event.pull_request.labels.*.name, 'publish') + # || contains(github.event.pull_request.labels.*.name, 'publish ng-thrift') + # working-directory: ./dist/libs/ng-thrift + # run: > + # npm version prerelease --preid pr-${{ github.event.number }}-${{ env.SHORT_SHA }} --no-git-tag-version + # && npm publish --tag pr + # env: + # NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.postcssrc.json b/.postcssrc.json new file mode 100644 index 000000000..fddc8af8f --- /dev/null +++ b/.postcssrc.json @@ -0,0 +1,5 @@ +{ + "plugins": { + "@tailwindcss/postcss": {} + } +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 9d45f8589..9dc63d4ed 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,13 @@ { - "typescript.preferences.importModuleSpecifier": "relative" + "typescript.preferences.importModuleSpecifier": "relative", + "angular-schematics.schematicsDefaultOptions": { + "angular-*": { + "externalTemplate": true, + "style": "scss" + }, + "angular-component": { + "skipDisplayBlock": true + } + }, + "tailwindCSS.experimental.configFile": "apps/control-center/src/app/styles/styles.scss" } \ No newline at end of file diff --git a/apps/control-center/project.json b/apps/control-center/project.json index 954c5139b..e02547125 100644 --- a/apps/control-center/project.json +++ b/apps/control-center/project.json @@ -37,8 +37,8 @@ "budgets": [ { "type": "initial", - "maximumWarning": "13mb", - "maximumError": "13mb" + "maximumWarning": "10mb", + "maximumError": "11mb" }, { "type": "anyComponentStyle", diff --git a/apps/control-center/src/app/api/accounter/accounter.service.ts b/apps/control-center/src/app/api/accounter/accounter.service.ts deleted file mode 100644 index 41b41400b..000000000 --- a/apps/control-center/src/app/api/accounter/accounter.service.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - accounter_Accounter, - accounter_AccounterCodegenClient, -} from '@vality/domain-proto'; -import { Account } from '@vality/domain-proto/internal/accounter'; -import { AccountID } from '@vality/domain-proto/internal/domain'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class AccounterService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('Accounter')), - ); - const metadata$ = from( - import('@vality/domain-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - accounter_Accounter({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - GetAccountByID(id: AccountID): Observable { - return this.client$.pipe(switchMap((c) => c.GetAccountByID(id))); - } -} diff --git a/apps/control-center/src/app/api/accounter/index.ts b/apps/control-center/src/app/api/accounter/index.ts deleted file mode 100644 index 6fee97715..000000000 --- a/apps/control-center/src/app/api/accounter/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './accounter.service'; diff --git a/apps/control-center/src/app/api/claim-management/claim-management.service.ts b/apps/control-center/src/app/api/claim-management/claim-management.service.ts deleted file mode 100644 index 83d251397..000000000 --- a/apps/control-center/src/app/api/claim-management/claim-management.service.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - claim_management_ClaimManagement, - claim_management_ClaimManagementCodegenClient, -} from '@vality/domain-proto'; -import { - Claim, - ClaimID, - ClaimRevision, - ClaimSearchQuery, - ClaimSearchResponse, - ModificationChange, - ModificationChangeset, - ModificationID, -} from '@vality/domain-proto/claim_management'; -import { PartyID } from '@vality/domain-proto/domain'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class ClaimManagementService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('ClaimManagement')), - ); - const metadata$ = from( - import('@vality/domain-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - claim_management_ClaimManagement({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - CreateClaim(partyId: PartyID, changeset: ModificationChangeset): Observable { - return this.client$.pipe(switchMap((c) => c.CreateClaim(partyId, changeset))); - } - - GetClaim(partyId: PartyID, id: ClaimID): Observable { - return this.client$.pipe(switchMap((c) => c.GetClaim(partyId, id))); - } - - SearchClaims(claimRequest: ClaimSearchQuery): Observable { - return this.client$.pipe(switchMap((c) => c.SearchClaims(claimRequest))); - } - - AcceptClaim(partyId: PartyID, id: ClaimID, revision: ClaimRevision): Observable { - return this.client$.pipe(switchMap((c) => c.AcceptClaim(partyId, id, revision))); - } - - UpdateClaim( - partyId: PartyID, - id: ClaimID, - revision: ClaimRevision, - changeset: ModificationChangeset, - ): Observable { - return this.client$.pipe(switchMap((c) => c.UpdateClaim(partyId, id, revision, changeset))); - } - - UpdateModification( - partyId: PartyID, - id: ClaimID, - revision: ClaimRevision, - modificationId: ModificationID, - modificationChange: ModificationChange, - ): Observable { - return this.client$.pipe( - switchMap((c) => - c.UpdateModification(partyId, id, revision, modificationId, modificationChange), - ), - ); - } - - RemoveModification( - partyId: PartyID, - id: ClaimID, - revision: ClaimRevision, - modificationId: ModificationID, - ): Observable { - return this.client$.pipe( - switchMap((c) => c.RemoveModification(partyId, id, revision, modificationId)), - ); - } - - RequestClaimReview(partyId: PartyID, id: ClaimID, revision: ClaimRevision): Observable { - return this.client$.pipe(switchMap((c) => c.RequestClaimReview(partyId, id, revision))); - } - - RequestClaimChanges(partyId: PartyID, id: ClaimID, revision: ClaimRevision): Observable { - return this.client$.pipe(switchMap((c) => c.RequestClaimChanges(partyId, id, revision))); - } - - DenyClaim( - partyId: PartyID, - id: ClaimID, - revision: ClaimRevision, - reason: string, - ): Observable { - return this.client$.pipe(switchMap((c) => c.DenyClaim(partyId, id, revision, reason))); - } - - RevokeClaim( - partyId: PartyID, - id: ClaimID, - revision: ClaimRevision, - reason: string, - ): Observable { - return this.client$.pipe(switchMap((c) => c.RevokeClaim(partyId, id, revision, reason))); - } -} diff --git a/apps/control-center/src/app/api/claim-management/index.ts b/apps/control-center/src/app/api/claim-management/index.ts deleted file mode 100644 index b06c0b411..000000000 --- a/apps/control-center/src/app/api/claim-management/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './claim-management.service'; -export * from './types'; diff --git a/apps/control-center/src/app/api/claim-management/types/claim-status.ts b/apps/control-center/src/app/api/claim-management/types/claim-status.ts deleted file mode 100644 index 1f2a1f077..000000000 --- a/apps/control-center/src/app/api/claim-management/types/claim-status.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ClaimStatus } from '@vality/domain-proto/claim_management'; - -import { enumerate } from '../../../../utils/enumerate'; - -export const CLAIM_STATUSES = enumerate()( - 'pending', - 'review', - 'pending_acceptance', - 'accepted', - 'denied', - 'revoked', -); diff --git a/apps/control-center/src/app/api/claim-management/types/index.ts b/apps/control-center/src/app/api/claim-management/types/index.ts deleted file mode 100644 index 1b9f1b4e8..000000000 --- a/apps/control-center/src/app/api/claim-management/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './claim-status'; diff --git a/apps/control-center/src/app/api/deposit/deposit-management.service.ts b/apps/control-center/src/app/api/deposit/deposit-management.service.ts deleted file mode 100644 index 7f7b49c69..000000000 --- a/apps/control-center/src/app/api/deposit/deposit-management.service.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - deposit_Management, - deposit_ManagementCodegenClient, -} from '@vality/fistful-proto'; -import { DepositID, DepositParams } from '@vality/fistful-proto/deposit'; -import { AdjustmentParams, AdjustmentState } from '@vality/fistful-proto/deposit_adjustment'; -import { RevertParams, RevertState } from '@vality/fistful-proto/deposit_revert'; -import { ContextSet } from '@vality/fistful-proto/internal/context'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class DepositManagementService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('DepositManagement')), - ); - const metadata$ = from( - import('@vality/fistful-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - deposit_Management({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - CreateAdjustment(id: DepositID, params: AdjustmentParams): Observable { - return this.client$.pipe(switchMap((c) => c.CreateAdjustment(id, params))); - } - - CreateRevert(id: DepositID, params: RevertParams): Observable { - return this.client$.pipe(switchMap((c) => c.CreateRevert(id, params))); - } - - Create(params: DepositParams, context: ContextSet) { - return this.client$.pipe(switchMap((c) => c.Create(params, context))); - } -} diff --git a/apps/control-center/src/app/api/deposit/index.ts b/apps/control-center/src/app/api/deposit/index.ts deleted file mode 100644 index 92406bd7b..000000000 --- a/apps/control-center/src/app/api/deposit/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './deposit-management.service'; diff --git a/apps/control-center/src/app/api/domain-config/author-management.service.ts b/apps/control-center/src/app/api/domain-config/author-management.service.ts deleted file mode 100644 index 78d41e792..000000000 --- a/apps/control-center/src/app/api/domain-config/author-management.service.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - domain_config_v2_AuthorManagementCodegenClient as CodegenClient, - ThriftAstMetadata, - domain_config_v2_AuthorManagement, -} from '@vality/domain-proto'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class AuthorManagementService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('DMTAuthor')), - ); - const metadata$ = from( - import('@vality/domain-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - domain_config_v2_AuthorManagement({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - Create(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.Create(...params))); - } - - Delete(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.Delete(...params))); - } - - Get(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.Get(...params))); - } - - GetByEmail(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.GetByEmail(...params))); - } -} diff --git a/apps/control-center/src/app/api/domain-config/index.ts b/apps/control-center/src/app/api/domain-config/index.ts index fa524014a..64170df2d 100644 --- a/apps/control-center/src/app/api/domain-config/index.ts +++ b/apps/control-center/src/app/api/domain-config/index.ts @@ -1,6 +1,3 @@ export * from './stores'; export * from './services'; -export * from './types'; -export * from './repository.service'; -export * from './repository2.service'; -export * from './author-management.service'; +export * from './utils/get-domain-object-reference'; diff --git a/apps/control-center/src/app/api/domain-config/repository-client.service.ts b/apps/control-center/src/app/api/domain-config/repository-client.service.ts deleted file mode 100644 index 443480e33..000000000 --- a/apps/control-center/src/app/api/domain-config/repository-client.service.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - domain_config_v2_RepositoryClientCodegenClient as CodegenClient, - ThriftAstMetadata, - domain_config_v2_RepositoryClient, -} from '@vality/domain-proto'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class RepositoryClientService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('DMTClient')), - ); - const metadata$ = from( - import('@vality/domain-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - domain_config_v2_RepositoryClient({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - CheckoutObject(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.CheckoutObject(...params))); - } -} diff --git a/apps/control-center/src/app/api/domain-config/repository.service.ts b/apps/control-center/src/app/api/domain-config/repository.service.ts deleted file mode 100644 index d8b0b311e..000000000 --- a/apps/control-center/src/app/api/domain-config/repository.service.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - domain_config_Repository, - domain_config_RepositoryCodegenClient, -} from '@vality/domain-proto'; -import { Commit, Reference, Snapshot, Version } from '@vality/domain-proto/domain_config'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class RepositoryService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe(map(toWachterHeaders('Domain'))); - const metadata$ = from( - import('@vality/domain-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - domain_config_Repository({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - Commit(version: Version, commit: Commit): Observable { - return this.client$.pipe(switchMap((c) => c.Commit(version, commit))); - } - - Checkout(reference: Reference): Observable { - return this.client$.pipe(switchMap((c) => c.Checkout(reference))); - } -} diff --git a/apps/control-center/src/app/api/domain-config/repository2.service.ts b/apps/control-center/src/app/api/domain-config/repository2.service.ts deleted file mode 100644 index 437441c77..000000000 --- a/apps/control-center/src/app/api/domain-config/repository2.service.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - domain_config_v2_RepositoryCodegenClient as CodegenClient, - ThriftAstMetadata, - domain_config_v2_Repository, -} from '@vality/domain-proto'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class Repository2Service { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe(map(toWachterHeaders('DMT'))); - const metadata$ = from( - import('@vality/domain-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - domain_config_v2_Repository({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - GetLatestVersion(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.GetLatestVersion(...params))); - } - - Commit(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.Commit(...params))); - } - - GetObjectHistory(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.GetObjectHistory(...params))); - } - - GetAllObjectsHistory(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.GetAllObjectsHistory(...params))); - } - - SearchObjects(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.SearchObjects(...params))); - } - - SearchFullObjects(...params: Parameters) { - return this.client$.pipe(switchMap((c) => c.SearchFullObjects(...params))); - } -} diff --git a/apps/control-center/src/app/api/domain-config/services/domain-secret-service/consts/secrets-role.ts b/apps/control-center/src/app/api/domain-config/services/domain-secret-service/consts/secrets-role.ts deleted file mode 100644 index ec7d927d2..000000000 --- a/apps/control-center/src/app/api/domain-config/services/domain-secret-service/consts/secrets-role.ts +++ /dev/null @@ -1 +0,0 @@ -export const SECRETS_ROLE = 'dominant:secrets'; diff --git a/apps/control-center/src/app/api/domain-config/services/domain-secret-service/domain-secret.service.ts b/apps/control-center/src/app/api/domain-config/services/domain-secret-service/domain-secret.service.ts deleted file mode 100644 index 35dea37bc..000000000 --- a/apps/control-center/src/app/api/domain-config/services/domain-secret-service/domain-secret.service.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { Domain, DomainObject } from '@vality/domain-proto/domain'; - -import { AppAuthGuardService } from '../../../../shared/services'; - -import { SECRETS_ROLE } from './consts/secrets-role'; -import { reduceObject } from './utils/reduce-object'; -import { restoreObject } from './utils/restore-object'; - -@Injectable({ - providedIn: 'root', -}) -export class DomainSecretService { - private appAuthGuardService = inject(AppAuthGuardService); - private hasDominantSecretRole = this.appAuthGuardService.userHasRoles([SECRETS_ROLE]); - - reduceObject(obj: DomainObject): DomainObject { - if (this.hasDominantSecretRole) { - return obj; - } - return reduceObject(obj); - } - - reduceDomain(domain: Domain): Domain { - if (this.hasDominantSecretRole) { - return domain; - } - const resDomain: Domain = new Map(); - for (const [name, value] of domain) { - resDomain.set(name, reduceObject(value)); - } - return resDomain; - } - - restoreDomain(srcObj: DomainObject, newObj: DomainObject): DomainObject { - if (this.hasDominantSecretRole) { - return newObj; - } - return restoreObject(srcObj, newObj); - } -} diff --git a/apps/control-center/src/app/api/domain-config/services/domain-secret-service/index.ts b/apps/control-center/src/app/api/domain-config/services/domain-secret-service/index.ts deleted file mode 100644 index 77ad76b49..000000000 --- a/apps/control-center/src/app/api/domain-config/services/domain-secret-service/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './domain-secret.service'; diff --git a/apps/control-center/src/app/api/domain-config/services/domain-secret-service/utils/reduce-object.ts b/apps/control-center/src/app/api/domain-config/services/domain-secret-service/utils/reduce-object.ts deleted file mode 100644 index 9657ef7f0..000000000 --- a/apps/control-center/src/app/api/domain-config/services/domain-secret-service/utils/reduce-object.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { DomainObject } from '@vality/domain-proto/domain'; -import { getUnionKey } from '@vality/ng-thrift'; -import cloneDeep from 'lodash-es/cloneDeep'; -import isNil from 'lodash-es/isNil'; - -export function reduceObject(obj: DomainObject): DomainObject { - const name = getUnionKey(obj); - switch (name) { - case 'proxy': - case 'terminal': { - if (isNil(obj[name].data.options)) { - return obj; - } - const result = cloneDeep(obj); - delete result[name].data.options; - return result; - } - case 'provider': { - if (isNil(obj[name].data.proxy.additional)) { - return obj; - } - const result = cloneDeep(obj); - delete result[name].data.proxy.additional; - return result; - } - default: - return obj; - } -} diff --git a/apps/control-center/src/app/api/domain-config/services/domain-secret-service/utils/restore-object.ts b/apps/control-center/src/app/api/domain-config/services/domain-secret-service/utils/restore-object.ts deleted file mode 100644 index fbf234c41..000000000 --- a/apps/control-center/src/app/api/domain-config/services/domain-secret-service/utils/restore-object.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { DomainObject } from '@vality/domain-proto/domain'; -import { getUnionKey } from '@vality/ng-thrift'; -import { cloneDeep, isNil } from 'lodash-es'; - -export function restoreObject(srcObj: DomainObject, newObj: DomainObject): DomainObject { - const name = getUnionKey(newObj); - switch (name) { - case 'proxy': - case 'terminal': { - if (isNil(srcObj[name].data.options)) { - return newObj; - } - const resObj = cloneDeep(newObj); - resObj[name].data.options = cloneDeep(srcObj[name].data.options); - return resObj; - } - case 'provider': { - if (isNil(srcObj[name].data.proxy.additional)) { - return newObj; - } - const resObj = cloneDeep(newObj); - resObj[name].data.proxy.additional = cloneDeep(srcObj[name].data.proxy.additional); - return resObj; - } - default: - return newObj; - } -} diff --git a/apps/control-center/src/app/api/domain-config/services/domain.service.ts b/apps/control-center/src/app/api/domain-config/services/domain.service.ts index e8d9c35d9..f55854742 100644 --- a/apps/control-center/src/app/api/domain-config/services/domain.service.ts +++ b/apps/control-center/src/app/api/domain-config/services/domain.service.ts @@ -1,75 +1,107 @@ import { Injectable, inject } from '@angular/core'; -import { rxResource } from '@angular/core/rxjs-interop'; -import { DomainObject, Reference, ReflessDomainObject } from '@vality/domain-proto/domain'; -import { Operation, Version } from '@vality/domain-proto/domain_config_v2'; -import { NotifyLogService, switchCombineWith } from '@vality/matez'; -import { getUnionKey } from '@vality/ng-thrift'; -import { EMPTY, catchError, map, tap } from 'rxjs'; +import { Reference } from '@vality/domain-proto/domain'; +import { + CommitResponse, + Operation, + Repository, + RepositoryClient, + Version, + VersionedObject, +} from '@vality/domain-proto/domain_config_v2'; +import { NotifyLogService, observableResource, switchCombineWith } from '@vality/matez'; +import { getUnionKey, isEqualThrift } from '@vality/ng-thrift'; +import { Observable, catchError, combineLatest, iif, map, of, switchMap, tap } from 'rxjs'; -import { RepositoryClientService } from '../repository-client.service'; -import { Repository2Service } from '../repository2.service'; import { AuthorStoreService } from '../stores/author-store.service'; +import { getDomainObjectReference } from '../utils/get-domain-object-reference'; -import { DomainSecretService } from './domain-secret-service'; +export class DomainServiceObsoleteCommitVersionError { + constructor( + public error: unknown, + public newObject: VersionedObject, + ) {} +} @Injectable({ providedIn: 'root', }) export class DomainService { - private repositoryService = inject(Repository2Service); + private repositoryService = inject(Repository); private authorStoreService = inject(AuthorStoreService); private log = inject(NotifyLogService); - private repositoryClientService = inject(RepositoryClientService); - private domainSecretService = inject(DomainSecretService); - version = rxResource({ - stream: () => this.repositoryService.GetLatestVersion(), + private repositoryClientService = inject(RepositoryClient); + + version = observableResource({ + loader: () => this.repositoryService.GetLatestVersion(), }); - get(ref: Reference, version?: Version) { + get(ref: Reference, version?: Version): Observable; + get(refs: Reference[], version?: Version): Observable; + get(refs: Reference | Reference[], version?: Version) { return this.repositoryClientService - .CheckoutObject(version ? { version } : { head: {} }, ref) - .pipe( - switchCombineWith((obj) => [this.domainSecretService.reduceObject(obj.object)]), - map(([{ info }, object]) => ({ info, object })), - ); + .CheckoutObjects( + version ? { version } : { head: {} }, + Array.isArray(refs) ? refs : [refs], + ) + .pipe(map((objs) => (Array.isArray(refs) ? objs : objs[0]))); } - insert(objs: ReflessDomainObject[], attempts = 1) { - return this.commit(objs.map((obj) => ({ insert: { object: obj } }))).pipe( + commit(ops: Operation[], version?: Version, attempts = 1): Observable { + return combineLatest([ + iif(() => !!version, of(version), this.version.getFirstValue()), + this.authorStoreService.author.getFirstValue(), + ]).pipe( + switchMap(([ver, author]) => this.repositoryService.Commit(ver, ops, author.id)), catchError((err) => { - if (err?.name === 'ObsoleteCommitVersion') { - if (attempts !== 0) { - this.version.reload(); - this.insert(objs, attempts - 1); + if (err?.error?.name === 'ObsoleteCommitVersion') { + if ( + attempts !== 0 && + // If no updates or only one update operation + (ops.every((o) => getUnionKey(o) !== 'update') || ops.length === 1) + ) { this.log.error(err, `Domain config is out of date, one more attempt...`); - return EMPTY; - } else { - this.log.error(err, `Domain config is out of date, please try again`); - } - } - throw err; - }), - tap((res) => { - this.version.set(res.version); - }), - ); - } - - update(objs: DomainObject[], version: Version, attempts = 1) { - return this.commit( - objs.map((obj) => ({ update: { object: obj } })), - version, - ).pipe( - catchError((err) => { - if (err?.name === 'ObsoleteCommitVersion') { - if (attempts !== 0) { this.version.reload(); - this.update(objs, version, attempts - 1); - this.log.error(err, `Domain config is out of date, one more attempt...`); - return EMPTY; + if (ops.every((o) => getUnionKey(o) !== 'update')) + return this.commit(ops, undefined, attempts - 1); + // If one update operation + return this.version.getFirstValue().pipe( + switchCombineWith(() => [ + this.get(ops.map((o) => getDomainObjectReference(o.update.object))), + ]), + switchMap(([ver, obj]) => { + if (isEqualThrift(obj[0].object, ops[0].update.object)) + return this.commit(ops, ver, attempts - 1); + this.log.error( + err, + `Domain config is out of date, please try again`, + ); + throw new DomainServiceObsoleteCommitVersionError(err, obj[0]); + }), + ); } else { this.log.error(err, `Domain config is out of date, please try again`); } + } else { + const types = Array.from(new Set(ops.map((o) => getUnionKey(o)))); + this.log.error( + err, + `Error ${types + .map((t) => { + switch (t) { + case 'insert': + return 'inserting'; + case 'update': + return 'updating'; + case 'remove': + return 'removing'; + default: + return 'operating'; + } + }) + .join( + ', ', + )} ${ops.length > 1 ? `(${ops.length}) domain objects` : 'domain object'}`, + ); } throw err; }), @@ -78,36 +110,4 @@ export class DomainService { }), ); } - - remove(refs: Reference[]) { - return this.commit(refs.map((ref) => ({ remove: { ref } }))).pipe( - tap((res) => { - this.version.set(res.version); - }), - ); - } - - private commit(ops: Operation[], version: Version = this.version.value()) { - return this.repositoryService - .Commit(version, ops, this.authorStoreService.author.value().id) - .pipe( - catchError((err) => { - const types = ops.map((o) => getUnionKey(o)); - const operationType = types.every((t) => t === types[0]) ? types[0] : 'update'; - this.log.errorOperation( - err, - operationType === 'update' - ? 'update' - : operationType === 'insert' - ? 'create' - : 'delete', - types.length > 1 ? 'domain objects' : 'domain object', - ); - throw err; - }), - tap((res) => { - this.version.set(res.version); - }), - ); - } } diff --git a/apps/control-center/src/app/api/domain-config/services/fetch-domain-objects.service.ts b/apps/control-center/src/app/api/domain-config/services/fetch-domain-objects.service.ts index 0e428b9fc..f348e4bce 100644 --- a/apps/control-center/src/app/api/domain-config/services/fetch-domain-objects.service.ts +++ b/apps/control-center/src/app/api/domain-config/services/fetch-domain-objects.service.ts @@ -1,10 +1,12 @@ import { Injectable, inject } from '@angular/core'; -import { LimitedVersionedObject, SearchRequestParams } from '@vality/domain-proto/domain_config_v2'; +import { + LimitedVersionedObject, + Repository, + SearchRequestParams, +} from '@vality/domain-proto/domain_config_v2'; import { FetchOptions, FetchSuperclass, NotifyLogService, clean } from '@vality/matez'; import { catchError, map, of } from 'rxjs'; -import { Repository2Service } from '../repository2.service'; - type FetchParams = Partial>; @Injectable() @@ -12,7 +14,7 @@ export class FetchDomainObjectsService extends FetchSuperclass< LimitedVersionedObject, FetchParams > { - private repositoryService = inject(Repository2Service); + private repositoryService = inject(Repository); private log = inject(NotifyLogService); fetch(params: FetchParams, options: FetchOptions) { diff --git a/apps/control-center/src/app/api/domain-config/services/fetch-full-domain-objects.service.ts b/apps/control-center/src/app/api/domain-config/services/fetch-full-domain-objects.service.ts new file mode 100644 index 000000000..a35cfe173 --- /dev/null +++ b/apps/control-center/src/app/api/domain-config/services/fetch-full-domain-objects.service.ts @@ -0,0 +1,38 @@ +import { Injectable, inject } from '@angular/core'; +import { + Repository, + SearchRequestParams, + VersionedObject, +} from '@vality/domain-proto/domain_config_v2'; +import { FetchOptions, FetchSuperclass, NotifyLogService, clean } from '@vality/matez'; +import { catchError, map, of } from 'rxjs'; + +type FetchParams = Partial>; + +@Injectable() +export class FetchFullDomainObjectsService extends FetchSuperclass { + private repositoryService = inject(Repository); + private log = inject(NotifyLogService); + + fetch(params: FetchParams, options: FetchOptions) { + return this.repositoryService + .SearchFullObjects( + clean({ + ...params, + query: params.query || '*', + limit: options.size, + continuation_token: options.continuationToken, + }), + ) + .pipe( + map((res) => ({ + result: res.result, + continuationToken: res.continuation_token, + })), + catchError((err) => { + this.log.errorOperation(err, 'receive', 'domain objects'); + return of({ result: [] }); + }), + ); + } +} diff --git a/apps/control-center/src/app/api/domain-config/services/index.ts b/apps/control-center/src/app/api/domain-config/services/index.ts index 082513990..c87161bae 100644 --- a/apps/control-center/src/app/api/domain-config/services/index.ts +++ b/apps/control-center/src/app/api/domain-config/services/index.ts @@ -1,3 +1,3 @@ -export * from './domain-secret-service'; export * from './domain.service'; export * from './fetch-domain-objects.service'; +export * from './fetch-full-domain-objects.service'; diff --git a/apps/control-center/src/app/api/domain-config/stores/author-store.service.ts b/apps/control-center/src/app/api/domain-config/stores/author-store.service.ts index 9c54fdbcd..9d3dea3ef 100644 --- a/apps/control-center/src/app/api/domain-config/stores/author-store.service.ts +++ b/apps/control-center/src/app/api/domain-config/stores/author-store.service.ts @@ -1,33 +1,32 @@ import { Injectable, inject } from '@angular/core'; -import { rxResource } from '@angular/core/rxjs-interop'; -import { Author } from '@vality/domain-proto/domain_config_v2'; -import { NotifyLogService } from '@vality/matez'; -import { catchError, of } from 'rxjs'; +import { AuthorManagement } from '@vality/domain-proto/domain_config_v2'; +import { NotifyLogService, observableResource } from '@vality/matez'; +import { catchError } from 'rxjs'; -import { KeycloakUserService } from '../../../shared/services'; -import { AuthorManagementService } from '../author-management.service'; +import { KeycloakUserService } from '../../../shared/services/app-auth-guard/keycloak-user.service'; @Injectable({ providedIn: 'root', }) export class AuthorStoreService { - private authorManagementService = inject(AuthorManagementService); + private authorManagementService = inject(AuthorManagement); private keycloakUserService = inject(KeycloakUserService); private log = inject(NotifyLogService); - author = rxResource({ - params: () => this.keycloakUserService.user.value(), - stream: ({ params }) => - this.authorManagementService.GetByEmail(params.email).pipe( + + author = observableResource({ + params: this.keycloakUserService.user.value$, + loader: (user) => + this.authorManagementService.GetByEmail(user.email).pipe( catchError(() => this.authorManagementService .Create({ - email: params.email, - name: params.username, + email: user.email, + name: user.username, }) .pipe( catchError((err) => { this.log.errorOperation(err, 'create', 'author'); - return of({ id: '', email: '', name: '' }); + throw err; }), ), ), diff --git a/apps/control-center/src/app/api/domain-config/stores/currencies-store.service.ts b/apps/control-center/src/app/api/domain-config/stores/currencies-store.service.ts index 869d3d761..7184ecdb8 100644 --- a/apps/control-center/src/app/api/domain-config/stores/currencies-store.service.ts +++ b/apps/control-center/src/app/api/domain-config/stores/currencies-store.service.ts @@ -1,27 +1,24 @@ -import { Injectable, computed, inject } from '@angular/core'; -import { rxResource } from '@angular/core/rxjs-interop'; +import { Injectable, inject } from '@angular/core'; import { DomainObjectType } from '@vality/domain-proto/domain'; -import { VersionedObject } from '@vality/domain-proto/domain_config_v2'; -import { NotifyLogService } from '@vality/matez'; -import { Observable, catchError, map, of, retry, switchMap } from 'rxjs'; - -import { environment } from '../../../../environments/environment'; -import { Repository2Service } from '../repository2.service'; - -import { DomainStoreService } from './domain-store.service'; +import { Repository, VersionedObject } from '@vality/domain-proto/domain_config_v2'; +import { NotifyLogService, fetchAll, observableResource } from '@vality/matez'; +import { catchError, map, of } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CurrenciesStoreService { - private repositoryService = inject(Repository2Service); + private repositoryService = inject(Repository); private log = inject(NotifyLogService); - private domainStoreService = inject(DomainStoreService); - currencies = computed(() => this.currencyObjects.value().map((v) => v.object.currency.data)); - isLoading = computed(() => this.currencyObjects.isLoading()); - private currencyObjects = rxResource({ - defaultValue: [], - stream: () => - this.getAllCurrencies().pipe( + resource = observableResource({ + loader: () => + fetchAll((continuationToken) => + this.repositoryService.SearchFullObjects({ + type: DomainObjectType.currency, + query: '*', + limit: 1_000_000, + continuation_token: continuationToken, + }), + ).pipe( catchError((err) => { this.log.errorOperation(err, 'receive', 'currencies'); return of([]); @@ -29,43 +26,9 @@ export class CurrenciesStoreService { ), }); - private getAllCurrencies(continuationToken = undefined): Observable { - return environment.domain2 - ? this.repositoryService - .SearchFullObjects({ - type: 2 satisfies DomainObjectType.currency, - query: '*', - limit: 1_000_000, - continuation_token: continuationToken, - }) - .pipe( - retry(2), - switchMap((resp) => { - if (resp.continuation_token) { - return this.getAllCurrencies(resp.continuation_token).pipe( - map((nextCurrencies) => [...resp.result, ...nextCurrencies]), - ); - } - return of(resp.result); - }), - ) - : this.domainStoreService.getObjects('currency').pipe( - map((currencies) => - currencies - ? currencies.map((c) => ({ - info: { - version: 0, - changed_at: '', - changed_by: { - id: '', - email: '', - name: '', - }, - }, - object: { currency: c }, - })) - : [], - ), - ); - } + currencies$ = this.resource.value$.pipe( + map((objs) => objs.map((obj) => obj.object.currency.data)), + ); + + isLoading$ = this.resource.isLoading$; } diff --git a/apps/control-center/src/app/api/domain-config/stores/domain-objects-store.service.ts b/apps/control-center/src/app/api/domain-config/stores/domain-objects-store.service.ts index 593fc30f2..3ec08eb32 100644 --- a/apps/control-center/src/app/api/domain-config/stores/domain-objects-store.service.ts +++ b/apps/control-center/src/app/api/domain-config/stores/domain-objects-store.service.ts @@ -1,70 +1,111 @@ -import { Injectable, computed, inject, signal } from '@angular/core'; -import { rxResource, toObservable } from '@angular/core/rxjs-interop'; -import { Reference } from '@vality/domain-proto/domain'; -import { LimitedVersionedObject } from '@vality/domain-proto/domain_config_v2'; -import { getUnionKey } from '@vality/ng-thrift'; -import { Observable, combineLatest, first, map, mergeScan, of, retry, switchMap } from 'rxjs'; +import { Injectable, Injector, inject, runInInjectionContext } from '@angular/core'; +import { DomainObjectType, Reference } from '@vality/domain-proto/domain'; +import { + LimitedVersionedObject, + Repository, + VersionedObject, +} from '@vality/domain-proto/domain_config_v2'; +import { ObservableResource, fetchAll, observableResource } from '@vality/matez'; +import { getUnionKey, getUnionValue } from '@vality/ng-thrift'; +import { map } from 'rxjs'; -import { Repository2Service } from '../repository2.service'; -import { DOMAIN_OBJECT_TYPE$ } from '../types'; import { createObjectHash } from '../utils/create-object-hash'; -import { createObjectsHashMap } from '../utils/create-objects-hash-map'; @Injectable({ providedIn: 'root' }) export class DomainObjectsStoreService { - private repositoryService = inject(Repository2Service); - private types = signal(new Set()); - private objects = rxResource({ - stream: () => - toObservable(this.types).pipe( - mergeScan((objects, types) => { - const newTypes = Array.from(types).filter((t) => !objects.has(t)); - if (!newTypes.length) return of(objects); - return combineLatest(newTypes.map((t) => this.getAllObjectByType(t))).pipe( - map((newObjects) => { - newObjects.forEach((newObjectsByType, idx) => { - objects.set( - newTypes[idx], - createObjectsHashMap(newObjectsByType, (obj) => obj.ref), - ); - }); - return objects; - }), - ); - }, new Map>()), - ), - }); + private repositoryService = inject(Repository); + private injector = inject(Injector); + + private limitedObjects = new Map< + keyof Reference, + ObservableResource> + >(); + + private objects = new Map>>(); + + getLimitedObjects(type: keyof Reference) { + return this.getLimitedObjectsByType(type).map((objects) => Array.from(objects.values())); + } + + getLimitedObject(ref: Reference) { + const type = getUnionKey(ref); + return this.getLimitedObjectsByType(type).map((objects) => + objects.get(createObjectHash(ref)), + ); + } + + getObjects(type: keyof Reference) { + return this.getObjectsByType(type).map((objects) => Array.from(objects.values())); + } getObject(ref: Reference) { const type = getUnionKey(ref); - this.types.update((types) => types.add(type)); - return computed(() => this.objects.value().get(type).get(createObjectHash(ref))); + return this.getObjectsByType(type).map((objects) => objects.get(createObjectHash(ref))); } - private getAllObjectByType( - type: keyof Reference, - continuationToken = undefined, - ): Observable { - return DOMAIN_OBJECT_TYPE$.pipe( - first(), - switchMap((types) => - this.repositoryService.SearchObjects({ - type: types[type], - query: '*', - limit: 1_000_000, - continuation_token: continuationToken, - }), - ), - ).pipe( - retry(2), - switchMap((resp) => { - if (resp.continuation_token) { - return this.getAllObjectByType(type, resp.continuation_token).pipe( - map((nextObject) => [...resp.result, ...nextObject]), - ); - } - return of(resp.result); - }), - ); + private getLimitedObjectsByType(type: keyof Reference) { + if (!this.limitedObjects.has(type)) + this.limitedObjects.set( + type, + runInInjectionContext(this.injector, () => + observableResource({ + loader: (_, objects) => + fetchAll((continuationToken) => + this.repositoryService.SearchObjects({ + type: DomainObjectType[type], + query: '*', + limit: 1_000_000, + continuation_token: continuationToken, + }), + ).pipe( + map((result) => { + objects.clear(); + result.forEach((obj) => { + objects.set(createObjectHash(obj.ref), obj); + }); + return objects; + }), + ), + seed: new Map(), + }), + ), + ); + return this.limitedObjects.get(type); + } + + private getObjectsByType(type: keyof Reference) { + if (!this.objects.has(type)) + this.objects.set( + type, + runInInjectionContext(this.injector, () => + observableResource({ + loader: (_, objects) => + fetchAll((continuationToken) => + this.repositoryService.SearchFullObjects({ + type: DomainObjectType[type], + query: '*', + limit: 1_000_000, + continuation_token: continuationToken, + }), + ).pipe( + map((result) => { + objects.clear(); + result.forEach((obj) => { + objects.set( + createObjectHash({ + [getUnionKey(obj.object)]: getUnionValue(obj.object) + .ref, + }), + obj, + ); + }); + return objects; + }), + ), + seed: new Map(), + }), + ), + ); + return this.objects.get(type); } } diff --git a/apps/control-center/src/app/api/domain-config/stores/domain-store.service.ts b/apps/control-center/src/app/api/domain-config/stores/domain-store.service.ts deleted file mode 100644 index 8a6dafb79..000000000 --- a/apps/control-center/src/app/api/domain-config/stores/domain-store.service.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { DestroyRef, Injectable, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { DomainObject, Reference } from '@vality/domain-proto/domain'; -import { Commit, Snapshot, Version } from '@vality/domain-proto/domain_config'; -import { NotifyLogService, handleError, inProgressFrom, progressTo } from '@vality/matez'; -import { getUnionKey } from '@vality/ng-thrift'; -import { BehaviorSubject, Observable, ReplaySubject, combineLatest, defer, filter, of } from 'rxjs'; -import { map, shareReplay, startWith, switchMap, take, tap } from 'rxjs/operators'; - -import { RepositoryService } from '../repository.service'; -import { DomainSecretService } from '../services'; -import { createObjectHash } from '../utils/create-object-hash'; - -/** - * @deprecated use DomainObjectsStoreService instead - */ -@Injectable({ - providedIn: 'root', -}) -export class DomainStoreService { - private repositoryService = inject(RepositoryService); - private domainSecretService = inject(DomainSecretService); - private log = inject(NotifyLogService); - private destroyRef = inject(DestroyRef); - /** - * @deprecated - */ - domain$ = defer(() => this.rawDomain$).pipe( - map((d) => this.domainSecretService.reduceDomain(d)), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - /** - * @deprecated - */ - version$ = defer(() => this.loadedSnapshot$).pipe( - map(([s]) => s.version), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - /** - * @deprecated - */ - isLoading$ = inProgressFrom( - () => this.progress$, - defer(() => this.snapshot$), - ); - - private snapshot$: Observable = defer(() => this.reload$).pipe( - startWith(undefined), - switchMap(() => - this.repositoryService - .Checkout({ head: {} }) - .pipe(progressTo(this.progress$), handleError(this.log.error)), - ), - takeUntilDestroyed(this.destroyRef), - shareReplay(1), - ); - private loadedSnapshot$ = combineLatest([ - defer(() => this.snapshot$), - defer(() => this.progress$), - ]).pipe( - filter(([, p]) => !p), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - private reload$ = new ReplaySubject(1); - private progress$ = new BehaviorSubject(0); - private rawDomain$ = this.loadedSnapshot$.pipe( - map(([s]) => s?.domain), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - private objects$ = combineLatest([this.rawDomain$, this.domain$]).pipe( - map( - ([rawDomain, domain]) => - new Map( - Array.from(rawDomain).map(([ref, raw]) => [ - createObjectHash(ref), - { raw, reduced: domain.get(ref) }, - ]), - ), - ), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - - /** - * @deprecated - */ - forceReload(): void { - this.reload$.next(); - } - - /** - * @deprecated - */ - getObject(ref: Reference, raw = false): Observable { - return this.objects$.pipe( - map((objects) => objects.get(createObjectHash(ref))?.[raw ? 'raw' : 'reduced']), - ); - } - - /** - * @deprecated - */ - getObjects(objectType: T): Observable { - return this.getObjectsRefs(objectType).pipe(map((d) => d.map(([, o]) => o[objectType]))); - } - - /** - * @deprecated - */ - getObjectsRefs( - objectType: T, - ): Observable<[Reference, DomainObject][]> { - return this.domain$.pipe( - map((d) => Array.from(d).filter(([, o]) => getUnionKey(o) === objectType)), - ); - } - - /** - * @deprecated - */ - commit(commit: Commit, version?: Version | number, reload = true) { - const version$ = version ? of(version) : this.version$.pipe(take(1)); - return version$.pipe( - switchMap((v) => this.repositoryService.Commit(v, commit)), - tap(() => { - if (reload) { - this.forceReload(); - } - }), - ); - } -} diff --git a/apps/control-center/src/app/api/domain-config/stores/index.ts b/apps/control-center/src/app/api/domain-config/stores/index.ts index ed22338c9..30a2be7ad 100644 --- a/apps/control-center/src/app/api/domain-config/stores/index.ts +++ b/apps/control-center/src/app/api/domain-config/stores/index.ts @@ -1,2 +1,4 @@ -export * from './domain-store.service'; export * from './currencies-store.service'; +export * from './routing-rules-store.service'; +export * from './payment-institutions-store.service'; +export * from './domain-objects-store.service'; diff --git a/apps/control-center/src/app/api/domain-config/stores/payment-institutions-store.service.ts b/apps/control-center/src/app/api/domain-config/stores/payment-institutions-store.service.ts new file mode 100644 index 000000000..a984613bf --- /dev/null +++ b/apps/control-center/src/app/api/domain-config/stores/payment-institutions-store.service.ts @@ -0,0 +1,38 @@ +import { Injectable, inject } from '@angular/core'; +import { DomainObjectType } from '@vality/domain-proto/domain'; +import { Repository, VersionedObject } from '@vality/domain-proto/domain_config_v2'; +import { NotifyLogService, fetchAll, observableResource } from '@vality/matez'; +import { catchError, map, of, shareReplay } from 'rxjs'; + +@Injectable({ providedIn: 'root' }) +export class PaymentInstitutionsStoreService { + private repositoryService = inject(Repository); + private log = inject(NotifyLogService); + + resource = observableResource({ + loader: () => + fetchAll((continuationToken) => + this.repositoryService.SearchFullObjects({ + type: DomainObjectType.payment_institution, + query: '*', + limit: 1_000_000, + continuation_token: continuationToken, + }), + ).pipe( + catchError((err) => { + this.log.errorOperation(err, 'receive', 'payment institutions'); + return of([]); + }), + ), + }); + + paymentInstitutions$ = this.resource.value$.pipe( + map((objs) => objs.map((obj) => obj.object.payment_institution)), + shareReplay({ refCount: true, bufferSize: 1 }), + ); + isLoading$ = this.resource.isLoading$; + + reload() { + this.resource.reload(); + } +} diff --git a/apps/control-center/src/app/api/domain-config/stores/routing-rules-store.service.ts b/apps/control-center/src/app/api/domain-config/stores/routing-rules-store.service.ts new file mode 100644 index 000000000..00852c770 --- /dev/null +++ b/apps/control-center/src/app/api/domain-config/stores/routing-rules-store.service.ts @@ -0,0 +1,59 @@ +import { Injectable, inject } from '@angular/core'; +import { DomainObjectType, RoutingRulesetRef } from '@vality/domain-proto/domain'; +import { Operation, Repository, VersionedObject } from '@vality/domain-proto/domain_config_v2'; +import { NotifyLogService, fetchAll, observableResource } from '@vality/matez'; +import { catchError, first, map, of, shareReplay, switchMap } from 'rxjs'; + +import { DomainService } from '../services'; + +@Injectable({ providedIn: 'root' }) +export class RoutingRulesStoreService { + private repositoryService = inject(Repository); + private domainService = inject(DomainService); + private log = inject(NotifyLogService); + + resource = observableResource({ + loader: () => + fetchAll((continuationToken) => + this.repositoryService.SearchFullObjects({ + type: DomainObjectType.routing_rules, + query: '*', + limit: 1_000_000, + continuation_token: continuationToken, + }), + ).pipe( + catchError((err) => { + this.log.errorOperation(err, 'receive', 'routing rules'); + return of([]); + }), + ), + }); + + routingRules$ = this.resource.value$.pipe( + map((objs) => objs.map((obj) => obj.object.routing_rules)), + shareReplay({ refCount: true, bufferSize: 1 }), + ); + version$ = this.resource.value$.pipe( + map((objs) => Math.max(...objs.map((obj) => obj.info.version))), + shareReplay({ refCount: true, bufferSize: 1 }), + ); + isLoading$ = this.resource.isLoading$; + + reload() { + this.resource.reload(); + } + + get(ref: RoutingRulesetRef) { + return this.routingRules$.pipe( + map((objs) => objs.find((o) => o.ref.id === ref.id)), + shareReplay({ refCount: true, bufferSize: 1 }), + ); + } + + commit(ops: Operation[]) { + return this.version$.pipe( + first(), + switchMap((ver) => this.domainService.commit(ops, ver)), + ); + } +} diff --git a/apps/control-center/src/app/api/domain-config/types/domain-object-type.ts b/apps/control-center/src/app/api/domain-config/types/domain-object-type.ts deleted file mode 100644 index 25630f4c2..000000000 --- a/apps/control-center/src/app/api/domain-config/types/domain-object-type.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { DomainObjectType } from '@vality/domain-proto/domain'; -import { getImportValue } from '@vality/matez'; -import { ThriftAstMetadata, createThriftEnum } from '@vality/ng-thrift'; -import { map, share } from 'rxjs'; - -export const DOMAIN_OBJECT_TYPE$ = getImportValue( - import('@vality/domain-proto/metadata.json'), -).pipe( - map((metadata) => createThriftEnum(metadata, 'domain', 'DomainObjectType')), - share(), -); diff --git a/apps/control-center/src/app/api/domain-config/types/index.ts b/apps/control-center/src/app/api/domain-config/types/index.ts deleted file mode 100644 index affe44bba..000000000 --- a/apps/control-center/src/app/api/domain-config/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './domain-object-type'; diff --git a/apps/control-center/src/app/api/domain-config/utils/get-domain-object-reference.ts b/apps/control-center/src/app/api/domain-config/utils/get-domain-object-reference.ts new file mode 100644 index 000000000..28522ccb8 --- /dev/null +++ b/apps/control-center/src/app/api/domain-config/utils/get-domain-object-reference.ts @@ -0,0 +1,6 @@ +import { DomainObject, Reference } from '@vality/domain-proto/domain'; +import { getUnionKey, getUnionValue } from '@vality/ng-thrift'; + +export function getDomainObjectReference(domainObject: DomainObject): Reference { + return { [getUnionKey(domainObject)]: getUnionValue(domainObject).ref }; +} diff --git a/apps/control-center/src/app/api/fistful-admin/fistful-admin.service.ts b/apps/control-center/src/app/api/fistful-admin/fistful-admin.service.ts deleted file mode 100644 index 9f2277d45..000000000 --- a/apps/control-center/src/app/api/fistful-admin/fistful-admin.service.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - fistful_admin_FistfulAdmin, - fistful_admin_FistfulAdminCodegenClient, -} from '@vality/fistful-proto'; -import { Deposit } from '@vality/fistful-proto/deposit'; -import { DepositParams } from '@vality/fistful-proto/fistful_admin'; -import { SourceParams } from '@vality/fistful-proto/internal/fistful_admin'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class FistfulAdminService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('FistfulAdmin')), - ); - const metadata$ = from( - import('@vality/fistful-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - fistful_admin_FistfulAdmin({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - CreateDeposit(params: DepositParams): Observable { - return this.client$.pipe(switchMap((c) => c.CreateDeposit(params))); - } - - CreateSource(params: SourceParams) { - return this.client$.pipe(switchMap((c) => c.CreateSource(params))); - } -} diff --git a/apps/control-center/src/app/api/fistful-admin/index.ts b/apps/control-center/src/app/api/fistful-admin/index.ts deleted file mode 100644 index b7d218174..000000000 --- a/apps/control-center/src/app/api/fistful-admin/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './fistful-admin.service'; diff --git a/apps/control-center/src/app/api/fistful-stat/fistful-statistics.service.ts b/apps/control-center/src/app/api/fistful-stat/fistful-statistics.service.ts deleted file mode 100644 index 384c37341..000000000 --- a/apps/control-center/src/app/api/fistful-stat/fistful-statistics.service.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - fistful_stat_FistfulStatistics, - fistful_stat_FistfulStatisticsCodegenClient, -} from '@vality/fistful-proto'; -import { StatRequest, StatResponse } from '@vality/fistful-proto/fistful_stat'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class FistfulStatisticsService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('FistfulStatistics')), - ); - const metadata$ = from( - import('@vality/fistful-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - fistful_stat_FistfulStatistics({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - GetWallets(req: StatRequest): Observable { - return this.client$.pipe(switchMap((c) => c.GetWallets(req))); - } - - GetWithdrawals(req: StatRequest): Observable { - return this.client$.pipe(switchMap((c) => c.GetWithdrawals(req))); - } - - GetDeposits(req: StatRequest): Observable { - return this.client$.pipe(switchMap((c) => c.GetDeposits(req))); - } - - GetDepositReverts(req: StatRequest): Observable { - return this.client$.pipe(switchMap((c) => c.GetDepositReverts(req))); - } - - GetSources(req: StatRequest): Observable { - return this.client$.pipe(switchMap((c) => c.GetSources(req))); - } -} diff --git a/apps/control-center/src/app/api/fistful-stat/index.ts b/apps/control-center/src/app/api/fistful-stat/index.ts index 884911bdd..7fc28f342 100644 --- a/apps/control-center/src/app/api/fistful-stat/index.ts +++ b/apps/control-center/src/app/api/fistful-stat/index.ts @@ -1,2 +1 @@ -export * from './fistful-statistics.service'; export * from './query-dsl'; diff --git a/apps/control-center/src/app/api/fistful-stat/query-dsl/types/deposit-revert.ts b/apps/control-center/src/app/api/fistful-stat/query-dsl/types/deposit-revert.ts deleted file mode 100644 index 6daff48dd..000000000 --- a/apps/control-center/src/app/api/fistful-stat/query-dsl/types/deposit-revert.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Status } from '@vality/fistful-proto/deposit_revert_status'; -import { DepositStatus } from '@vality/fistful-proto/fistful_stat'; - -export interface DepositRevertParams { - party_id?: string; - identity_id?: string; - source_id?: string; - wallet_id?: string; - deposit_id?: string; - revert_id?: string; - amount_from?: number; - amount_to?: number; - currency_code?: string; - status?: Status; - deposit_status?: DepositStatus; - from_time?: string; - to_time?: string; - size?: string; -} diff --git a/apps/control-center/src/app/api/fistful-stat/query-dsl/types/index.ts b/apps/control-center/src/app/api/fistful-stat/query-dsl/types/index.ts index a2a4e0d81..340f0d731 100644 --- a/apps/control-center/src/app/api/fistful-stat/query-dsl/types/index.ts +++ b/apps/control-center/src/app/api/fistful-stat/query-dsl/types/index.ts @@ -2,4 +2,3 @@ export * from './query-dsl'; export * from './chargebacks'; export * from './refund'; export * from './withdrawal-params'; -export * from './deposit-revert'; diff --git a/apps/control-center/src/app/api/fistful-stat/query-dsl/types/query-dsl.ts b/apps/control-center/src/app/api/fistful-stat/query-dsl/types/query-dsl.ts index 76f3421e2..4f70dc711 100644 --- a/apps/control-center/src/app/api/fistful-stat/query-dsl/types/query-dsl.ts +++ b/apps/control-center/src/app/api/fistful-stat/query-dsl/types/query-dsl.ts @@ -1,6 +1,5 @@ import { Chargebacks } from './chargebacks'; import { Deposit } from './deposit'; -import { DepositRevertParams } from './deposit-revert'; import { PagedBaseParameters } from './paged-base-parameters'; import { Params } from './params'; import { Payment } from './payment'; @@ -25,7 +24,6 @@ export interface QueryDsl { chargebacks?: ChargebacksParams; refunds?: RefundsParams; wallets?: WalletParams; - deposit_reverts?: DepositRevertParams; withdrawals?: WithdrawalParams; sources?: SourceParams; }; diff --git a/apps/control-center/src/app/api/identity/identity-management.service.ts b/apps/control-center/src/app/api/identity/identity-management.service.ts deleted file mode 100644 index 5adfec15a..000000000 --- a/apps/control-center/src/app/api/identity/identity-management.service.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - identity_Management, - identity_ManagementCodegenClient, -} from '@vality/fistful-proto'; -import * as identity from '@vality/fistful-proto/internal/identity'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class IdentityManagementService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('IdentityManagement')), - ); - const metadata$ = from( - import('@vality/fistful-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - identity_Management({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - Get(id: identity.IdentityID, range: identity.EventRange): Observable { - return this.client$.pipe(switchMap((c) => c.Get(id, range))); - } -} diff --git a/apps/control-center/src/app/api/identity/index.ts b/apps/control-center/src/app/api/identity/index.ts deleted file mode 100644 index bd775292e..000000000 --- a/apps/control-center/src/app/api/identity/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './identity-management.service'; diff --git a/apps/control-center/src/app/api/payment-processing/index.ts b/apps/control-center/src/app/api/payment-processing/index.ts index e1b168d4f..0c925297e 100644 --- a/apps/control-center/src/app/api/payment-processing/index.ts +++ b/apps/control-center/src/app/api/payment-processing/index.ts @@ -1,3 +1 @@ -export * from './party-management.service'; -export * from './invoicing.service'; export * from './stores/parties-store.service'; diff --git a/apps/control-center/src/app/api/payment-processing/invoicing.service.ts b/apps/control-center/src/app/api/payment-processing/invoicing.service.ts deleted file mode 100644 index 7bb796b2c..000000000 --- a/apps/control-center/src/app/api/payment-processing/invoicing.service.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - payment_processing_Invoicing, - payment_processing_InvoicingCodegenClient, -} from '@vality/domain-proto'; -import { - InvoiceID, - InvoicePaymentChargeback, - InvoicePaymentChargebackID, - InvoicePaymentID, -} from '@vality/domain-proto/domain'; -import { - InvoicePayment, - InvoicePaymentAdjustment, - InvoicePaymentAdjustmentParams, - InvoicePaymentChargebackAcceptParams, - InvoicePaymentChargebackCancelParams, - InvoicePaymentChargebackParams, - InvoicePaymentChargebackRejectParams, - InvoicePaymentChargebackReopenParams, -} from '@vality/domain-proto/payment_processing'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class InvoicingService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('Invoicing')), - ); - const metadata$ = from( - import('@vality/domain-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - payment_processing_Invoicing({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - GetPayment(id: InvoiceID, paymentId: InvoicePaymentID): Observable { - return this.client$.pipe(switchMap((c) => c.GetPayment(id, paymentId))); - } - - CreatePaymentAdjustment( - id: InvoiceID, - paymentId: InvoicePaymentID, - params: InvoicePaymentAdjustmentParams, - ): Observable { - return this.client$.pipe( - switchMap((c) => c.CreatePaymentAdjustment(id, paymentId, params)), - ); - } - - CreateChargeback( - id: InvoiceID, - paymentId: InvoicePaymentID, - params: InvoicePaymentChargebackParams, - ): Observable { - return this.client$.pipe(switchMap((c) => c.CreateChargeback(id, paymentId, params))); - } - - AcceptChargeback( - id: InvoiceID, - paymentId: InvoicePaymentID, - chargebackId: InvoicePaymentChargebackID, - params: InvoicePaymentChargebackAcceptParams, - ): Observable { - return this.client$.pipe( - switchMap((c) => c.AcceptChargeback(id, paymentId, chargebackId, params)), - ); - } - - RejectChargeback( - id: InvoiceID, - paymentId: InvoicePaymentID, - chargebackId: InvoicePaymentChargebackID, - params: InvoicePaymentChargebackRejectParams, - ): Observable { - return this.client$.pipe( - switchMap((c) => c.RejectChargeback(id, paymentId, chargebackId, params)), - ); - } - - ReopenChargeback( - id: InvoiceID, - paymentId: InvoicePaymentID, - chargebackId: InvoicePaymentChargebackID, - params: InvoicePaymentChargebackReopenParams, - ): Observable { - return this.client$.pipe( - switchMap((c) => c.ReopenChargeback(id, paymentId, chargebackId, params)), - ); - } - - CancelChargeback( - id: InvoiceID, - paymentId: InvoicePaymentID, - chargebackId: InvoicePaymentChargebackID, - params: InvoicePaymentChargebackCancelParams, - ): Observable { - return this.client$.pipe( - switchMap((c) => c.CancelChargeback(id, paymentId, chargebackId, params)), - ); - } - - GetEvents(...args: Parameters) { - return this.client$.pipe(switchMap((c) => c.GetEvents(...args))); - } -} diff --git a/apps/control-center/src/app/api/payment-processing/party-management.service.ts b/apps/control-center/src/app/api/payment-processing/party-management.service.ts deleted file mode 100644 index 345b0a823..000000000 --- a/apps/control-center/src/app/api/payment-processing/party-management.service.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - payment_processing_PartyManagement, - payment_processing_PartyManagementCodegenClient, -} from '@vality/domain-proto'; -import { Contract, Party, Shop } from '@vality/domain-proto/domain'; -import { ContractID, PartyID, ShopContract, ShopID } from '@vality/domain-proto/payment_processing'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class PartyManagementService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('PartyManagement')), - ); - const metadata$ = from( - import('@vality/domain-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - payment_processing_PartyManagement({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - Get(partyId: PartyID): Observable { - return this.client$.pipe(switchMap((c) => c.Get(partyId))); - } - - GetContract(partyId: PartyID, contractId: ContractID): Observable { - return this.client$.pipe(switchMap((c) => c.GetContract(partyId, contractId))); - } - - GetShop(partyId: PartyID, id: ShopID): Observable { - return this.client$.pipe(switchMap((c) => c.GetShop(partyId, id))); - } - - GetShopContract(partyId: PartyID, id: ShopID): Observable { - return this.client$.pipe(switchMap((c) => c.GetShopContract(partyId, id))); - } - - SuspendShop(partyId: PartyID, id: ShopID): Observable { - return this.client$.pipe(switchMap((c) => c.SuspendShop(partyId, id))); - } - - ActivateShop(partyId: PartyID, id: ShopID): Observable { - return this.client$.pipe(switchMap((c) => c.ActivateShop(partyId, id))); - } - - BlockShop(partyId: PartyID, id: ShopID, reason: string): Observable { - return this.client$.pipe(switchMap((c) => c.BlockShop(partyId, id, reason))); - } - - UnblockShop(partyId: PartyID, id: ShopID, reason: string): Observable { - return this.client$.pipe(switchMap((c) => c.UnblockShop(partyId, id, reason))); - } -} diff --git a/apps/control-center/src/app/api/payment-processing/stores/parties-store.service.ts b/apps/control-center/src/app/api/payment-processing/stores/parties-store.service.ts index ce0d377a0..397e19446 100644 --- a/apps/control-center/src/app/api/payment-processing/stores/parties-store.service.ts +++ b/apps/control-center/src/app/api/payment-processing/stores/parties-store.service.ts @@ -1,78 +1,65 @@ import { Injectable, inject } from '@angular/core'; -import { PartyID } from '@vality/domain-proto/domain'; -import { ShopID, WalletID } from '@vality/domain-proto/internal/domain'; -import { progressTo } from '@vality/matez'; -import { BehaviorSubject, combineLatest } from 'rxjs'; -import { map, shareReplay } from 'rxjs/operators'; +import { PartyID, ShopID, WalletID } from '@vality/domain-proto/domain'; import { MemoizeExpiring } from 'typescript-memoize'; -import { FistfulStatisticsService, createDsl } from '../../fistful-stat'; -import { PartyManagementService } from '../party-management.service'; +import { DomainObjectsStoreService } from '../../domain-config'; @Injectable({ providedIn: 'root', }) export class PartiesStoreService { - private partyManagementService = inject(PartyManagementService); - private fistfulStatisticsService = inject(FistfulStatisticsService); - progress$ = new BehaviorSubject(0); + private domainObjectsStoreService = inject(DomainObjectsStoreService); - @MemoizeExpiring(5 * 60_000) - get(partyId: PartyID) { - return this.partyManagementService - .Get(partyId) - .pipe(progressTo(this.progress$), shareReplay({ refCount: true, bufferSize: 1 })); + parties = this.domainObjectsStoreService + .getObjects('party_config') + .map((objs) => objs.map((obj) => obj.object.party_config)); + shops = this.domainObjectsStoreService + .getObjects('shop_config') + .map((objs) => objs.map((obj) => obj.object.shop_config)); + wallets = this.domainObjectsStoreService + .getObjects('wallet_config') + .map((objs) => objs.map((obj) => obj.object.wallet_config)); + + @MemoizeExpiring(5_000) + getParty(partyId: PartyID) { + return this.domainObjectsStoreService + .getObject({ party_config: { id: partyId } }) + .map((obj) => obj.object.party_config); + } + + @MemoizeExpiring(5_000) + getPartyShops(partyId: PartyID) { + return this.shops.map((shops) => shops.filter((s) => s.data.party_id === partyId)); + } + + @MemoizeExpiring(5_000) + getPartyWallets(partyId: PartyID) { + return this.wallets.map((wallets) => wallets.filter((w) => w.data.party_id === partyId)); } - @MemoizeExpiring(5 * 60_000) - getShop(shopId: ShopID, partyId: PartyID) { - return this.get(partyId).pipe( - map((p) => p.shops.get(shopId)), - progressTo(this.progress$), - shareReplay({ refCount: true, bufferSize: 1 }), - ); + @MemoizeExpiring(5_000) + getShop(shopId: ShopID) { + return this.domainObjectsStoreService + .getObject({ shop_config: { id: shopId } }) + .map((obj) => obj.object.shop_config); } - @MemoizeExpiring(5 * 60_000) - getWallet(walletId: WalletID, partyId: PartyID) { - // return this.get(partyId).pipe( - // map((p) => p.wallets.get(walletId)), - // progressTo(this.progress$), - // shareReplay({ refCount: true, bufferSize: 1 }), - // ); - // TODO: We get it from fistful because wallets are not returned in the party object - return this.getWallets(partyId).pipe( - map((wallets) => wallets.find((w) => w.id === walletId)), - progressTo(this.progress$), - shareReplay({ refCount: true, bufferSize: 1 }), - ); + @MemoizeExpiring(5_000) + getWallet(walletId: WalletID) { + return this.domainObjectsStoreService + .getObject({ wallet_config: { id: walletId } }) + .map((obj) => obj.object.wallet_config); } - @MemoizeExpiring(5 * 60_000) - getContractor(shopId: ShopID, partyId: PartyID) { - return combineLatest([this.get(partyId), this.getShop(shopId, partyId)]).pipe( - map(([party, shop]) => { - const contractorId = party.contracts.get(shop.contract_id)?.contractor_id; - return party.contractors.get(contractorId)?.contractor; - }), - ); + reloadParties() { + this.parties.reload(); } - @MemoizeExpiring(5 * 60_000) - getContract(shopId: ShopID, partyId: PartyID) { - return combineLatest([this.get(partyId), this.getShop(shopId, partyId)]).pipe( - map(([party, shop]) => party.contracts.get(shop.contract_id)), - ); + reloadShops() { + this.shops.reload(); } - @MemoizeExpiring(5 * 60_000) - private getWallets(partyId: PartyID) { - return this.fistfulStatisticsService - .GetWallets({ dsl: createDsl({ wallets: { party_id: partyId } }) }) - .pipe( - map(({ data }) => data.wallets), - progressTo(this.progress$), - shareReplay({ refCount: true, bufferSize: 1 }), - ); + reloadWallets() { + this.wallets.reload(); } } diff --git a/apps/control-center/src/app/api/wallet/index.ts b/apps/control-center/src/app/api/wallet/index.ts deleted file mode 100644 index 9ea94922b..000000000 --- a/apps/control-center/src/app/api/wallet/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './management.service'; diff --git a/apps/control-center/src/app/api/wallet/management.service.ts b/apps/control-center/src/app/api/wallet/management.service.ts deleted file mode 100644 index 1825c7ef0..000000000 --- a/apps/control-center/src/app/api/wallet/management.service.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - wallet_Management, - wallet_ManagementCodegenClient, -} from '@vality/fistful-proto'; -import { ContextSet } from '@vality/fistful-proto/internal/context'; -import { AccountBalance, WalletParams } from '@vality/fistful-proto/internal/wallet'; -import { EventRange, WalletID, WalletState } from '@vality/fistful-proto/wallet'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class ManagementService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('WalletManagement')), - ); - const metadata$ = from( - import('@vality/fistful-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - wallet_Management({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - Get(id: WalletID, range: EventRange): Observable { - return this.client$.pipe(switchMap((c) => c.Get(id, range))); - } - - Create(params: WalletParams, context: ContextSet): Observable { - return this.client$.pipe(switchMap((c) => c.Create(params, context))); - } - - GetAccountBalance(id: WalletID): Observable { - return this.client$.pipe(switchMap((c) => c.GetAccountBalance(id))); - } -} diff --git a/apps/control-center/src/app/api/withdrawal/index.ts b/apps/control-center/src/app/api/withdrawal/index.ts deleted file mode 100644 index 9ea94922b..000000000 --- a/apps/control-center/src/app/api/withdrawal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './management.service'; diff --git a/apps/control-center/src/app/api/withdrawal/management.service.ts b/apps/control-center/src/app/api/withdrawal/management.service.ts deleted file mode 100644 index f23ac2b51..000000000 --- a/apps/control-center/src/app/api/withdrawal/management.service.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { - ThriftAstMetadata, - withdrawal_Management, - withdrawal_ManagementCodegenClient, -} from '@vality/fistful-proto'; -import { EventRange, WithdrawalID, WithdrawalState } from '@vality/fistful-proto/withdrawal'; -import { AdjustmentParams, AdjustmentState } from '@vality/fistful-proto/withdrawal_adjustment'; -import { Observable, combineLatest, from, map, switchMap } from 'rxjs'; - -import { environment } from '../../../environments/environment'; -import { ConfigService } from '../../core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../shared/services'; - -@Injectable({ providedIn: 'root' }) -export class ManagementService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private client$: Observable; - - constructor() { - const configService = inject(ConfigService); - const headers$ = this.keycloakTokenInfoService.info$.pipe( - map(toWachterHeaders('WithdrawalManagement')), - ); - const metadata$ = from( - import('@vality/fistful-proto/metadata.json').then( - (m) => m.default as ThriftAstMetadata[], - ), - ); - this.client$ = combineLatest([metadata$, headers$]).pipe( - switchMap(([metadata, headers]) => - withdrawal_Management({ - metadata, - headers, - logging: environment.logging.requests, - ...configService.config.api.wachter, - }), - ), - ); - } - - CreateAdjustment(id: WithdrawalID, params: AdjustmentParams): Observable { - return this.client$.pipe(switchMap((c) => c.CreateAdjustment(id, params))); - } - - Get(id: WithdrawalID, range: EventRange): Observable { - return this.client$.pipe(switchMap((c) => c.Get(id, range))); - } -} diff --git a/apps/control-center/src/app/app-routes.ts b/apps/control-center/src/app/app-routes.ts index 6ebf7d1e8..1d86816b8 100644 --- a/apps/control-center/src/app/app-routes.ts +++ b/apps/control-center/src/app/app-routes.ts @@ -5,10 +5,17 @@ import { Route, Services } from './shared/services'; type SectionPageRoutes = { [KSection: string]: { [KPage: string]: Route } }; export const APP_ROUTES = { - domain2: { - root: new Route('domain2', { - services: [Services.Domain], - loadChildren: () => import('./sections/domain2').then((m) => m.DomainModule), + domain: { + root: new Route('domain', { + services: [Services.DMT], + loadComponent: () => import('./domain-config').then((m) => m.DomainConfigComponent), + queryParams: z.object({ type: z.string() }), + }), + }, + parties: { + root: new Route('parties', { + services: [Services.DMT], + loadComponent: () => import('./parties').then((m) => m.PartiesComponent), queryParams: z.object({ type: z.string() }), }), }, diff --git a/apps/control-center/src/app/app-routing.module.ts b/apps/control-center/src/app/app-routing.module.ts index 550340b11..cfc13fb5e 100644 --- a/apps/control-center/src/app/app-routing.module.ts +++ b/apps/control-center/src/app/app-routing.module.ts @@ -1,15 +1,17 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from './shared/services'; +import { APP_ROUTES } from './app-routes'; @NgModule({ imports: [ RouterModule.forRoot( [ + APP_ROUTES.domain.root.getRoute(), + APP_ROUTES.parties.root.getRoute(), { path: '', - redirectTo: '/payments', + redirectTo: '/domain', pathMatch: 'full', }, ], @@ -18,7 +20,6 @@ import { AppAuthGuardService } from './shared/services'; }, ), ], - providers: [AppAuthGuardService], exports: [RouterModule], }) export class AppRoutingModule {} diff --git a/apps/control-center/src/app/app.component.html b/apps/control-center/src/app/app.component.html index 073c3364b..81b8926bc 100644 --- a/apps/control-center/src/app/app.component.html +++ b/apps/control-center/src/app/app.component.html @@ -1,27 +1,57 @@ - - - + - +
+
+ +
+
+ +
+ + +
+ + + + +
+
- + diff --git a/apps/control-center/src/app/app.component.scss b/apps/control-center/src/app/app.component.scss index 19c44cd86..8f8eff3ee 100644 --- a/apps/control-center/src/app/app.component.scss +++ b/apps/control-center/src/app/app.component.scss @@ -1,5 +1,6 @@ -.container { +.app-container { min-height: 100vh; + background: linear-gradient(67deg, #eeefff, #fbfbff); } .spacer { @@ -15,10 +16,8 @@ z-index: 2; } -.content { - margin-top: 64px; -} - -.sidenav-info { - width: 50vw; +.search-button { + box-shadow: + inset 0 2px 4px 0 rgba(0, 0, 0, 0.1), + inset 0 2px 2px 0 rgba(0, 0, 0, 0.06); } diff --git a/apps/control-center/src/app/app.component.ts b/apps/control-center/src/app/app.component.ts index 8b7ebf2a7..e01858fd3 100644 --- a/apps/control-center/src/app/app.component.ts +++ b/apps/control-center/src/app/app.component.ts @@ -1,18 +1,16 @@ -import { Component, inject, isDevMode } from '@angular/core'; -import { Link } from '@vality/matez'; -import { KeycloakService } from 'keycloak-angular'; -import sortBy from 'lodash-es/sortBy'; -import { Observable, from } from 'rxjs'; -import { map, shareReplay, startWith } from 'rxjs/operators'; +import { Component, Injector, inject, runInInjectionContext } from '@angular/core'; +import { Router } from '@angular/router'; +import { Repository } from '@vality/domain-proto/domain_config_v2'; +import { BaseLink, CmdkOption, CmdkService, Link, getUrlPath } from '@vality/matez'; +import Keycloak from 'keycloak-js'; +import { map, of } from 'rxjs'; import { environment } from '../environments/environment'; -import { ROUTING_CONFIG as CLAIMS_ROUTING_CONFIG } from './sections/claims/routing-config'; +import { APP_ROUTES } from './app-routes'; import { ROUTING_CONFIG as DEPOSITS_ROUTING_CONFIG } from './sections/deposits/routing-config'; -import { ROUTING_CONFIG as DOMAIN_ROUTING_CONFIG } from './sections/domain/routing-config'; import { ROUTING_CONFIG as MACHINES_ROUTING_CONFIG } from './sections/machines/routing-config'; import { ROUTING_CONFIG as PAYMENTS_ROUTING_CONFIG } from './sections/payments/routing-config'; -import { ROUTING_CONFIG as PARTIES_ROUTING_CONFIG } from './sections/search-parties/routing-config'; import { SHOPS_ROUTING_CONFIG } from './sections/shops'; import { ROUTING_CONFIG as SOURCES_ROUTING_CONFIG } from './sections/sources/routing-config'; import { ROUTING_CONFIG as TERMINALS_ROUTING_CONFIG } from './sections/terminals'; @@ -20,7 +18,155 @@ import { ROUTING_CONFIG as TERMS_ROUTING_CONFIG } from './sections/terms/routing import { ROUTING_CONFIG as WALLETS_ROUTING_CONFIG } from './sections/wallets/routing-config'; import { ROUTING_CONFIG as WITHDRAWALS_ROUTING_CONFIG } from './sections/withdrawals/routing-config'; import { SidenavInfoService } from './shared/components/sidenav-info'; -import { AppAuthGuardService, Services } from './shared/services'; +import { getLimitedDomainObjectDetails } from './shared/components/thrift-api-crud'; +import { DomainObjectCardComponent } from './shared/components/thrift-api-crud/domain2'; +import { KeycloakUserService, Services } from './shared/services'; + +function isHidden(services: Services[]): BaseLink['isHidden'] { + const keycloakUserService = inject(KeycloakUserService); + return !keycloakUserService.hasServiceRole(...services); +} + +const createNavLinks = (): Link[] => [ + { + children: [ + { + label: 'Domain config', + url: '/domain', + isHidden: isHidden(APP_ROUTES.domain.root.config.services), + }, + { + label: 'Terminals', + url: '/terminals', + isHidden: isHidden(TERMINALS_ROUTING_CONFIG.services), + }, + { + label: 'Machines', + url: '/machines', + isHidden: isHidden(MACHINES_ROUTING_CONFIG.services), + }, + { + label: 'Sources', + url: '/sources', + isHidden: isHidden(SOURCES_ROUTING_CONFIG.services), + }, + { + label: 'Terms', + url: '/terms', + isHidden: isHidden(TERMS_ROUTING_CONFIG.services), + children: [ + { + label: 'Shops', + url: '/terms/shops', + }, + { + label: 'Wallets', + url: '/terms/wallets', + }, + { + label: 'Terminals', + url: '/terms/terminals', + }, + ], + }, + ], + }, + { + children: [ + (url) => { + const urlPath = getUrlPath(url); + const partyPath = '/' + urlPath.slice(0, 2).join('/'); + const isPartyPath = urlPath[0] === 'parties' && urlPath.length > 1; + return { + label: isPartyPath ? 'Party' : 'Parties', + url: '/parties', + isHidden: isHidden(APP_ROUTES.parties.root.config.services), + children: isPartyPath + ? [ + { + label: 'Shops', + url: `${partyPath}/shops`, + }, + { + label: 'Wallets', + url: `${partyPath}/wallets`, + }, + { + label: 'Payment RR', + url: `${partyPath}/routing-rules/payment/main`, + checkUrl: `${partyPath}/routing-rules/payment`, + }, + { + label: 'Withdrawal RR', + url: `${partyPath}/routing-rules/withdrawal/main`, + checkUrl: `${partyPath}/routing-rules/withdrawal`, + }, + ] + : [], + }; + }, + { + label: 'Shops', + url: '/shops', + isHidden: isHidden(SHOPS_ROUTING_CONFIG.services), + }, + { + label: 'Wallets', + url: '/wallets', + isHidden: isHidden(WALLETS_ROUTING_CONFIG.services), + }, + ], + }, + { + children: [ + (url) => { + const urlPath = getUrlPath(url); + const paymentPath = '/' + urlPath.slice(0, 2).join('/'); + const isPaymentPath = urlPath[0] === 'payments' && urlPath.length > 1; + return { + label: isPaymentPath ? 'Payment' : 'Payments', + url: '/payments', + isHidden: isHidden(PAYMENTS_ROUTING_CONFIG.services), + children: isPaymentPath + ? [ + { + label: 'Details', + url: `${paymentPath}/details`, + }, + { + label: 'Events', + url: `${paymentPath}/events`, + }, + { + label: 'Refunds', + url: `${paymentPath}/refunds`, + }, + { + label: 'Chargebacks', + url: `${paymentPath}/chargebacks`, + }, + ] + : [], + }; + }, + { + label: 'Chargebacks', + url: '/chargebacks', + isHidden: isHidden(WALLETS_ROUTING_CONFIG.services), + }, + { + label: 'Deposits', + url: '/deposits', + isHidden: isHidden(DEPOSITS_ROUTING_CONFIG.services), + }, + { + label: 'Withdrawals', + url: '/withdrawals', + isHidden: isHidden(WITHDRAWALS_ROUTING_CONFIG.services), + }, + ], + }, +]; @Component({ selector: 'cc-root', @@ -29,17 +175,52 @@ import { AppAuthGuardService, Services } from './shared/services'; standalone: false, }) export class AppComponent { - private keycloakService = inject(KeycloakService); - private appAuthGuardService = inject(AppAuthGuardService); - public sidenavInfoService = inject(SidenavInfoService); - links$: Observable = from(this.keycloakService.loadUserProfile()).pipe( - startWith(null), - map(() => this.getMenuItemsGroups()), - shareReplay({ refCount: true, bufferSize: 1 }), - ); + private keycloakService = inject(Keycloak); + private keycloakUserService = inject(KeycloakUserService); + private repositoryService = inject(Repository); + private injector = inject(Injector); + + sidenavInfoService = inject(SidenavInfoService); + cmdkService = inject(CmdkService); + + searchKeys = [navigator.platform.toUpperCase().includes('MAC') ? '⌘' : 'Ctrl', 'K']; + links = createNavLinks(); + username = this.keycloakUserService.username; constructor() { this.registerConsoleUtils(); + this.cmdkService.init({ + search: (searchStr) => + runInInjectionContext(this.injector, () => { + if (!searchStr) return of([]); + const router = inject(Router); + const sidenavInfoService = inject(SidenavInfoService); + return this.repositoryService + .SearchObjects({ query: searchStr, limit: 25 }) + .pipe( + map((objects): CmdkOption[] => { + return objects.result.map((obj) => { + const details = getLimitedDomainObjectDetails(obj); + return { + label: details.label, + description: details.description, + tooltip: `${details.id} (${details.type})`, + action: async () => { + await router.navigate(['/domain']); + sidenavInfoService.open(DomainObjectCardComponent, { + ref: obj.ref, + }); + }, + }; + }); + }), + ); + }), + }); + } + + logout() { + this.keycloakService.logout(); } private registerConsoleUtils() { @@ -50,100 +231,8 @@ export class AppComponent { console.log(`Logging ${environment.logging.requests ? 'enabled' : 'disabled'}`); }, ccGetMyRoles: () => { - console.log(this.keycloakService.getUserRoles(true).sort().join('\n')); + console.log(this.keycloakUserService.roles.sort().join('\n')); }, }); } - - private getMenuItemsGroups() { - const menuItems: (Link & { services: Services[] })[][] = [ - [ - { - label: 'Domain config', - url: '/domain', - services: DOMAIN_ROUTING_CONFIG.services, - }, - ...(isDevMode() - ? [ - { - label: 'Domain config 2', - url: '/domain2', - services: DOMAIN_ROUTING_CONFIG.services, - }, - ] - : []), - { - label: 'Terminals', - url: '/terminals', - services: TERMINALS_ROUTING_CONFIG.services, - }, - { - label: 'Machines', - url: '/machines', - services: MACHINES_ROUTING_CONFIG.services, - }, - { - label: 'Sources', - url: '/sources', - services: SOURCES_ROUTING_CONFIG.services, - }, - { - label: 'Terms', - url: '/terms', - services: TERMS_ROUTING_CONFIG.services, - }, - ], - [ - { - label: 'Merchants', - url: '/parties', - services: PARTIES_ROUTING_CONFIG.services, - }, - { - label: 'Shops', - url: '/shops', - services: SHOPS_ROUTING_CONFIG.services, - }, - { - label: 'Wallets', - url: '/wallets', - services: WALLETS_ROUTING_CONFIG.services, - }, - { - label: 'Claims', - url: '/claims', - services: CLAIMS_ROUTING_CONFIG.services, - }, - ], - [ - { - label: 'Payments', - url: '/payments', - services: PAYMENTS_ROUTING_CONFIG.services, - }, - { - label: 'Chargebacks', - url: '/chargebacks', - services: WALLETS_ROUTING_CONFIG.services, - }, - { - label: 'Deposits', - url: '/deposits', - services: DEPOSITS_ROUTING_CONFIG.services, - }, - { - label: 'Withdrawals', - url: '/withdrawals', - services: WITHDRAWALS_ROUTING_CONFIG.services, - }, - ], - ]; - return menuItems - .map((group) => - group.filter((item) => - this.appAuthGuardService.userHasSomeServiceMethods(item.services), - ), - ) - .map((group) => sortBy(group, 'label')); - } } diff --git a/apps/control-center/src/app/app.module.ts b/apps/control-center/src/app/app.module.ts index 79e3a5fa3..54110105c 100644 --- a/apps/control-center/src/app/app.module.ts +++ b/apps/control-center/src/app/app.module.ts @@ -1,44 +1,51 @@ import { registerLocaleData } from '@angular/common'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import localeRu from '@angular/common/locales/ru'; -import { LOCALE_ID, NgModule } from '@angular/core'; +import { LOCALE_ID, NgModule, isDevMode } from '@angular/core'; import { MAT_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY_PROVIDER } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; +import { MatChipsModule } from '@angular/material/chips'; import { MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; import { MatDatepickerModule } from '@angular/material/datepicker'; +import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field'; import { MatIconModule, MatIconRegistry } from '@angular/material/icon'; import { MatListModule } from '@angular/material/list'; import { MatMenuModule } from '@angular/material/menu'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatToolbarModule } from '@angular/material/toolbar'; -import { BrowserModule, DomSanitizer } from '@angular/platform-browser'; +import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { InputMaskModule } from '@ngneat/input-mask'; -import { Deanonimus } from '@vality/deanonimus-proto/deanonimus'; +import { + AuthorManagement, + Repository, + RepositoryClient, +} from '@vality/domain-proto/domain_config_v2'; +import { Invoicing, PartyManagement } from '@vality/domain-proto/payment_processing'; import { DominatorService } from '@vality/dominator-proto/dominator'; +import { Management as DepositManagement } from '@vality/fistful-proto/deposit'; +import { FistfulStatistics } from '@vality/fistful-proto/fistful_stat'; +import { Management as SourceManagement } from '@vality/fistful-proto/source'; +import { Management as WithdrawalManagement } from '@vality/fistful-proto/withdrawal'; import { Automaton } from '@vality/machinegun-proto/state_processing'; import { MerchantStatisticsService } from '@vality/magista-proto/magista'; -import { NavComponent, QUERY_PARAMS_SERIALIZERS } from '@vality/matez'; +import { ERROR_PARSER, LogError, NavComponent, QUERY_PARAMS_SERIALIZERS } from '@vality/matez'; import { MonacoEditorModule } from '@vality/ng-thrift'; import { RepairManagement } from '@vality/repairer-proto/repairer'; import { AccountService } from '@vality/scrooge-proto/account_balance'; +import { provideKeycloak } from 'keycloak-angular'; -import { provideThriftServices } from '../utils'; +import { environment } from '../environments/environment'; +import { ConfigService } from '../services'; +import { parseThriftError, provideThriftServices } from '../utils'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; -import { ToolbarComponent } from './core/components/toolbar/toolbar.component'; -import { CoreModule } from './core/core.module'; -import icons from './icons.json'; -import { ClaimsModule } from './sections/claims/claims.module'; -import { SearchPartiesModule } from './sections/search-parties/search-parties.module'; import { SectionsModule } from './sections/sections.module'; import { CandidateCardComponent } from './shared/components/candidate-card/candidate-card.component'; -import { ShopCardComponent } from './shared/components/shop-card/shop-card.component'; -import { ShopContractCardComponent } from './shared/components/shop-contract-card/shop-contract-card.component'; import { SIDENAV_INFO_COMPONENTS, SidenavInfoComponent } from './shared/components/sidenav-info'; import { TerminalDelegatesCardComponent } from './shared/components/terminal-delegates-card/terminal-delegates-card.component'; -import { DomainObjectCardComponent } from './shared/components/thrift-api-crud'; -import { KeycloakTokenInfoModule } from './shared/services'; +import { DomainObjectCardComponent } from './shared/components/thrift-api-crud/domain2'; import { DATE_RANGE_DAYS, DEBOUNCE_TIME_MS, @@ -56,19 +63,14 @@ registerLocaleData(localeRu); BrowserModule, BrowserAnimationsModule, AppRoutingModule, - CoreModule, MatToolbarModule, MatIconModule, MatButtonModule, MatMenuModule, MatSidenavModule, MatListModule, - SearchPartiesModule, - ClaimsModule, - KeycloakTokenInfoModule, SectionsModule, SidenavInfoComponent, - ToolbarComponent, NavComponent, MonacoEditorModule.forRoot({ requireConfig: { @@ -82,51 +84,64 @@ registerLocaleData(localeRu); MatDatepickerModule, // TODO: hack for cash field 😡 InputMaskModule, + MatChipsModule, ], providers: [ + ConfigService, + provideKeycloak({ + config: environment.authConfigPath as never, + initOptions: { onLoad: 'login-required', checkLoginIframe: !isDevMode() }, + }), + provideHttpClient(withInterceptorsFromDi()), { provide: MAT_DATE_FORMATS, useValue: DEFAULT_MAT_DATE_FORMATS }, { provide: MAT_DATE_LOCALE, useValue: 'en-GB' }, { provide: LOCALE_ID, useValue: 'ru' }, { provide: QUERY_PARAMS_SERIALIZERS, useValue: DEFAULT_QUERY_PARAMS_SERIALIZERS }, { provide: DATE_RANGE_DAYS, useValue: DEFAULT_DATE_RANGE_DAYS }, { provide: DEBOUNCE_TIME_MS, useValue: DEFAULT_DEBOUNCE_TIME_MS }, + { + provide: ERROR_PARSER, + useValue: (err) => { + const parsedError = parseThriftError(err) as LogError; + parsedError.noConsole = parsedError.name !== 'UnknownError'; + return parsedError; + }, + }, { provide: SIDENAV_INFO_COMPONENTS, useValue: { domainObject: DomainObjectCardComponent, - shop: ShopCardComponent, - shopContract: ShopContractCardComponent, terminalDelegates: TerminalDelegatesCardComponent, candidate: CandidateCardComponent, }, }, + { provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { appearance: 'outline' } }, MAT_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY_PROVIDER, provideThriftServices([ { service: RepairManagement, name: 'RepairManagement' }, - { service: Deanonimus, name: 'Deanonimus' }, { service: AccountService, name: 'Scrooge' }, { service: MerchantStatisticsService, name: 'MerchantStatistics' }, { service: DominatorService, name: 'Dominator' }, { service: Automaton, name: 'Automaton' }, + { service: Invoicing, name: 'Invoicing' }, + { service: PartyManagement, name: 'PartyManagement' }, + { service: Repository, name: 'DMT' }, + { service: RepositoryClient, name: 'DMTClient' }, + { service: AuthorManagement, name: 'DMTAuthor' }, + { service: DepositManagement, name: 'DepositManagement' }, + { service: FistfulStatistics, name: 'FistfulStatistics' }, + { service: WithdrawalManagement, name: 'WithdrawalManagement' }, + { service: SourceManagement, name: 'SourceManagement' }, ]), ], bootstrap: [AppComponent], }) export class AppModule { - constructor( - private matIconRegistry: MatIconRegistry, - private domSanitizer: DomSanitizer, - ) { + constructor(private matIconRegistry: MatIconRegistry) { this.registerIcons(); } registerIcons(): void { this.matIconRegistry.setDefaultFontSetClass('material-symbols-outlined'); - for (const name of icons) { - this.matIconRegistry.addSvgIcon( - name, - this.domSanitizer.bypassSecurityTrustResourceUrl(`../assets/icons/${name}.svg`), - ); - } } } diff --git a/apps/control-center/src/app/core/components/toolbar/toolbar.component.html b/apps/control-center/src/app/core/components/toolbar/toolbar.component.html deleted file mode 100644 index 2a0653e62..000000000 --- a/apps/control-center/src/app/core/components/toolbar/toolbar.component.html +++ /dev/null @@ -1,41 +0,0 @@ - - - -
- {{ user()?.username }} - -
-
- - diff --git a/apps/control-center/src/app/core/components/toolbar/toolbar.component.scss b/apps/control-center/src/app/core/components/toolbar/toolbar.component.scss deleted file mode 100644 index cf9a956a2..000000000 --- a/apps/control-center/src/app/core/components/toolbar/toolbar.component.scss +++ /dev/null @@ -1,63 +0,0 @@ -.toolbar { - position: fixed; - z-index: 2; - display: grid; - grid-template-columns: 1fr 1fr 1fr; - color: #fff; - - & > * { - display: flex; - align-items: center; - } -} - -.logo { - display: grid; - gap: 8px; - grid-template-columns: 1fr 1fr; - cursor: pointer; -} - -.search { - gap: 12px; - --mtx-select-enabled-arrow-color: #fff; - - & > * { - min-width: 96px; - } - - .input { - width: 280px; - align-self: end; - margin-bottom: 4px; - } - - ::ng-deep .mat-mdc-form-field-subscript-wrapper { - display: none; - } - - ::ng-deep .mdc-floating-label { - pointer-events: none !important; - } - - ::ng-deep * { - color: #fff !important; - } - - ::ng-deep .mdc-notched-outline * { - border-color: rgba(255, 255, 255, 0.5) !important; - } - - ::ng-deep .ng-spinner-loader { - border-color: rgba(255, 255, 255, 0.2); - border-left-color: #fff; - } -} - -.user { - justify-self: end; - - * { - color: #fff; - } -} diff --git a/apps/control-center/src/app/core/components/toolbar/toolbar.component.ts b/apps/control-center/src/app/core/components/toolbar/toolbar.component.ts deleted file mode 100644 index 41db587ff..000000000 --- a/apps/control-center/src/app/core/components/toolbar/toolbar.component.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { CdkCopyToClipboard } from '@angular/cdk/clipboard'; -import { CommonModule } from '@angular/common'; -import { Component, DestroyRef, OnInit, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatIconModule } from '@angular/material/icon'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatSnackBar } from '@angular/material/snack-bar'; -import { MatToolbarModule } from '@angular/material/toolbar'; -import { Router, RouterModule } from '@angular/router'; -import { UrlService } from '@vality/matez'; -import { distinctUntilChanged, map } from 'rxjs/operators'; - -import { MerchantFieldModule } from '../../../shared/components/merchant-field'; -import { KeycloakUserService } from '../../../shared/services'; - -@Component({ - selector: 'cc-toolbar', - imports: [ - MatButtonModule, - MatIconModule, - MatMenuModule, - MatToolbarModule, - CommonModule, - MerchantFieldModule, - FormsModule, - ReactiveFormsModule, - CdkCopyToClipboard, - RouterModule, - ], - templateUrl: './toolbar.component.html', - styleUrl: './toolbar.component.scss', -}) -export class ToolbarComponent implements OnInit { - private keycloakUserService = inject(KeycloakUserService); - private router = inject(Router); - private urlService = inject(UrlService); - private destroyRef = inject(DestroyRef); - private snackBar = inject(MatSnackBar); - user = this.keycloakUserService.user.value; - partyIdControl = new FormControl(this.getPartyId()); - hasMenu$ = this.urlService.path$.pipe(map((p) => p.length <= 3)); - - ngOnInit() { - this.partyIdControl.valueChanges - .pipe(distinctUntilChanged(), takeUntilDestroyed(this.destroyRef)) - .subscribe((partyId) => { - const currentPartyId = this.getPartyId() || null; - if (partyId) { - if (currentPartyId !== partyId) { - void this.router.navigate([`/party/${partyId}`]); - } - } else if (currentPartyId) { - void this.router.navigate([`/parties`]); - } - }); - this.urlService.path$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((path) => { - const partyId = this.getPartyId(path) || null; - if (partyId !== this.partyIdControl.value) { - this.partyIdControl.setValue(partyId); - } - }); - } - - logout() { - void this.keycloakUserService.logout(); - } - - copyNotify(res: boolean) { - this.snackBar.open( - res - ? `Party Id #${this.partyIdControl.value} copied` - : `Party Id not copied, select and copy yourself: ${this.partyIdControl.value}`, - 'OK', - { duration: res ? 2_000 : 60_000 }, - ); - } - - toParty() { - void this.router.navigate([`/party/${this.partyIdControl.value}`]); - } - - private getPartyId(path: string[] = this.urlService.path) { - return path[0] === 'party' && path[1] ? path[1] : null; - } -} diff --git a/apps/control-center/src/app/core/core.module.ts b/apps/control-center/src/app/core/core.module.ts deleted file mode 100644 index acbe9a504..000000000 --- a/apps/control-center/src/app/core/core.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; -import { NgModule, inject, provideAppInitializer } from '@angular/core'; -import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular'; - -import { environment } from '../../environments/environment'; - -import { ConfigService } from './config.service'; - -const initializer = (keycloak: KeycloakService, configService: ConfigService) => () => - Promise.all([ - configService.load().then(() => - Promise.all([ - keycloak - .init({ - config: environment.authConfigPath, - initOptions: { - onLoad: 'login-required', - checkLoginIframe: true, - }, - enableBearerInterceptor: true, - bearerExcludedUrls: ['/assets'], - bearerPrefix: 'Bearer', - }) - .then(() => keycloak.getToken()), - ]), - ), - ]); - -@NgModule({ - imports: [CommonModule, KeycloakAngularModule], - providers: [ - ConfigService, - provideAppInitializer(() => { - const initializerFn = initializer(inject(KeycloakService), inject(ConfigService)); - return initializerFn(); - }), - provideHttpClient(withInterceptorsFromDi()), - ], -}) -export class CoreModule {} diff --git a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects.component.html b/apps/control-center/src/app/domain-config/domain-config.component.html similarity index 100% rename from apps/control-center/src/app/sections/domain2/domain-objects/domain-objects.component.html rename to apps/control-center/src/app/domain-config/domain-config.component.html diff --git a/apps/control-center/src/app/sections/domain/domain-info/domain-info.component.scss b/apps/control-center/src/app/domain-config/domain-config.component.scss similarity index 100% rename from apps/control-center/src/app/sections/domain/domain-info/domain-info.component.scss rename to apps/control-center/src/app/domain-config/domain-config.component.scss diff --git a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects.component.ts b/apps/control-center/src/app/domain-config/domain-config.component.ts similarity index 72% rename from apps/control-center/src/app/sections/domain2/domain-objects/domain-objects.component.ts rename to apps/control-center/src/app/domain-config/domain-config.component.ts index 380a69916..6606c0396 100644 --- a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects.component.ts +++ b/apps/control-center/src/app/domain-config/domain-config.component.ts @@ -3,22 +3,23 @@ import { MatButtonModule } from '@angular/material/button'; import { ReflessDomainObject } from '@vality/domain-proto/domain'; import { DialogResponseStatus } from '@vality/matez'; -import { DomainService, FetchDomainObjectsService } from '../../../api/domain-config'; -import { PageLayoutModule } from '../../../shared'; -import { DomainObjectService } from '../../../shared/components/thrift-api-crud/domain2'; +import { DomainService, FetchDomainObjectsService } from '../api/domain-config'; +import { PageLayoutModule } from '../shared'; +import { DomainObjectService } from '../shared/components/thrift-api-crud/domain2'; import { DomainObjectsTableComponent } from './domain-objects-table'; @Component({ - templateUrl: './domain-objects.component.html', - styleUrls: ['./domain-objects.component.scss'], + templateUrl: './domain-config.component.html', + styleUrls: ['./domain-config.component.scss'], imports: [PageLayoutModule, DomainObjectsTableComponent, MatButtonModule], providers: [FetchDomainObjectsService], }) -export class DomainObjectsComponent { +export class DomainConfigComponent { private domainService = inject(DomainService); private domainObjectService = inject(DomainObjectService); private fetchDomainObjectsService = inject(FetchDomainObjectsService); + version = this.domainService.version.value; selectedType = signal(null); diff --git a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects-table/domain-objects-table.component.html b/apps/control-center/src/app/domain-config/domain-objects-table/domain-objects-table.component.html similarity index 91% rename from apps/control-center/src/app/sections/domain2/domain-objects/domain-objects-table/domain-objects-table.component.html rename to apps/control-center/src/app/domain-config/domain-objects-table/domain-objects-table.component.html index 152538560..7d768cb7d 100644 --- a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects-table/domain-objects-table.component.html +++ b/apps/control-center/src/app/domain-config/domain-objects-table/domain-objects-table.component.html @@ -13,7 +13,7 @@ diff --git a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects-table/domain-objects-table.component.ts b/apps/control-center/src/app/domain-config/domain-objects-table/domain-objects-table.component.ts similarity index 84% rename from apps/control-center/src/app/sections/domain2/domain-objects/domain-objects-table/domain-objects-table.component.ts rename to apps/control-center/src/app/domain-config/domain-objects-table/domain-objects-table.component.ts index 8be242587..c6b307507 100644 --- a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects-table/domain-objects-table.component.ts +++ b/apps/control-center/src/app/domain-config/domain-objects-table/domain-objects-table.component.ts @@ -3,7 +3,7 @@ import { Component, DestroyRef, Injector, OnInit, inject, model, output } from ' import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; -import { DomainObject, ReflessDomainObject } from '@vality/domain-proto/domain'; +import { DomainObject, DomainObjectType, ReflessDomainObject } from '@vality/domain-proto/domain'; import { LimitedVersionedObject } from '@vality/domain-proto/domain_config_v2'; import { ActionsModule, @@ -13,20 +13,21 @@ import { SelectFieldModule, TableModule, createMenuColumn, + getEnumKeys, getValueChanges, } from '@vality/matez'; import { getUnionKey } from '@vality/ng-thrift'; import startCase from 'lodash-es/startCase'; import { combineLatest, merge } from 'rxjs'; -import { debounceTime, distinctUntilChanged, first, map, shareReplay } from 'rxjs/operators'; +import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'; -import { DOMAIN_OBJECT_TYPE$, FetchDomainObjectsService } from '../../../../api/domain-config'; -import { SidenavInfoService } from '../../../../shared/components/sidenav-info'; -import { getReferenceId } from '../../../../shared/components/thrift-api-crud'; +import { FetchDomainObjectsService } from '../../api/domain-config'; +import { SidenavInfoService } from '../../shared/components/sidenav-info'; +import { getReferenceId } from '../../shared/components/thrift-api-crud'; import { DomainObjectCardComponent, DomainObjectService, -} from '../../../../shared/components/thrift-api-crud/domain2'; +} from '../../shared/components/thrift-api-crud/domain2'; @Component({ selector: 'cc-domain-objects-table', @@ -116,17 +117,12 @@ export class DomainObjectsTableComponent implements OnInit { ], })), ]; - options$ = DOMAIN_OBJECT_TYPE$.pipe( - map((types) => - Object.keys(types) - .sort() - .map((type) => ({ - label: startCase(String(type)), - value: type, - })), - ), - shareReplay({ refCount: true, bufferSize: 1 }), - ); + options = getEnumKeys(DomainObjectType) + .sort() + .map((type) => ({ + label: startCase(String(type)), + value: type, + })); isLoading$ = this.fetchDomainObjectsService.isLoading$; hasMore$ = this.fetchDomainObjectsService.hasMore$; filter = model(this.qp.params.filter); @@ -151,9 +147,7 @@ export class DomainObjectsTableComponent implements OnInit { .pipe(takeUntilDestroyed(this.dr)) .subscribe(([type, query]) => { if (type) { - DOMAIN_OBJECT_TYPE$.pipe(first()).subscribe((types) => { - this.fetchDomainObjectsService.load({ type: types[type], query }); - }); + this.fetchDomainObjectsService.load({ type: DomainObjectType[type], query }); } else { this.fetchDomainObjectsService.load({ query }); } diff --git a/apps/control-center/src/app/sections/domain/domain-info/domain-objects-table/index.ts b/apps/control-center/src/app/domain-config/domain-objects-table/index.ts similarity index 100% rename from apps/control-center/src/app/sections/domain/domain-info/domain-objects-table/index.ts rename to apps/control-center/src/app/domain-config/domain-objects-table/index.ts diff --git a/apps/control-center/src/app/domain-config/index.ts b/apps/control-center/src/app/domain-config/index.ts new file mode 100644 index 000000000..6f01d9da3 --- /dev/null +++ b/apps/control-center/src/app/domain-config/index.ts @@ -0,0 +1 @@ +export * from './domain-config.component'; diff --git a/apps/control-center/src/app/icons.json b/apps/control-center/src/app/icons.json deleted file mode 100644 index fe51488c7..000000000 --- a/apps/control-center/src/app/icons.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/apps/control-center/src/app/parties/index.ts b/apps/control-center/src/app/parties/index.ts new file mode 100644 index 000000000..ecbc0d7b0 --- /dev/null +++ b/apps/control-center/src/app/parties/index.ts @@ -0,0 +1 @@ +export * from './parties.component'; diff --git a/apps/control-center/src/app/sections/search-parties/search-parties.component.html b/apps/control-center/src/app/parties/parties.component.html similarity index 82% rename from apps/control-center/src/app/sections/search-parties/search-parties.component.html rename to apps/control-center/src/app/parties/parties.component.html index a9b12f36b..935e1a4c5 100644 --- a/apps/control-center/src/app/sections/search-parties/search-parties.component.html +++ b/apps/control-center/src/app/parties/parties.component.html @@ -1,4 +1,4 @@ - + diff --git a/apps/control-center/src/app/sections/search-parties/search-parties.component.scss b/apps/control-center/src/app/parties/parties.component.scss similarity index 100% rename from apps/control-center/src/app/sections/search-parties/search-parties.component.scss rename to apps/control-center/src/app/parties/parties.component.scss diff --git a/apps/control-center/src/app/parties/parties.component.ts b/apps/control-center/src/app/parties/parties.component.ts new file mode 100644 index 000000000..d444af995 --- /dev/null +++ b/apps/control-center/src/app/parties/parties.component.ts @@ -0,0 +1,114 @@ +import { CommonModule } from '@angular/common'; +import { Component, OnInit, inject } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { Router } from '@angular/router'; +import { DomainObjectType, PartyConfig } from '@vality/domain-proto/domain'; +import { + Column, + DebounceTime, + QueryParamsService, + TableModule, + UpdateOptions, + createMenuColumn, +} from '@vality/matez'; +import { getUnionKey } from '@vality/ng-thrift'; +import startCase from 'lodash-es/startCase'; +import { map } from 'rxjs/operators'; + +import { FetchFullDomainObjectsService } from '../api/domain-config'; +import { PageLayoutModule } from '../shared'; + +@Component({ + templateUrl: 'parties.component.html', + styleUrls: ['parties.component.scss'], + providers: [FetchFullDomainObjectsService], + imports: [MatCardModule, CommonModule, MatProgressBarModule, PageLayoutModule, TableModule], +}) +export class PartiesComponent implements OnInit { + private qp = inject>(QueryParamsService<{ text: string }>); + private fetchFullDomainObjectsService = inject(FetchFullDomainObjectsService); + private router = inject(Router); + + initSearchParams$ = this.qp.params$.pipe(map((p) => p?.text ?? '')); + inProgress$ = this.fetchFullDomainObjectsService.isLoading$; + parties$ = this.fetchFullDomainObjectsService.result$.pipe( + map((objs) => objs.map((obj) => obj.object.party_config.data)), + ); + columns: Column[] = [ + { + field: 'id', + cell: (party) => ({ value: party.id }), + }, + { + field: 'email', + cell: (party) => ({ + value: party.contact_info.registration_email, + description: (party.contact_info.manager_contact_emails || []) + .filter(Boolean) + .join(', '), + link: () => `/parties/${party.id}`, + }), + }, + { + field: 'blocking', + cell: (party) => ({ + value: startCase(getUnionKey(party.blocking)), + color: ( + { + blocked: 'warn', + unblocked: 'success', + } as const + )[getUnionKey(party.blocking)], + }), + }, + { + field: 'suspension', + cell: (party) => ({ + value: startCase(getUnionKey(party.suspension)), + color: ( + { + suspended: 'warn', + active: 'success', + } as const + )[getUnionKey(party.suspension)], + }), + }, + { + field: 'shops', + cell: (party) => ({ + value: party.shops.length, + link: () => `/parties/${party.id}/shops`, + }), + }, + createMenuColumn((party) => ({ + items: [ + { + label: 'Details', + click: () => this.router.navigate([`/parties/${party.id}`]), + }, + ], + })), + ]; + + ngOnInit() { + this.searchParamsUpdated(this.qp.params.text); + } + + @DebounceTime() + searchParamsUpdated(filter: string) { + void this.qp.set({ text: filter }); + this.fetchFullDomainObjectsService.load({ + query: filter, + type: DomainObjectType.party_config, + }); + } + + reload(options: UpdateOptions) { + this.fetchFullDomainObjectsService.reload(options); + } + + more() { + this.fetchFullDomainObjectsService.more(); + } +} diff --git a/apps/control-center/src/app/sections/chargebacks/chargebacks-routing.module.ts b/apps/control-center/src/app/sections/chargebacks/chargebacks-routing.module.ts index 4c3c0c3fb..1fd435d91 100644 --- a/apps/control-center/src/app/sections/chargebacks/chargebacks-routing.module.ts +++ b/apps/control-center/src/app/sections/chargebacks/chargebacks-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { ChargebacksComponent } from './chargebacks.component'; import { ROUTING_CONFIG } from './routing-config'; @@ -12,7 +12,7 @@ import { ROUTING_CONFIG } from './routing-config'; { path: '', component: ChargebacksComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/chargebacks/chargebacks.component.html b/apps/control-center/src/app/sections/chargebacks/chargebacks.component.html index a96a1cbe6..e85ed401e 100644 --- a/apps/control-center/src/app/sections/chargebacks/chargebacks.component.html +++ b/apps/control-center/src/app/sections/chargebacks/chargebacks.component.html @@ -8,11 +8,7 @@ - + Statuses diff --git a/apps/control-center/src/app/sections/chargebacks/components/create-chargebacks-by-file-dialog/create-chargebacks-by-file-dialog.component.ts b/apps/control-center/src/app/sections/chargebacks/components/create-chargebacks-by-file-dialog/create-chargebacks-by-file-dialog.component.ts index 5144a5277..d9f45ef59 100644 --- a/apps/control-center/src/app/sections/chargebacks/components/create-chargebacks-by-file-dialog/create-chargebacks-by-file-dialog.component.ts +++ b/apps/control-center/src/app/sections/chargebacks/components/create-chargebacks-by-file-dialog/create-chargebacks-by-file-dialog.component.ts @@ -1,6 +1,7 @@ import { Component, DestroyRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { InvoicePaymentChargeback } from '@vality/domain-proto/domain'; +import { Invoicing } from '@vality/domain-proto/payment_processing'; import { DEFAULT_DIALOG_CONFIG, DialogSuperclass, @@ -9,8 +10,6 @@ import { } from '@vality/matez'; import { BehaviorSubject } from 'rxjs'; -import { InvoicingService } from '../../../../api/payment-processing/invoicing.service'; - import { CSV_CHARGEBACK_PROPS, CsvChargeback } from './types/csv-chargeback'; import { getCreateChargebackArgs } from './utils/get-create-chargeback-args'; @@ -24,7 +23,7 @@ export class CreateChargebacksByFileDialogComponent extends DialogSuperclass< void, InvoicePaymentChargeback[] > { - private invoicingService = inject(InvoicingService); + private invoicingService = inject(Invoicing); private log = inject(NotifyLogService); private destroyRef = inject(DestroyRef); static override defaultDialogConfig = DEFAULT_DIALOG_CONFIG.large; diff --git a/apps/control-center/src/app/sections/chargebacks/components/create-chargebacks-by-file-dialog/utils/get-create-chargeback-args.ts b/apps/control-center/src/app/sections/chargebacks/components/create-chargebacks-by-file-dialog/utils/get-create-chargeback-args.ts index 3fbd62c62..e8b4c4169 100644 --- a/apps/control-center/src/app/sections/chargebacks/components/create-chargebacks-by-file-dialog/utils/get-create-chargeback-args.ts +++ b/apps/control-center/src/app/sections/chargebacks/components/create-chargebacks-by-file-dialog/utils/get-create-chargeback-args.ts @@ -1,4 +1,4 @@ -import { CodegenClient } from '@vality/domain-proto/internal/payment_processing-Invoicing'; +import { Invoicing } from '@vality/domain-proto/payment_processing'; import { clean } from '@vality/matez'; import isNil from 'lodash-es/isNil'; import short from 'short-uuid'; @@ -7,7 +7,7 @@ import { CsvChargeback } from '../types/csv-chargeback'; export function getCreateChargebackArgs( c: CsvChargeback, -): Parameters { +): Parameters { return [ c.invoice_id, c.payment_id, diff --git a/apps/control-center/src/app/sections/claim/claim-routing.module.ts b/apps/control-center/src/app/sections/claim/claim-routing.module.ts deleted file mode 100644 index a8595b610..000000000 --- a/apps/control-center/src/app/sections/claim/claim-routing.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { AppAuthGuardService } from '../../shared/services'; - -import { ClaimComponent } from './claim.component'; -import { ROUTING_CONFIG } from './routing-config'; - -@NgModule({ - imports: [ - RouterModule.forChild([ - { - path: '', - component: ClaimComponent, - canActivate: [AppAuthGuardService], - data: ROUTING_CONFIG, - }, - ]), - ], -}) -export class ClaimRoutingModule {} diff --git a/apps/control-center/src/app/sections/claim/claim.component.html b/apps/control-center/src/app/sections/claim/claim.component.html deleted file mode 100644 index 7c737c580..000000000 --- a/apps/control-center/src/app/sections/claim/claim.component.html +++ /dev/null @@ -1,102 +0,0 @@ - - @if (claim$ | async; as claim) { - - {{ - claim.status | ngtUnionKey | ngtKeyTitle | titlecase - }} - - } - -
- - @if (isLoading$ | async) { - - - hourglass_empty - - - - - - } @else { - @let claim = claim$ | async; - - - - create - - - - - - @for (modificationUnit of claim.changeset; track modificationUnit) { - @switch (modificationUnit.modification | ngtUnionKey) { - @case ('party_modification') { - @switch (modificationUnit.modification | ngtUnionValue | ngtUnionKey) { - @case ('shop_modification') { - - - } - @default { - - } - } - } - @case ('claim_modification') { - @switch (modificationUnit.modification | ngtUnionValue | ngtUnionKey) { - @case ('status_modification') { - - - } - @default { - - } - } - } - @default { - - } - } - } - } - - - - - - - - -
-
diff --git a/apps/control-center/src/app/sections/claim/claim.component.scss b/apps/control-center/src/app/sections/claim/claim.component.scss deleted file mode 100644 index 5261aadde..000000000 --- a/apps/control-center/src/app/sections/claim/claim.component.scss +++ /dev/null @@ -1,4 +0,0 @@ -.content { - margin: 32px 24px; - max-width: 1128px; -} diff --git a/apps/control-center/src/app/sections/claim/claim.component.ts b/apps/control-center/src/app/sections/claim/claim.component.ts deleted file mode 100644 index bf3aa73e8..000000000 --- a/apps/control-center/src/app/sections/claim/claim.component.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { Component, DestroyRef, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { ActivatedRoute } from '@angular/router'; -import { - DialogResponseStatus, - DialogService, - NotifyLogService, - handleError, - inProgressFrom, - progressTo, -} from '@vality/matez'; -import { getUnionKey } from '@vality/ng-thrift'; -import { BehaviorSubject, Observable, Subject, combineLatest, defer, merge, switchMap } from 'rxjs'; -import { first, map, shareReplay } from 'rxjs/operators'; - -import { ClaimManagementService } from '../../api/claim-management/claim-management.service'; -import { PartyManagementService } from '../../api/payment-processing/party-management.service'; -import { DomainMetadataFormExtensionsService } from '../../shared/services'; - -import { AddModificationDialogComponent } from './components/add-modification-dialog/add-modification-dialog.component'; -import { ChangeStatusDialogComponent } from './components/change-status-dialog/change-status-dialog.component'; -import { CreateShopDialogComponent } from './components/create-shop-dialog/create-shop-dialog.component'; -import { AllowedClaimStatusesService } from './services/allowed-claim-statuses.service'; -import { CLAIM_STATUS_COLOR } from './types/claim-status-color'; - -@Component({ - selector: 'cc-claim', - templateUrl: './claim.component.html', - styleUrls: ['claim.component.scss'], - standalone: false, -}) -export class ClaimComponent { - private route = inject(ActivatedRoute); - private claimManagementService = inject(ClaimManagementService); - private partyManagementService = inject(PartyManagementService); - private allowedClaimStatusesService = inject(AllowedClaimStatusesService); - private dialogService = inject(DialogService); - private log = inject(NotifyLogService); - private destroyRef = inject(DestroyRef); - private domainMetadataFormExtensionsService = inject(DomainMetadataFormExtensionsService); - party$ = (this.route.params as Observable>).pipe( - switchMap(({ partyID }) => - this.partyManagementService - .Get(partyID) - .pipe(progressTo(this.progress$), handleError(this.log.error)), - ), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - claim$ = merge( - this.route.params, - defer(() => this.loadClaim$), - ).pipe( - map(() => this.route.snapshot.params as Record), - switchMap(({ partyID, claimID }) => - this.claimManagementService - .GetClaim(partyID, Number(claimID)) - .pipe(progressTo(this.progress$), handleError(this.log.error)), - ), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - isAllowedChangeStatus$ = this.claim$.pipe( - map( - (claim) => - !!this.allowedClaimStatusesService.getAllowedStatuses(getUnionKey(claim.status)) - .length, - ), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - isLoading$ = inProgressFrom(() => this.progress$, merge(this.claim$, this.party$)); - statusColor = CLAIM_STATUS_COLOR; - - private progress$ = new BehaviorSubject(0); - private loadClaim$ = new Subject(); - - reloadClaim() { - this.loadClaim$.next(); - } - - addModification() { - combineLatest([this.party$, this.claim$]) - .pipe( - first(), - switchMap(([party, claim]) => - this.dialogService - .open(AddModificationDialogComponent, { party, claim }) - .afterClosed(), - ), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe((result) => { - if (result.status === DialogResponseStatus.Success) { - this.reloadClaim(); - } - }); - } - - changeStatus() { - combineLatest([this.party$, this.claim$]) - .pipe( - first(), - switchMap(([party, claim]) => - this.dialogService - .open(ChangeStatusDialogComponent, { partyID: party.id, claim }) - .afterClosed(), - ), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe((result) => { - if (result.status === DialogResponseStatus.Success) { - this.reloadClaim(); - } - }); - } - - createShop() { - combineLatest([this.party$, this.claim$]) - .pipe( - first(), - switchMap(([party, claim]) => - this.dialogService - .open(CreateShopDialogComponent, { party, claim }) - .afterClosed(), - ), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe((result) => { - if (result.status === DialogResponseStatus.Success) { - this.reloadClaim(); - } - }); - } - - createWallet() { - combineLatest([ - this.party$, - this.claim$, - this.domainMetadataFormExtensionsService.generateNextWalletId(), - ]) - .pipe( - first(), - switchMap(([party, claim, nextWalledId]) => - this.dialogService - .open(AddModificationDialogComponent, { - party, - claim, - createModification: { - wallet_modification: { - id: nextWalledId, - modification: { creation: {} }, - }, - }, - }) - .afterClosed(), - ), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe((result) => { - if (result.status === DialogResponseStatus.Success) { - this.reloadClaim(); - } - }); - } -} diff --git a/apps/control-center/src/app/sections/claim/claim.module.ts b/apps/control-center/src/app/sections/claim/claim.module.ts deleted file mode 100644 index 01d963917..000000000 --- a/apps/control-center/src/app/sections/claim/claim.module.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatSelectModule } from '@angular/material/select'; -import { RouterModule } from '@angular/router'; -import { ActionsModule, DialogModule, PipesModule } from '@vality/matez'; -import { ThriftFormModule, ThriftPipesModule, ThriftViewerModule } from '@vality/ng-thrift'; - -import { TimelineModule } from '../../../components/timeline/timeline.module'; -import { PageLayoutModule } from '../../shared'; -import { StatusModule } from '../../shared/components/status/status.module'; -import { DomainThriftFormComponent } from '../../shared/components/thrift-api-crud'; - -import { ClaimRoutingModule } from './claim-routing.module'; -import { ClaimComponent } from './claim.component'; -import { AddModificationDialogComponent } from './components/add-modification-dialog/add-modification-dialog.component'; -import { ChangeStatusDialogComponent } from './components/change-status-dialog/change-status-dialog.component'; -import { ModificationUnitTimelineItemComponent } from './components/modification-unit-timeline-item/modification-unit-timeline-item.component'; -import { ShopModificationTimelineItemComponent } from './components/shop-modification-timeline-item/shop-modification-timeline-item.component'; -import { StatusModificationTimelineItemComponent } from './components/status-modification-timeline-item/status-modification-timeline-item.component'; -import { TimelineItemHeaderComponent } from './components/timeline-item-header/timeline-item-header.component'; -import { TimelineItemLoadingComponent } from './components/timeline-item-loading/timeline-item-loading.component'; - -@NgModule({ - declarations: [ - ClaimComponent, - ModificationUnitTimelineItemComponent, - StatusModificationTimelineItemComponent, - ShopModificationTimelineItemComponent, - AddModificationDialogComponent, - ChangeStatusDialogComponent, - TimelineItemHeaderComponent, - TimelineItemLoadingComponent, - ], - imports: [ - CommonModule, - ClaimRoutingModule, - RouterModule, - TimelineModule, - MatIconModule, - ThriftPipesModule, - MatExpansionModule, - ThriftViewerModule, - MatCardModule, - StatusModule, - MatDividerModule, - MatButtonModule, - ReactiveFormsModule, - MatFormFieldModule, - MatSelectModule, - MatInputModule, - ThriftFormModule, - MatMenuModule, - MatDialogModule, - MatProgressBarModule, - DialogModule, - ActionsModule, - PageLayoutModule, - PipesModule, - DomainThriftFormComponent, - ], -}) -export class ClaimModule {} diff --git a/apps/control-center/src/app/sections/claim/components/add-modification-dialog/add-modification-dialog.component.html b/apps/control-center/src/app/sections/claim/components/add-modification-dialog/add-modification-dialog.component.html deleted file mode 100644 index 8276a1494..000000000 --- a/apps/control-center/src/app/sections/claim/components/add-modification-dialog/add-modification-dialog.component.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/apps/control-center/src/app/sections/claim/components/add-modification-dialog/add-modification-dialog.component.ts b/apps/control-center/src/app/sections/claim/components/add-modification-dialog/add-modification-dialog.component.ts deleted file mode 100644 index 6a5138a33..000000000 --- a/apps/control-center/src/app/sections/claim/components/add-modification-dialog/add-modification-dialog.component.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Component, DestroyRef, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormBuilder, Validators } from '@angular/forms'; -import { Claim, ModificationUnit } from '@vality/domain-proto/claim_management'; -import { Party } from '@vality/domain-proto/domain'; -import { Modification, ModificationChange } from '@vality/domain-proto/internal/claim_management'; -import { - DEFAULT_DIALOG_CONFIG, - DialogSuperclass, - NotifyLogService, - inProgressFrom, - progressTo, -} from '@vality/matez'; -import { BehaviorSubject } from 'rxjs'; -import { DeepPartial } from 'utility-types'; - -import { ClaimManagementService } from '../../../../api/claim-management/claim-management.service'; - -@Component({ - selector: 'cc-add-modification-dialog', - templateUrl: './add-modification-dialog.component.html', - standalone: false, -}) -export class AddModificationDialogComponent extends DialogSuperclass< - AddModificationDialogComponent, - { - party: Party; - claim: Claim; - modificationUnit?: ModificationUnit; - createModification?: DeepPartial; - } -> { - private fb = inject(FormBuilder); - private claimManagementService = inject(ClaimManagementService); - private log = inject(NotifyLogService); - private destroyRef = inject(DestroyRef); - static override defaultDialogConfig = DEFAULT_DIALOG_CONFIG.large; - - control = this.fb.control( - this.dialogData.modificationUnit?.modification || - (this.dialogData.createModification as Modification) || - null, - Validators.required, - ); - isLoading$ = inProgressFrom(() => this.progress$); - - get isUpdate() { - return !!this.dialogData.modificationUnit; - } - - private progress$ = new BehaviorSubject(0); - - add() { - const { party, claim } = this.dialogData; - this.claimManagementService - .UpdateClaim(party.id, claim.id, claim.revision, [this.control.value]) - .pipe(progressTo(this.progress$), takeUntilDestroyed(this.destroyRef)) - .subscribe({ - next: () => { - this.log.success('Modification added successfully'); - this.closeWithSuccess(); - }, - error: this.log.error, - }); - } - - update() { - const { party, claim, modificationUnit } = this.dialogData; - this.claimManagementService - .UpdateModification( - party.id, - claim.id, - claim.revision, - modificationUnit.modification_id, - this.control.value, - ) - .pipe(progressTo(this.progress$), takeUntilDestroyed(this.destroyRef)) - .subscribe({ - next: () => { - this.log.success('Modification updated successfully'); - this.closeWithSuccess(); - }, - error: this.log.error, - }); - } -} diff --git a/apps/control-center/src/app/sections/claim/components/change-status-dialog/change-status-dialog.component.html b/apps/control-center/src/app/sections/claim/components/change-status-dialog/change-status-dialog.component.html deleted file mode 100644 index ea97adc52..000000000 --- a/apps/control-center/src/app/sections/claim/components/change-status-dialog/change-status-dialog.component.html +++ /dev/null @@ -1,62 +0,0 @@ - -
- - - @for (status of statuses; track status) { - {{ status | ngtKeyTitle | titlecase }} - } - - - @if (form.value.status === 'denied') { - - - - } - @if (form.value.status === 'revoked') { - - Reason - - - @for ( - reason of [ - 'Длительное ожидание подключения', - 'Не устраивает комиссия', - 'Большой пакет документов', - 'Не подходит продукт', - 'Нет сплитов', - ]; - track reason - ) { - - {{ reason }} - - } - - - } -
- - - -
diff --git a/apps/control-center/src/app/sections/claim/components/change-status-dialog/change-status-dialog.component.ts b/apps/control-center/src/app/sections/claim/components/change-status-dialog/change-status-dialog.component.ts deleted file mode 100644 index 0c7bd5089..000000000 --- a/apps/control-center/src/app/sections/claim/components/change-status-dialog/change-status-dialog.component.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Component, DestroyRef, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormBuilder, Validators } from '@angular/forms'; -import { Claim, ClaimStatus } from '@vality/domain-proto/claim_management'; -import { - DialogResponseStatus, - DialogSuperclass, - NotifyLogService, - inProgressFrom, - progressTo, -} from '@vality/matez'; -import { getUnionKey } from '@vality/ng-thrift'; -import { BehaviorSubject, Observable } from 'rxjs'; - -import { ClaimManagementService } from '../../../../api/claim-management/claim-management.service'; -import { AllowedClaimStatusesService } from '../../services/allowed-claim-statuses.service'; - -@Component({ - selector: 'cc-change-status-dialog', - templateUrl: './change-status-dialog.component.html', - standalone: false, -}) -export class ChangeStatusDialogComponent extends DialogSuperclass< - ChangeStatusDialogComponent, - { partyID: string; claim: Claim } -> { - private fb = inject(FormBuilder); - private claimManagementService = inject(ClaimManagementService); - private log = inject(NotifyLogService); - private allowedClaimStatusesService = inject(AllowedClaimStatusesService); - private destroyRef = inject(DestroyRef); - form = this.fb.group({ - status: [null as keyof ClaimStatus, Validators.required], - revokeReason: null as string, - denyReason: null as string, - }); - statuses = this.allowedClaimStatusesService.getAllowedStatuses( - getUnionKey(this.dialogData.claim.status), - ); - inProgress$ = inProgressFrom(() => this.progress$); - - private progress$ = new BehaviorSubject(0); - - confirm(): void { - let result$: Observable; - const { value } = this.form; - const { partyID, claim } = this.dialogData; - const params = [partyID, claim.id, claim.revision] as const; - switch (value.status) { - case 'accepted': - result$ = this.claimManagementService.AcceptClaim(...params); - break; - case 'revoked': - result$ = this.claimManagementService.RevokeClaim(...params, value.revokeReason); - break; - case 'denied': - result$ = this.claimManagementService.DenyClaim(...params, value.denyReason); - break; - case 'review': - result$ = this.claimManagementService.RequestClaimReview(...params); - break; - case 'pending': - result$ = this.claimManagementService.RequestClaimChanges(...params); - break; - } - result$.pipe(progressTo(this.progress$), takeUntilDestroyed(this.destroyRef)).subscribe({ - next: () => { - this.dialogRef.close({ status: DialogResponseStatus.Success }); - this.log.success('Status successfully changed'); - }, - error: this.log.error, - }); - } -} diff --git a/apps/control-center/src/app/sections/claim/components/create-shop-dialog/create-shop-dialog.component.html b/apps/control-center/src/app/sections/claim/components/create-shop-dialog/create-shop-dialog.component.html deleted file mode 100644 index 8f8d67e60..000000000 --- a/apps/control-center/src/app/sections/claim/components/create-shop-dialog/create-shop-dialog.component.html +++ /dev/null @@ -1,54 +0,0 @@ - -
- - - - - -
- - - - -
diff --git a/apps/control-center/src/app/sections/claim/components/create-shop-dialog/create-shop-dialog.component.ts b/apps/control-center/src/app/sections/claim/components/create-shop-dialog/create-shop-dialog.component.ts deleted file mode 100644 index 782a37b6b..000000000 --- a/apps/control-center/src/app/sections/claim/components/create-shop-dialog/create-shop-dialog.component.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component, DestroyRef, OnInit, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; -import { MatButton } from '@angular/material/button'; -import { Claim } from '@vality/domain-proto/claim_management'; -import { - CategoryRef, - ContractTemplateRef, - Contractor, - CurrencyRef, - Party, - PaymentInstitutionRef, - RussianBankAccount, - ShopDetails, - ShopLocation, -} from '@vality/domain-proto/domain'; -import { - DEFAULT_DIALOG_CONFIG, - DialogModule, - DialogSuperclass, - NotifyLogService, - progressTo, -} from '@vality/matez'; -import { ThriftFormExtension, isTypeWithAliases } from '@vality/ng-thrift'; -import { BehaviorSubject, of } from 'rxjs'; -import { first } from 'rxjs/operators'; -import short from 'short-uuid'; - -import { ClaimManagementService } from '../../../../api/claim-management'; -import { DomainStoreService } from '../../../../api/domain-config'; -import { DomainThriftFormComponent } from '../../../../shared/components/thrift-api-crud'; - -const DEFAULT_RUSSIAN_BANK_ACCOUNT: RussianBankAccount = { - account: '1', - bank_name: '1', - bank_post_account: '1', - bank_bik: '1', -}; - -const DEFAULT_CONTRACTOR: Contractor = { - legal_entity: { - russian_legal_entity: { - registered_name: '1', - registered_number: '1', - inn: '1', - actual_address: '1', - post_address: '1', - representative_position: '1', - representative_full_name: '1', - representative_document: '1', - russian_bank_account: DEFAULT_RUSSIAN_BANK_ACCOUNT, - }, - }, -}; - -const DEFAULT_SHOP_LOCATION: ShopLocation = { - url: '1', -}; - -@Component({ - selector: 'cc-create-shop-dialog', - imports: [ - DialogModule, - MatButton, - DomainThriftFormComponent, - ReactiveFormsModule, - CommonModule, - ], - templateUrl: './create-shop-dialog.component.html', - styles: ``, -}) -export class CreateShopDialogComponent - extends DialogSuperclass - implements OnInit -{ - private claimManagementService = inject(ClaimManagementService); - private destroyRef = inject(DestroyRef); - private fb = inject(FormBuilder); - private log = inject(NotifyLogService); - private domainStoreService = inject(DomainStoreService); - static override defaultDialogConfig = DEFAULT_DIALOG_CONFIG.large; - extensions: ThriftFormExtension[] = [ - { - determinant: (d) => - of(isTypeWithAliases(d?.trueParent, 'ContractTemplateRef', 'domain')), - extension: () => - of({ - label: 'Contract Template', - }), - }, - { - determinant: (d) => of(isTypeWithAliases(d?.trueParent, 'CategoryRef', 'domain')), - extension: () => - of({ - label: 'Category', - }), - }, - { - determinant: (d) => - of(isTypeWithAliases(d?.trueParent, 'PaymentInstitutionRef', 'domain')), - extension: () => - of({ - label: 'Payment Institution', - }), - }, - { - determinant: (d) => of(isTypeWithAliases(d, 'CurrencySymbolicCode', 'domain')), - extension: () => - of({ - label: 'Currency', - }), - }, - ]; - form = this.fb.group({ - shopDetails: [null as ShopDetails, Validators.required], - contractTemplate: [null as ContractTemplateRef, Validators.required], - currency: [null as CurrencyRef, Validators.required], - category: [null as CategoryRef, Validators.required], - paymentInstitution: [null as PaymentInstitutionRef, Validators.required], - }); - progress$ = new BehaviorSubject(0); - - ngOnInit() { - this.domainStoreService - .getObjects('payment_institution') - .pipe(takeUntilDestroyed(this.destroyRef), first()) - .subscribe((paymentInstitutions) => { - this.form.controls.paymentInstitution.setValue({ - id: paymentInstitutions.sort((a, b) => b.ref.id - a.ref.id)[0].ref.id, - }); - }); - this.domainStoreService - .getObjects('category') - .pipe(takeUntilDestroyed(this.destroyRef), first()) - .subscribe((categories) => { - this.form.controls.category.setValue({ - id: categories.sort((a, b) => b.ref.id - a.ref.id)[0].ref.id, - }); - }); - } - - create() { - const { shopDetails, contractTemplate, category, currency, paymentInstitution } = - this.form.value; - const contractorId = short().uuid(); - const contractId = short().uuid(); - const shopId = short().uuid(); - this.claimManagementService - .UpdateClaim( - this.dialogData.party.id, - this.dialogData.claim.id, - this.dialogData.claim.revision, - [ - { - party_modification: { - contractor_modification: { - id: contractorId, - modification: { - creation: DEFAULT_CONTRACTOR, - }, - }, - }, - }, - { - party_modification: { - contract_modification: { - id: contractId, - modification: { - creation: { - contractor_id: contractorId, - template: contractTemplate, - payment_institution: paymentInstitution, - }, - }, - }, - }, - }, - { - party_modification: { - shop_modification: { - id: shopId, - modification: { - creation: { - details: shopDetails, - category: category, - location: DEFAULT_SHOP_LOCATION, - contract_id: contractId, - }, - }, - }, - }, - }, - { - party_modification: { - shop_modification: { - id: shopId, - modification: { - shop_account_creation: { - currency: currency, - }, - }, - }, - }, - }, - ], - ) - .pipe(progressTo(this.progress$), takeUntilDestroyed(this.destroyRef)) - .subscribe({ - next: () => { - this.log.successOperation('update', 'claim'); - this.closeWithSuccess(); - }, - error: this.log.error, - }); - } -} diff --git a/apps/control-center/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.html b/apps/control-center/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.html deleted file mode 100644 index f21060fc6..000000000 --- a/apps/control-center/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.html +++ /dev/null @@ -1,51 +0,0 @@ - - - {{ icon ?? 'add' }} - - - -
- - - @if (isChangeable) { - - } - - -
-
- @if (hasModificationContent) { - - @if (isLoading) { - - } @else { -
- @if (!content.innerHTML.trim().length) { - - - {{ name | ngtKeyTitle | titlecase }} - - - - } - } -
- } -
diff --git a/apps/control-center/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.ts b/apps/control-center/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.ts deleted file mode 100644 index c71c7532e..000000000 --- a/apps/control-center/src/app/sections/claim/components/modification-unit-timeline-item/modification-unit-timeline-item.component.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { - Component, - DestroyRef, - EventEmitter, - Input, - Output, - booleanAttribute, - inject, -} from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { Claim, ModificationUnit } from '@vality/domain-proto/claim_management'; -import { - ConfirmDialogComponent, - DialogResponseStatus, - DialogService, - NotifyLogService, - getImportValue, - inProgressFrom, - progressTo, -} from '@vality/matez'; -import { ThriftAstMetadata, getUnionValue } from '@vality/ng-thrift'; -import isEmpty from 'lodash-es/isEmpty'; -import { BehaviorSubject, switchMap } from 'rxjs'; -import { filter, first } from 'rxjs/operators'; - -import { ClaimManagementService } from '../../../../api/claim-management/claim-management.service'; -import { PartyManagementService } from '../../../../api/payment-processing/party-management.service'; -import { DomainMetadataViewExtensionsService } from '../../../../shared/components/thrift-api-crud/domain/domain-thrift-viewer/services/domain-metadata-view-extensions/domain-metadata-view-extensions.service'; -import { Color, StatusColor } from '../../../../styles/consts'; -import { getModificationName } from '../../utils/get-modification-name'; -import { AddModificationDialogComponent } from '../add-modification-dialog/add-modification-dialog.component'; - -@Component({ - selector: 'cc-modification-unit-timeline-item', - templateUrl: './modification-unit-timeline-item.component.html', - standalone: false, -}) -export class ModificationUnitTimelineItemComponent { - private partyManagementService = inject(PartyManagementService); - private dialogService = inject(DialogService); - private claimManagementService = inject(ClaimManagementService); - private log = inject(NotifyLogService); - private domainMetadataViewExtensionsService = inject(DomainMetadataViewExtensionsService); - private destroyRef = inject(DestroyRef); - @Input() claim: Claim; - @Input() modificationUnit: ModificationUnit; - - @Input({ transform: booleanAttribute }) isLoading: boolean = false; - @Input({ transform: booleanAttribute }) isChangeable: boolean = false; - @Input() title?: string; - @Input() icon?: string; - @Input() color?: StatusColor | Color; - - @Output() claimChanged = new EventEmitter(); - - isLoading$ = inProgressFrom(() => this.progress$); - metadata$ = getImportValue(import('@vality/domain-proto/metadata.json')); - extensions$ = this.domainMetadataViewExtensionsService.extensions$; - - private progress$ = new BehaviorSubject(0); - - get name() { - return getModificationName(this.modificationUnit.modification); - } - - get hasModificationContent() { - return !isEmpty(getUnionValue(getUnionValue(this.modificationUnit?.modification))); - } - - update() { - this.partyManagementService - .Get(this.claim.party_id) - .pipe( - first(), - switchMap((party) => - this.dialogService - .open(AddModificationDialogComponent, { - party, - claim: this.claim, - modificationUnit: this.modificationUnit, - }) - .afterClosed(), - ), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe((result) => { - if (result.status === DialogResponseStatus.Success) { - this.claimChanged.emit(); - } - }); - } - - remove() { - this.dialogService - .open(ConfirmDialogComponent, { title: 'Confirm deletion' }) - .afterClosed() - .pipe( - filter(({ status }) => status === DialogResponseStatus.Success), - switchMap(() => this.partyManagementService.Get(this.claim.party_id)), - switchMap((party) => - this.claimManagementService.RemoveModification( - party.id, - this.claim.id, - this.claim.revision, - this.modificationUnit.modification_id, - ), - ), - progressTo(this.progress$), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe({ - next: () => { - this.log.success(); - this.claimChanged.emit(); - }, - error: this.log.error, - }); - } -} diff --git a/apps/control-center/src/app/sections/claim/components/modification-unit-timeline-item/utils/get-modification-name.ts b/apps/control-center/src/app/sections/claim/components/modification-unit-timeline-item/utils/get-modification-name.ts deleted file mode 100644 index c9fec70a7..000000000 --- a/apps/control-center/src/app/sections/claim/components/modification-unit-timeline-item/utils/get-modification-name.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { getUnionKey } from '@vality/ng-thrift'; -import isObject from 'lodash-es/isObject'; - -export function getModificationNameParts(modification: unknown): string[] { - const parts: string[] = []; - for (let modKey = '', mod = modification; isObject(mod); ) { - modKey = Object.keys(mod).find((k) => k.endsWith('_modification')); - if (modKey) { - mod = mod[modKey]; - parts.push(modKey); - } else if ('modification' in mod) { - mod = (mod as Record).modification; - modKey = getUnionKey(mod) as string; - if (modKey) { - parts.push(modKey); - } - } else { - break; - } - } - return parts; -} diff --git a/apps/control-center/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.html b/apps/control-center/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.html deleted file mode 100644 index 09d28eeda..000000000 --- a/apps/control-center/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.html +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/apps/control-center/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.ts b/apps/control-center/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.ts deleted file mode 100644 index dd6e4878a..000000000 --- a/apps/control-center/src/app/sections/claim/components/shop-modification-timeline-item/shop-modification-timeline-item.component.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; -import { Claim, ModificationUnit } from '@vality/domain-proto/claim_management'; -import { ComponentChanges } from '@vality/matez'; -import { ReplaySubject } from 'rxjs'; - -@Component({ - selector: 'cc-shop-modification-timeline-item', - templateUrl: './shop-modification-timeline-item.component.html', - standalone: false, -}) -export class ShopModificationTimelineItemComponent implements OnChanges { - @Input() modificationUnit: ModificationUnit; - @Input() claim: Claim; - @Output() claimChanged = new EventEmitter(); - - private modificationUnit$ = new ReplaySubject(1); - - ngOnChanges({ modificationUnit }: ComponentChanges) { - if (modificationUnit) { - this.modificationUnit$.next(modificationUnit.currentValue); - } - } -} diff --git a/apps/control-center/src/app/sections/claim/components/status-modification-timeline-item/status-modification-timeline-item.component.html b/apps/control-center/src/app/sections/claim/components/status-modification-timeline-item/status-modification-timeline-item.component.html deleted file mode 100644 index cd46eae1a..000000000 --- a/apps/control-center/src/app/sections/claim/components/status-modification-timeline-item/status-modification-timeline-item.component.html +++ /dev/null @@ -1,16 +0,0 @@ - - @if (reason) { - - - {{ reason }} - - - } - diff --git a/apps/control-center/src/app/sections/claim/components/status-modification-timeline-item/status-modification-timeline-item.component.ts b/apps/control-center/src/app/sections/claim/components/status-modification-timeline-item/status-modification-timeline-item.component.ts deleted file mode 100644 index 1c502e914..000000000 --- a/apps/control-center/src/app/sections/claim/components/status-modification-timeline-item/status-modification-timeline-item.component.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { Claim, ClaimStatus, ModificationUnit } from '@vality/domain-proto/claim_management'; -import { getUnionKey, getUnionValue } from '@vality/ng-thrift'; - -import { CLAIM_STATUS_COLOR } from '../../types/claim-status-color'; - -@Component({ - selector: 'cc-status-modification-timeline-item', - templateUrl: './status-modification-timeline-item.component.html', - standalone: false, -}) -export class StatusModificationTimelineItemComponent { - @Input() modificationUnit: ModificationUnit; - @Input() claim: Claim; - @Output() claimChanged = new EventEmitter(); - - get statusModification() { - return this.modificationUnit.modification.claim_modification.status_modification; - } - - get status() { - return getUnionKey(this.statusModification.status); - } - - get reason() { - const statusValue = getUnionValue(this.statusModification.status); - return 'reason' in statusValue ? statusValue.reason : null; - } - - claimStatusColor = CLAIM_STATUS_COLOR; - claimStatusIconName: Record = { - pending_acceptance: 'check', - accepted: 'check', - pending: 'double_arrow', - review: 'double_arrow', - revoked: 'block', - denied: 'block', - }; -} diff --git a/apps/control-center/src/app/sections/claim/components/timeline-item-header/timeline-item-header.component.html b/apps/control-center/src/app/sections/claim/components/timeline-item-header/timeline-item-header.component.html deleted file mode 100644 index 7e2ac4fbf..000000000 --- a/apps/control-center/src/app/sections/claim/components/timeline-item-header/timeline-item-header.component.html +++ /dev/null @@ -1,17 +0,0 @@ -{{ username }} - - {{ text }} - @if (createdAt) { - at {{ createdAt | date: 'dd.MM.yyyy HH:mm:ss' }} ({{ - createdAt | humanizedDuration: { hasAgoEnding: true } - }}) - } - -@if (outdated) { - (Outdated) -} -@if (removed) { - (Removed) -} diff --git a/apps/control-center/src/app/sections/claim/components/timeline-item-header/timeline-item-header.component.scss b/apps/control-center/src/app/sections/claim/components/timeline-item-header/timeline-item-header.component.scss deleted file mode 100644 index 1a80a2600..000000000 --- a/apps/control-center/src/app/sections/claim/components/timeline-item-header/timeline-item-header.component.scss +++ /dev/null @@ -1,4 +0,0 @@ -.outdated, -.removed { - text-decoration: line-through; -} diff --git a/apps/control-center/src/app/sections/claim/components/timeline-item-header/timeline-item-header.component.ts b/apps/control-center/src/app/sections/claim/components/timeline-item-header/timeline-item-header.component.ts deleted file mode 100644 index f8da41477..000000000 --- a/apps/control-center/src/app/sections/claim/components/timeline-item-header/timeline-item-header.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, Input } from '@angular/core'; - -@Component({ - selector: 'cc-timeline-item-header', - templateUrl: 'timeline-item-header.component.html', - styleUrls: ['./timeline-item-header.component.scss'], - standalone: false, -}) -export class TimelineItemHeaderComponent { - @Input() - username: string; - - @Input() - createdAt: string; - - @Input() - text: string; - - @Input() - outdated?: boolean; - - @Input() - removed?: boolean; -} diff --git a/apps/control-center/src/app/sections/claim/components/timeline-item-loading/timeline-item-loading.component.html b/apps/control-center/src/app/sections/claim/components/timeline-item-loading/timeline-item-loading.component.html deleted file mode 100644 index 44c994690..000000000 --- a/apps/control-center/src/app/sections/claim/components/timeline-item-loading/timeline-item-loading.component.html +++ /dev/null @@ -1 +0,0 @@ -

Loading...

diff --git a/apps/control-center/src/app/sections/claim/components/timeline-item-loading/timeline-item-loading.component.ts b/apps/control-center/src/app/sections/claim/components/timeline-item-loading/timeline-item-loading.component.ts deleted file mode 100644 index 96f1a953d..000000000 --- a/apps/control-center/src/app/sections/claim/components/timeline-item-loading/timeline-item-loading.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; - -@Component({ - selector: 'cc-timeline-item-loading', - templateUrl: 'timeline-item-loading.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class TimelineItemLoadingComponent {} diff --git a/apps/control-center/src/app/sections/claim/index.ts b/apps/control-center/src/app/sections/claim/index.ts deleted file mode 100644 index 1b27ecb49..000000000 --- a/apps/control-center/src/app/sections/claim/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './claim.module'; diff --git a/apps/control-center/src/app/sections/claim/routing-config.ts b/apps/control-center/src/app/sections/claim/routing-config.ts deleted file mode 100644 index 0f4e62df1..000000000 --- a/apps/control-center/src/app/sections/claim/routing-config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { RoutingConfig, Services } from '../../shared/services'; - -export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.ClaimManagement], -}; diff --git a/apps/control-center/src/app/sections/claim/services/allowed-claim-statuses.service.ts b/apps/control-center/src/app/sections/claim/services/allowed-claim-statuses.service.ts deleted file mode 100644 index c402a0c46..000000000 --- a/apps/control-center/src/app/sections/claim/services/allowed-claim-statuses.service.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { ClaimStatus } from '@vality/domain-proto/claim_management'; - -import { CLAIM_STATUSES } from '../../../api/claim-management'; -import { AppAuthGuardService, ClaimManagementRole } from '../../../shared/services'; - -const CLAIM_STATUS_ROLES: { [N in keyof ClaimStatus]: ClaimManagementRole[] } = { - accepted: [ClaimManagementRole.AcceptClaim], - denied: [ClaimManagementRole.DenyClaim], - review: [ClaimManagementRole.RequestClaimReview], - revoked: [ClaimManagementRole.RevokeClaim], - pending: [ClaimManagementRole.RequestClaimChanges], -}; - -@Injectable({ - providedIn: 'root', -}) -export class AllowedClaimStatusesService { - private appAuthGuardService = inject(AppAuthGuardService); - - getAllowedStatuses(currentClaimStatus: keyof ClaimStatus) { - return CLAIM_STATUSES.filter((status) => this.isAllowed(status, currentClaimStatus)); - } - - private isAllowed(status: keyof ClaimStatus, currentClaimStatus: keyof ClaimStatus): boolean { - const excludedStatuses: (keyof ClaimStatus)[] = [currentClaimStatus, 'pending_acceptance']; - const includedCurrentStatuses: (keyof ClaimStatus)[] = ['pending', 'review']; - if ( - excludedStatuses.includes(status) || - !includedCurrentStatuses.includes(currentClaimStatus) - ) { - return false; - } - return this.appAuthGuardService.userHasRoles(CLAIM_STATUS_ROLES[status]); - } -} diff --git a/apps/control-center/src/app/sections/claim/types/claim-status-color.ts b/apps/control-center/src/app/sections/claim/types/claim-status-color.ts deleted file mode 100644 index 3e0dd5473..000000000 --- a/apps/control-center/src/app/sections/claim/types/claim-status-color.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ClaimStatus } from '@vality/domain-proto/claim_management'; - -import { StatusColor } from '../../../styles/consts'; - -export const CLAIM_STATUS_COLOR: Record = { - pending_acceptance: null, - accepted: StatusColor.Success, - pending: StatusColor.Pending, - review: StatusColor.Pending, - revoked: StatusColor.Warn, - denied: StatusColor.Warn, -}; diff --git a/apps/control-center/src/app/sections/claim/utils/get-modification-name.ts b/apps/control-center/src/app/sections/claim/utils/get-modification-name.ts deleted file mode 100644 index 8693433ba..000000000 --- a/apps/control-center/src/app/sections/claim/utils/get-modification-name.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Modification } from '@vality/domain-proto/claim_management'; -import { getUnionKey } from '@vality/ng-thrift'; -import isObject from 'lodash-es/isObject'; - -import { MODIFICATIONS_NAME_TREE } from './types/modifications-name-tree'; - -export function getModificationName(modification: Modification) { - let currentValue: unknown = modification; - let currentName: unknown = MODIFICATIONS_NAME_TREE; - while (isObject(currentName) && isObject(currentValue)) { - const key = Object.keys(currentValue).find((k) => Object.keys(currentName).includes(k)); - currentValue = currentValue?.[key]; - currentName = currentName?.[key]; - } - return typeof currentName === 'string' - ? `${currentName} Modification` - : getUnionKey(modification); -} diff --git a/apps/control-center/src/app/sections/claim/utils/types/modifications-name-tree.ts b/apps/control-center/src/app/sections/claim/utils/types/modifications-name-tree.ts deleted file mode 100644 index b22b8c11d..000000000 --- a/apps/control-center/src/app/sections/claim/utils/types/modifications-name-tree.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Modification } from '@vality/domain-proto/claim_management'; - -type ModificationsNameTree = { [N in keyof T]?: ModificationsNameTree | string }; - -export const MODIFICATIONS_NAME_TREE: ModificationsNameTree = { - claim_modification: { - document_modification: { - modification: { - creation: 'Document Creation', - changed: 'Document Change', - }, - }, - file_modification: { - modification: { - creation: 'File Creation', - deletion: 'File Deletion', - changed: 'File Change', - }, - }, - comment_modification: { - modification: { - creation: 'Comment Creation', - changed: 'Comment Change', - deletion: 'Comment Deletion', - }, - }, - status_modification: { - modification: { - change: 'Status Change', - }, - }, - external_info_modification: 'External Info', - }, - party_modification: { - contractor_modification: { - modification: { - creation: 'Contractor Creation', - identification_level_modification: 'Contractor Identification Level', - }, - }, - contract_modification: { - modification: { - creation: 'Contract Creation', - termination: 'Contract Termination', - adjustment_modification: { - modification: { - creation: 'Contract Adjustment Creation', - }, - }, - payout_tool_modification: { - modification: { - creation: 'Contract Payout Tool Creation', - info_modification: 'Contract Payout Tool Info', - }, - }, - legal_agreement_binding: 'Contract Legal Agreement Binding', - report_preferences_modification: 'Contract Report Preferences', - contractor_modification: 'Contract Contractor', - }, - }, - shop_modification: { - modification: { - creation: 'Shop Creation', - category_modification: 'Shop Category', - details_modification: 'Shop Details', - contract_modification: 'Shop Contract', - payout_tool_modification: 'Shop Payout Tool', - location_modification: 'Shop Location', - shop_account_creation: 'Shop Account Creation', - payout_schedule_modification: 'Shop Schedule', - cash_register_modification_unit: { - modification: { - creation: 'Shop Cash Register Creation', - }, - }, - }, - }, - wallet_modification: { - modification: { - creation: 'Old Wallet Creation', - account_creation: 'Old Wallet Account Creation', - }, - }, - additional_info_modification: 'Party Additional Info', - }, - identity_modification: { - modification: { - creation: 'Identity Creation', - }, - }, - wallet_modification: { - modification: { - creation: 'Wallet Creation', - }, - }, -}; diff --git a/apps/control-center/src/app/sections/claims/claims-routing.module.ts b/apps/control-center/src/app/sections/claims/claims-routing.module.ts deleted file mode 100644 index ce395fc1a..000000000 --- a/apps/control-center/src/app/sections/claims/claims-routing.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { AppAuthGuardService } from '../../shared/services'; - -import { ClaimsComponent } from './claims.component'; -import { ROUTING_CONFIG } from './routing-config'; - -@NgModule({ - imports: [ - RouterModule.forChild([ - { - path: '', - component: ClaimsComponent, - canActivate: [AppAuthGuardService], - data: ROUTING_CONFIG, - }, - ]), - ], - exports: [RouterModule], -}) -export class ClaimsComponentRouting {} diff --git a/apps/control-center/src/app/sections/claims/claims-table/claims-table.component.html b/apps/control-center/src/app/sections/claims/claims-table/claims-table.component.html deleted file mode 100644 index 4247718fd..000000000 --- a/apps/control-center/src/app/sections/claims/claims-table/claims-table.component.html +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/apps/control-center/src/app/sections/claims/claims-table/claims-table.component.scss b/apps/control-center/src/app/sections/claims/claims-table/claims-table.component.scss deleted file mode 100644 index 832cae033..000000000 --- a/apps/control-center/src/app/sections/claims/claims-table/claims-table.component.scss +++ /dev/null @@ -1,11 +0,0 @@ -table { - width: 100%; -} - -.action-cell { - width: 8px; -} - -.party-id { - opacity: 0.54; -} diff --git a/apps/control-center/src/app/sections/claims/claims-table/claims-table.component.ts b/apps/control-center/src/app/sections/claims/claims-table/claims-table.component.ts deleted file mode 100644 index 0114b3b94..000000000 --- a/apps/control-center/src/app/sections/claims/claims-table/claims-table.component.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Component, EventEmitter, Input, Output, booleanAttribute, input } from '@angular/core'; -import { toObservable } from '@angular/core/rxjs-interop'; -import { Claim } from '@vality/domain-proto/claim_management'; -import { Column, LoadOptions, createMenuColumn } from '@vality/matez'; -import { getUnionKey } from '@vality/ng-thrift'; -import { startCase } from 'lodash-es'; - -import { createPartyColumn } from '../../../shared'; - -@Component({ - selector: 'cc-claims-table', - templateUrl: './claims-table.component.html', - styleUrls: ['./claims-table.component.scss'], - standalone: false, -}) -export class ClaimsTableComponent { - @Input() data!: Claim[]; - @Input() isLoading?: boolean | null; - @Input() hasMore?: boolean | null; - noParty = input(false, { transform: booleanAttribute }); - - @Output() update = new EventEmitter(); - @Output() more = new EventEmitter(); - - columns: Column[] = [ - { field: 'id', cell: (d) => ({ link: () => `/party/${d.party_id}/claim/${d.id}` }) }, - createPartyColumn((d) => ({ id: d.party_id }), { hidden: toObservable(this.noParty) }), - { - field: 'status', - cell: (d) => ({ - value: startCase(getUnionKey(d.status)), - color: ( - { - pending: 'pending', - review: 'pending', - pending_acceptance: 'pending', - accepted: 'success', - denied: 'warn', - revoked: 'neutral', - } as const - )[getUnionKey(d.status)], - }), - }, - { field: 'revision' }, - { field: 'created_at', cell: { type: 'datetime' } }, - { field: 'updated_at', cell: { type: 'datetime' } }, - createMenuColumn((d) => ({ - items: [ - { - label: 'Details', - link: () => `/party/${d.party_id}/claim/${d.id}`, - }, - ], - })), - ]; -} diff --git a/apps/control-center/src/app/sections/claims/claims.component.html b/apps/control-center/src/app/sections/claims/claims.component.html deleted file mode 100644 index 48c68a1bb..000000000 --- a/apps/control-center/src/app/sections/claims/claims.component.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - @if (!(party$ | async)) { - - } - - - - - - @for (status of claimStatuses; track status) { - {{ - status | ngtKeyTitle | titlecase - }} - } - - - - - - - - diff --git a/apps/control-center/src/app/sections/claims/claims.component.ts b/apps/control-center/src/app/sections/claims/claims.component.ts deleted file mode 100644 index a460fc1b3..000000000 --- a/apps/control-center/src/app/sections/claims/claims.component.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Component, DestroyRef, OnInit, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { NonNullableFormBuilder } from '@angular/forms'; -import { PartyID } from '@vality/domain-proto/domain'; -import { - DialogService, - LoadOptions, - QueryParamsService, - clean, - countChanged, - debounceTimeWithFirst, - getValueChanges, -} from '@vality/matez'; -import { map, shareReplay, take } from 'rxjs/operators'; - -import { CLAIM_STATUSES } from '../../api/claim-management'; -import { DEBOUNCE_TIME_MS } from '../../tokens'; -import { PartyStoreService } from '../party'; - -import { CreateClaimDialogComponent } from './components/create-claim-dialog/create-claim-dialog.component'; -import { FetchClaimsService } from './fetch-claims.service'; - -@Component({ - templateUrl: './claims.component.html', - providers: [PartyStoreService], - standalone: false, -}) -export class ClaimsComponent implements OnInit { - private fetchClaimsService = inject(FetchClaimsService); - private dialogService = inject(DialogService); - private fb = inject(NonNullableFormBuilder); - private qp = inject>( - QueryParamsService, - ); - private destroyRef = inject(DestroyRef); - private partyStoreService = inject(PartyStoreService); - private debounceTimeMs = inject(DEBOUNCE_TIME_MS); - isLoading$ = this.fetchClaimsService.isLoading$; - claims$ = this.fetchClaimsService.result$; - hasMore$ = this.fetchClaimsService.hasMore$; - claimStatuses = CLAIM_STATUSES; - filtersForm = this.fb.group({ - party_id: undefined as string, - claim_id: undefined as number, - statuses: [[] as string[]], - }); - active$ = getValueChanges(this.filtersForm).pipe( - map((v) => countChanged(this.initFilters, v)), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - party$ = this.partyStoreService.party$; - - private selectedPartyId: PartyID; - private initFilters = this.filtersForm.value; - - ngOnInit(): void { - this.filtersForm.patchValue(this.qp.params); - getValueChanges(this.filtersForm) - .pipe(debounceTimeWithFirst(this.debounceTimeMs), takeUntilDestroyed(this.destroyRef)) - .subscribe(() => { - const filters = clean(this.filtersForm.value); - void this.qp.set(filters); - this.load(filters); - }); - } - - load(filters: ClaimsComponent['filtersForm']['value'], options?: LoadOptions): void { - this.partyStoreService.party$.pipe(take(1)).subscribe((p) => { - this.fetchClaimsService.load( - clean({ - party_id: p ? p.id : undefined, - ...filters, - statuses: filters.statuses?.map((status) => ({ [status]: {} })) || [], - }), - options, - ); - }); - } - - reload(options?: LoadOptions) { - this.fetchClaimsService.reload(options); - } - - more(): void { - this.fetchClaimsService.more(); - } - - create() { - this.dialogService.open(CreateClaimDialogComponent, { partyId: this.selectedPartyId }); - } -} diff --git a/apps/control-center/src/app/sections/claims/claims.module.ts b/apps/control-center/src/app/sections/claims/claims.module.ts deleted file mode 100644 index bfd25408b..000000000 --- a/apps/control-center/src/app/sections/claims/claims.module.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatExpansionModule } from '@angular/material/expansion'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatSelectModule } from '@angular/material/select'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { ActionsModule, DialogModule, FiltersModule, TableModule } from '@vality/matez'; -import { ThriftPipesModule } from '@vality/ng-thrift'; - -import { PageLayoutModule } from '../../shared/components'; -import { MerchantFieldModule } from '../../shared/components/merchant-field/merchant-field.module'; - -import { ClaimsComponentRouting } from './claims-routing.module'; -import { ClaimsTableComponent } from './claims-table/claims-table.component'; -import { ClaimsComponent } from './claims.component'; -import { CreateClaimDialogComponent } from './components/create-claim-dialog/create-claim-dialog.component'; - -@NgModule({ - imports: [ - CommonModule, - ClaimsComponentRouting, - MatButtonModule, - MatCardModule, - MatDialogModule, - MatFormFieldModule, - MatIconModule, - MatInputModule, - MatMenuModule, - MatProgressBarModule, - MatSelectModule, - MatSnackBarModule, - ReactiveFormsModule, - MatExpansionModule, - ThriftPipesModule, - ActionsModule, - DialogModule, - MerchantFieldModule, - PageLayoutModule, - TableModule, - FiltersModule, - ], - declarations: [ClaimsComponent, ClaimsTableComponent, CreateClaimDialogComponent], -}) -export class ClaimsModule {} diff --git a/apps/control-center/src/app/sections/claims/components/create-claim-dialog/create-claim-dialog.component.html b/apps/control-center/src/app/sections/claims/components/create-claim-dialog/create-claim-dialog.component.html deleted file mode 100644 index 2499f3dbf..000000000 --- a/apps/control-center/src/app/sections/claims/components/create-claim-dialog/create-claim-dialog.component.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/apps/control-center/src/app/sections/claims/components/create-claim-dialog/create-claim-dialog.component.ts b/apps/control-center/src/app/sections/claims/components/create-claim-dialog/create-claim-dialog.component.ts deleted file mode 100644 index 3526f8d4f..000000000 --- a/apps/control-center/src/app/sections/claims/components/create-claim-dialog/create-claim-dialog.component.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Component, DestroyRef, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormControl, Validators } from '@angular/forms'; -import { Router } from '@angular/router'; -import { DialogSuperclass, NotifyLogService, progressTo } from '@vality/matez'; -import { BehaviorSubject } from 'rxjs'; - -import { ClaimManagementService } from '../../../../api/claim-management/claim-management.service'; - -@Component({ - selector: 'cc-create-claim-dialog', - templateUrl: './create-claim-dialog.component.html', - standalone: false, -}) -export class CreateClaimDialogComponent extends DialogSuperclass< - CreateClaimDialogComponent, - { partyId: string } -> { - private claimService = inject(ClaimManagementService); - private log = inject(NotifyLogService); - private router = inject(Router); - private destroyRef = inject(DestroyRef); - control = new FormControl(this.dialogData.partyId, Validators.required); - progress$ = new BehaviorSubject(0); - - create() { - this.claimService - .CreateClaim(this.control.value, []) - .pipe(progressTo(this.progress$), takeUntilDestroyed(this.destroyRef)) - .subscribe({ - next: (claim) => { - this.log.successOperation('create', 'claim'); - void this.router.navigate([`party/${claim.party_id}/claim/${claim.id}`]); - this.closeWithSuccess(); - }, - error: (err) => { - this.log.errorOperation(err, 'create', 'claim'); - }, - }); - } -} diff --git a/apps/control-center/src/app/sections/claims/fetch-claims.service.ts b/apps/control-center/src/app/sections/claims/fetch-claims.service.ts deleted file mode 100644 index 0565ed6fb..000000000 --- a/apps/control-center/src/app/sections/claims/fetch-claims.service.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { Claim, ClaimSearchQuery } from '@vality/domain-proto/claim_management'; -import { FetchOptions, FetchResult, FetchSuperclass, NotifyLogService } from '@vality/matez'; -import { Observable, of } from 'rxjs'; -import { catchError, map } from 'rxjs/operators'; - -import { ClaimManagementService } from '../../api/claim-management/claim-management.service'; - -@Injectable({ providedIn: 'root' }) -export class FetchClaimsService extends FetchSuperclass< - Claim, - Omit -> { - private claimManagementService = inject(ClaimManagementService); - private log = inject(NotifyLogService); - - protected fetch( - params: Omit, - { size, continuationToken }: FetchOptions, - ): Observable> { - return this.claimManagementService - .SearchClaims({ - ...params, - continuation_token: continuationToken, - limit: size, - }) - .pipe( - map((r) => ({ - result: r.result, - continuationToken: r.continuation_token, - })), - catchError((err) => { - this.log.errorOperation(err, 'receive', 'claims'); - return of({ result: [] }); - }), - ); - } -} diff --git a/apps/control-center/src/app/sections/claims/index.ts b/apps/control-center/src/app/sections/claims/index.ts deleted file mode 100644 index 395f398e0..000000000 --- a/apps/control-center/src/app/sections/claims/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './claims.module'; diff --git a/apps/control-center/src/app/sections/claims/routing-config.ts b/apps/control-center/src/app/sections/claims/routing-config.ts deleted file mode 100644 index 0f4e62df1..000000000 --- a/apps/control-center/src/app/sections/claims/routing-config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { RoutingConfig, Services } from '../../shared/services'; - -export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.ClaimManagement], -}; diff --git a/apps/control-center/src/app/sections/deposit-details/deposit-details-routing.module.ts b/apps/control-center/src/app/sections/deposit-details/deposit-details-routing.module.ts index 2fd99afe9..ad69e6337 100644 --- a/apps/control-center/src/app/sections/deposit-details/deposit-details-routing.module.ts +++ b/apps/control-center/src/app/sections/deposit-details/deposit-details-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { DepositDetailsComponent } from './deposit-details.component'; import { ROUTING_CONFIG } from './routing-config'; @@ -12,7 +12,7 @@ import { ROUTING_CONFIG } from './routing-config'; { path: '', component: DepositDetailsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/deposit-details/deposit-details.component.html b/apps/control-center/src/app/sections/deposit-details/deposit-details.component.html index 79313d88f..ef3bafd88 100644 --- a/apps/control-center/src/app/sections/deposit-details/deposit-details.component.html +++ b/apps/control-center/src/app/sections/deposit-details/deposit-details.component.html @@ -12,7 +12,4 @@ } - @if (deposit$ | async; as deposit) { - - }
diff --git a/apps/control-center/src/app/sections/deposit-details/deposit-details.component.ts b/apps/control-center/src/app/sections/deposit-details/deposit-details.component.ts index 146b02355..c4145ca8c 100644 --- a/apps/control-center/src/app/sections/deposit-details/deposit-details.component.ts +++ b/apps/control-center/src/app/sections/deposit-details/deposit-details.component.ts @@ -1,11 +1,10 @@ import { formatDate } from '@angular/common'; import { ChangeDetectionStrategy, Component, LOCALE_ID, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { DepositStatus, RevertStatus } from '@vality/fistful-proto/fistful_stat'; -import { Timestamp } from '@vality/fistful-proto/internal/base'; -import { formatCurrency, getImportValue } from '@vality/matez'; +import { metadata$ } from '@vality/fistful-proto'; +import { DepositStatus } from '@vality/fistful-proto/fistful_stat'; +import { formatCurrency } from '@vality/matez'; import { - ThriftAstMetadata, ThriftViewExtension, ThriftViewExtensionResult, getUnionKey, @@ -16,7 +15,7 @@ import startCase from 'lodash-es/startCase'; import { Observable, of } from 'rxjs'; import { map, take } from 'rxjs/operators'; -import { ManagementService } from '../../api/wallet'; +import { DomainObjectsStoreService } from '../../api/domain-config'; import { AmountCurrencyService } from '../../shared/services'; import { FetchSourcesService } from '../sources'; @@ -33,16 +32,17 @@ export class DepositDetailsComponent implements OnInit { private route = inject(ActivatedRoute); private _locale = inject(LOCALE_ID); private amountCurrencyService = inject(AmountCurrencyService); - private walletManagementService = inject(ManagementService); + private domainObjectsStoreService = inject(DomainObjectsStoreService); private fetchSourcesService = inject(FetchSourcesService); + deposit$ = this.fetchDepositService.deposit$; isLoading$ = this.fetchDepositService.isLoading$; - metadata$ = getImportValue(import('@vality/fistful-proto/metadata.json')); + metadata$ = metadata$; extensions$: Observable = this.fetchDepositService.deposit$.pipe( map((deposit) => [ { determinant: (d) => of(isTypeWithAliases(d, 'Timestamp', 'base')), - extension: (_, value: Timestamp) => + extension: (_, value: string) => of({ value: formatDate(value, 'dd.MM.yyyy HH:mm:ss', 'en') }), }, { @@ -82,35 +82,23 @@ export class DepositDetailsComponent implements OnInit { )[getUnionKey(status)], }), }, - { - determinant: (d) => of(isTypeWithAliases(d, 'RevertStatus', 'fistful_stat')), - extension: (_, status: RevertStatus, viewValue: string) => - of({ - value: startCase(viewValue), - color: ( - { - [1]: 'pending', - [2]: 'success', - [0]: 'neutral', - } as const - )[status], - }), - }, { determinant: (d) => of(isTypeWithAliases(d, 'WalletID', 'fistful_stat')), extension: (_, id: string) => - this.walletManagementService.Get(id, {}).pipe( - map( - (wallet): ThriftViewExtensionResult => ({ - value: wallet.name, - tooltip: wallet.id, - link: [ - ['/wallets'], - { queryParams: { wallet_id: JSON.stringify([wallet.id]) } }, - ], - }), + this.domainObjectsStoreService + .getLimitedObject({ wallet_config: { id } }) + .value$.pipe( + map( + (wallet): ThriftViewExtensionResult => ({ + value: wallet.name, + tooltip: id, + link: [ + ['/wallets'], + { queryParams: { wallet_id: JSON.stringify([id]) } }, + ], + }), + ), ), - ), }, { determinant: (d) => of(isTypeWithAliases(d, 'SourceID', 'fistful_stat')), diff --git a/apps/control-center/src/app/sections/deposit-details/deposit-details.module.ts b/apps/control-center/src/app/sections/deposit-details/deposit-details.module.ts index 2285d122b..5558bb17a 100644 --- a/apps/control-center/src/app/sections/deposit-details/deposit-details.module.ts +++ b/apps/control-center/src/app/sections/deposit-details/deposit-details.module.ts @@ -11,7 +11,6 @@ import { PageLayoutModule, StatusModule } from '../../shared/components'; import { DepositDetailsRoutingModule } from './deposit-details-routing.module'; import { DepositDetailsComponent } from './deposit-details.component'; -import { RevertsModule } from './reverts/reverts.module'; @NgModule({ imports: [ @@ -23,7 +22,6 @@ import { RevertsModule } from './reverts/reverts.module'; MatProgressSpinnerModule, MatButtonModule, MatDialogModule, - RevertsModule, PageLayoutModule, ThriftViewerModule, ], diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.component.html b/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.component.html deleted file mode 100644 index a60472982..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.component.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.component.scss b/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.component.scss deleted file mode 100644 index 6bf4f789a..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -mat-dialog-actions { - margin-bottom: -24px; - padding: 0; -} - -button { - height: 36px; -} diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.component.ts b/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.component.ts deleted file mode 100644 index 38e771e4d..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.component.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - DestroyRef, - TemplateRef, - inject, - viewChild, -} from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormControl } from '@angular/forms'; -import { DepositParams } from '@vality/fistful-proto/deposit'; -import { Revert } from '@vality/fistful-proto/internal/deposit_revert'; -import { DialogSuperclass, NotifyLogService, clean } from '@vality/matez'; -import { ThriftFormExtension, isTypeWithAliases } from '@vality/ng-thrift'; -import { BehaviorSubject, of } from 'rxjs'; -import { Overwrite } from 'utility-types'; - -import { Cash } from '../../../../../components/cash-field'; -import { DepositManagementService } from '../../../../api/deposit/deposit-management.service'; -import { UserInfoBasedIdGeneratorService } from '../../../../shared/services'; - -import { CreateRevertDialogConfig } from './types/create-revert-dialog-config'; - -@Component({ - templateUrl: 'create-revert-dialog.component.html', - styleUrls: ['create-revert-dialog.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class CreateRevertDialogComponent extends DialogSuperclass< - CreateRevertDialogComponent, - CreateRevertDialogConfig, - Revert -> { - private depositManagementService = inject(DepositManagementService); - private idGenerator = inject(UserInfoBasedIdGeneratorService); - private log = inject(NotifyLogService); - private destroyRef = inject(DestroyRef); - control = new FormControl({ - id: this.idGenerator.getUsernameBasedId(), - body: { currencyCode: this.dialogData.currency }, - } as Overwrite); - progress$ = new BehaviorSubject(0); - cashTemplate = viewChild>('cashTemplate'); - extensions: ThriftFormExtension[] = [ - { - determinant: (data) => of(isTypeWithAliases(data, 'RevertID', 'deposit_revert')), - extension: () => of({ hidden: true }), - }, - { - determinant: (data) => of(isTypeWithAliases(data, 'Cash', 'base')), - extension: () => of({ template: this.cashTemplate() }), - }, - ]; - - createRevert() { - const { body, ...value } = this.control.value; - this.depositManagementService - .CreateRevert( - this.dialogData.depositID, - clean( - { - ...value, - body: { - currency: { symbolic_code: body.currencyCode }, - amount: body.amount, - }, - }, - false, - true, - ), - ) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe({ - next: (revert) => { - this.log.successOperation('create', 'revert'); - this.closeWithSuccess(revert); - }, - error: (err) => { - this.log.errorOperation(err, 'create', 'revert'); - }, - }); - } -} diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.module.ts b/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.module.ts deleted file mode 100644 index 5ed593b6c..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/create-revert-dialog.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { DialogModule } from '@vality/matez'; - -import { CashFieldComponent } from '../../../../../components/cash-field'; -import { FistfulThriftFormComponent } from '../../../../shared/components/fistful-thrift-form'; -import { UserInfoBasedIdGeneratorModule } from '../../../../shared/services/user-info-based-id-generator/user-info-based-id-generator.module'; - -import { CreateRevertDialogComponent } from './create-revert-dialog.component'; - -@NgModule({ - imports: [ - CommonModule, - ReactiveFormsModule, - MatDialogModule, - MatFormFieldModule, - MatProgressBarModule, - MatButtonModule, - MatInputModule, - UserInfoBasedIdGeneratorModule, - DialogModule, - FistfulThriftFormComponent, - CashFieldComponent, - ], - declarations: [CreateRevertDialogComponent], -}) -export class CreateRevertDialogModule {} diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/types/create-revert-dialog-config.ts b/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/types/create-revert-dialog-config.ts deleted file mode 100644 index 031d1e976..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/create-revert-dialog/types/create-revert-dialog-config.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface CreateRevertDialogConfig { - depositID: string; - currency: string; -} diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/reverts.component.html b/apps/control-center/src/app/sections/deposit-details/reverts/reverts.component.html deleted file mode 100644 index 626a3d211..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/reverts.component.html +++ /dev/null @@ -1,21 +0,0 @@ -
-

Reverts

- - - - - -
diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/reverts.component.scss b/apps/control-center/src/app/sections/deposit-details/reverts/reverts.component.scss deleted file mode 100644 index bdab8c15c..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/reverts.component.scss +++ /dev/null @@ -1,7 +0,0 @@ -button { - height: 36px; -} - -h1 { - margin: 0; -} diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/reverts.component.ts b/apps/control-center/src/app/sections/deposit-details/reverts/reverts.component.ts deleted file mode 100644 index 8774f0273..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/reverts.component.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit, inject } from '@angular/core'; -import { DepositStatus, StatDeposit, StatDepositRevert } from '@vality/fistful-proto/fistful_stat'; -import { Column, DialogService, UpdateOptions } from '@vality/matez'; -import { getUnionKey } from '@vality/ng-thrift'; -import startCase from 'lodash-es/startCase'; -import { filter } from 'rxjs/operators'; - -import { createCurrencyColumn } from '../../../shared'; - -import { CreateRevertDialogComponent } from './create-revert-dialog/create-revert-dialog.component'; -import { FetchRevertsService } from './services/fetch-reverts/fetch-reverts.service'; - -@Component({ - selector: 'cc-reverts', - templateUrl: 'reverts.component.html', - styleUrls: ['reverts.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [FetchRevertsService], - standalone: false, -}) -export class RevertsComponent implements OnInit { - private fetchRevertsService = inject(FetchRevertsService); - private dialog = inject(DialogService); - @Input() deposit: StatDeposit; - - reverts$ = this.fetchRevertsService.result$; - hasMore$ = this.fetchRevertsService.hasMore$; - isLoading$ = this.fetchRevertsService.isLoading$; - columns: Column[] = [ - { field: 'id' }, - { - field: 'status', - cell: (d) => ({ - value: startCase(getUnionKey(d.status)), - color: ( - { - pending: 'pending', - succeeded: 'success', - failed: 'warn', - } as const - )[getUnionKey(d.status)], - }), - }, - createCurrencyColumn( - (d) => ({ - amount: d.body.amount, - code: d.body.currency.symbolic_code, - }), - { header: 'Amount' }, - ), - { field: 'created_at', cell: { type: 'datetime' } }, - ]; - - ngOnInit() { - this.fetchRevertsService.load({ deposit_id: this.deposit.id }); - } - - createRevert() { - this.dialog - .open(CreateRevertDialogComponent, { - depositID: this.deposit.id, - currency: this.deposit.currency_symbolic_code, - }) - .afterClosed() - .pipe(filter((res) => res?.status === 'success')) - .subscribe(() => { - this.fetchRevertsService.reload(); - }); - } - - isCreateRevertAvailable(status: DepositStatus): boolean { - return getUnionKey(status) !== 'succeeded'; - } - - reload(options: UpdateOptions) { - this.fetchRevertsService.reload(options); - } - - more() { - this.fetchRevertsService.more(); - } -} diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/reverts.module.ts b/apps/control-center/src/app/sections/deposit-details/reverts/reverts.module.ts deleted file mode 100644 index c60e09bdd..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/reverts.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { TableModule } from '@vality/matez'; - -import { CreateRevertDialogModule } from './create-revert-dialog/create-revert-dialog.module'; -import { RevertsComponent } from './reverts.component'; - -@NgModule({ - imports: [ - CommonModule, - MatCardModule, - MatButtonModule, - CreateRevertDialogModule, - MatProgressSpinnerModule, - TableModule, - ], - declarations: [RevertsComponent], - exports: [RevertsComponent], -}) -export class RevertsModule {} diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/services/fetch-reverts/fetch-reverts.service.ts b/apps/control-center/src/app/sections/deposit-details/reverts/services/fetch-reverts/fetch-reverts.service.ts deleted file mode 100644 index c96ac6fd2..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/services/fetch-reverts/fetch-reverts.service.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { StatDepositRevert } from '@vality/fistful-proto/fistful_stat'; -import { FetchOptions, FetchSuperclass, clean } from '@vality/matez'; -import { map } from 'rxjs/operators'; - -import { createDsl } from '../../../../../api/fistful-stat'; -import { FistfulStatisticsService } from '../../../../../api/fistful-stat/fistful-statistics.service'; -import { FetchRevertsParams } from '../../types/fetch-reverts-params'; - -@Injectable() -export class FetchRevertsService extends FetchSuperclass { - private fistfulStatisticsService = inject(FistfulStatisticsService); - - fetch(params: FetchRevertsParams, options: FetchOptions) { - return this.fistfulStatisticsService - .GetDepositReverts({ - dsl: createDsl({ - deposit_reverts: { - ...clean(params), - size: String(options.size), - }, - }), - ...(!!options.continuationToken && { - continuation_token: options.continuationToken, - }), - }) - .pipe( - map((res) => ({ - result: res.data.deposit_reverts, - continuationToken: res.continuation_token, - })), - ); - } -} diff --git a/apps/control-center/src/app/sections/deposit-details/reverts/types/fetch-reverts-params.ts b/apps/control-center/src/app/sections/deposit-details/reverts/types/fetch-reverts-params.ts deleted file mode 100644 index 6f2c43710..000000000 --- a/apps/control-center/src/app/sections/deposit-details/reverts/types/fetch-reverts-params.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface FetchRevertsParams { - deposit_id: string; -} diff --git a/apps/control-center/src/app/sections/deposit-details/services/receive-deposit/receive-deposit.service.ts b/apps/control-center/src/app/sections/deposit-details/services/receive-deposit/receive-deposit.service.ts index a0e0e1977..45132e67c 100644 --- a/apps/control-center/src/app/sections/deposit-details/services/receive-deposit/receive-deposit.service.ts +++ b/apps/control-center/src/app/sections/deposit-details/services/receive-deposit/receive-deposit.service.ts @@ -1,15 +1,16 @@ import { Injectable, inject } from '@angular/core'; +import { FistfulStatistics } from '@vality/fistful-proto/fistful_stat'; import { NotifyLogService, inProgressFrom, progressTo } from '@vality/matez'; import { BehaviorSubject, EMPTY, ReplaySubject, Subject, defer } from 'rxjs'; import { catchError, map, shareReplay, switchMap } from 'rxjs/operators'; import { createDsl } from '../../../../api/fistful-stat'; -import { FistfulStatisticsService } from '../../../../api/fistful-stat/fistful-statistics.service'; @Injectable() export class ReceiveDepositService { - private fistfulStatisticsService = inject(FistfulStatisticsService); + private fistfulStatisticsService = inject(FistfulStatistics); private log = inject(NotifyLogService); + deposit$ = defer(() => this.receiveDeposit$).pipe( switchMap((depositId) => this.fistfulStatisticsService diff --git a/apps/control-center/src/app/sections/deposits/components/create-deposit-dialog/create-deposit-dialog.component.ts b/apps/control-center/src/app/sections/deposits/components/create-deposit-dialog/create-deposit-dialog.component.ts index cdaf00aa6..881fe3dd9 100644 --- a/apps/control-center/src/app/sections/deposits/components/create-deposit-dialog/create-deposit-dialog.component.ts +++ b/apps/control-center/src/app/sections/deposits/components/create-deposit-dialog/create-deposit-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, DestroyRef, TemplateRef, ViewChild, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormControl, Validators } from '@angular/forms'; -import { DepositParams } from '@vality/fistful-proto/deposit'; +import { DepositParams, Management } from '@vality/fistful-proto/deposit'; import { DialogSuperclass, NotifyLogService, progressTo } from '@vality/matez'; import { ThriftFormExtension, isTypeWithAliases } from '@vality/ng-thrift'; import { BehaviorSubject, of } from 'rxjs'; @@ -9,7 +9,6 @@ import { first, map, switchMap } from 'rxjs/operators'; import { Overwrite } from 'utility-types'; import { SourceCash } from '../../../../../components/source-cash-field'; -import { DepositManagementService } from '../../../../api/deposit'; import { UserInfoBasedIdGeneratorService } from '../../../../shared/services'; import { FetchSourcesService } from '../../../sources'; @@ -19,10 +18,11 @@ import { FetchSourcesService } from '../../../sources'; }) export class CreateDepositDialogComponent extends DialogSuperclass { private destroyRef = inject(DestroyRef); - private depositManagementService = inject(DepositManagementService); + private depositManagementService = inject(Management); private log = inject(NotifyLogService); private userInfoBasedIdGeneratorService = inject(UserInfoBasedIdGeneratorService); private fetchSourcesService = inject(FetchSourcesService); + @ViewChild('sourceCashTemplate') sourceCashTemplate: TemplateRef; control = new FormControl(this.getDefaultValue(), [Validators.required]); diff --git a/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/create-deposits-by-file-dialog.component.ts b/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/create-deposits-by-file-dialog.component.ts index ab3fce402..91a738d08 100644 --- a/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/create-deposits-by-file-dialog.component.ts +++ b/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/create-deposits-by-file-dialog.component.ts @@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'; import { Component, DestroyRef, Injector, inject, runInInjectionContext } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatButton } from '@angular/material/button'; -import { DepositState } from '@vality/fistful-proto/internal/deposit'; +import { DepositState, Management } from '@vality/fistful-proto/deposit'; import { DEFAULT_DIALOG_CONFIG, DialogModule, @@ -13,7 +13,6 @@ import { import { BehaviorSubject, switchMap } from 'rxjs'; import { UploadCsvComponent } from '../../../../../components/upload-csv'; -import { DepositManagementService } from '../../../../api/deposit'; import { CSV_DEPOSIT_PROPS, CsvDeposit } from './types/csv-deposit'; import { getCreateDepositArgs } from './utils/get-create-deposit-args'; @@ -28,10 +27,11 @@ export class CreateDepositsByFileDialogComponent extends DialogSuperclass< void, DepositState[] > { - private depositManagementService = inject(DepositManagementService); + private depositManagementService = inject(Management); private log = inject(NotifyLogService); private destroyRef = inject(DestroyRef); private injector = inject(Injector); + static override defaultDialogConfig = DEFAULT_DIALOG_CONFIG.large; progress$ = new BehaviorSubject(0); @@ -45,7 +45,7 @@ export class CreateDepositsByFileDialogComponent extends DialogSuperclass< forkJoinToResult( selected.map((c) => runInInjectionContext(this.injector, () => getCreateDepositArgs(c)).pipe( - switchMap((params) => this.depositManagementService.Create(...params)), + switchMap((params) => this.depositManagementService.Create(params, new Map())), ), ), this.progress$, diff --git a/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/types/csv-deposit.ts b/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/types/csv-deposit.ts index 24db496b9..ccde6f1db 100644 --- a/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/types/csv-deposit.ts +++ b/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/types/csv-deposit.ts @@ -3,7 +3,7 @@ import { DeepReadonly } from 'utility-types'; import { CsvObject, CsvProps } from '../../../../../../components/upload-csv'; export const CSV_DEPOSIT_PROPS = { - required: ['wallet_id', 'source_id', 'body.amount', 'body.currency'], + required: ['wallet_id', 'source_id', 'party_id', 'body.amount', 'body.currency'], optional: ['external_id', 'description', 'metadata'], } as const satisfies DeepReadonly; diff --git a/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/utils/get-create-deposit-args.ts b/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/utils/get-create-deposit-args.ts index 03587ab6a..e9b06090b 100644 --- a/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/utils/get-create-deposit-args.ts +++ b/apps/control-center/src/app/sections/deposits/components/create-deposits-by-file-dialog/utils/get-create-deposit-args.ts @@ -1,5 +1,5 @@ import { inject } from '@angular/core'; -import { CodegenClient } from '@vality/fistful-proto/internal/deposit-Management'; +import { DepositParams } from '@vality/fistful-proto/deposit'; import { clean } from '@vality/matez'; import { map } from 'rxjs/operators'; @@ -14,12 +14,13 @@ export function getCreateDepositArgs(c: CsvDeposit) { const amountCurrencyService = inject(AmountCurrencyService); return amountCurrencyService.toMinor(Number(c['body.amount']), c['body.currency']).pipe( map( - (amount): Parameters => [ + (amount): DepositParams => clean( { id: userInfoBasedIdGeneratorService.getUsernameBasedId(), wallet_id: c.wallet_id, source_id: c.source_id, + party_id: c.party_id, body: { amount, currency: { symbolic_code: c['body.currency'] }, @@ -31,8 +32,6 @@ export function getCreateDepositArgs(c: CsvDeposit) { false, true, ), - new Map(), - ], ), ); } diff --git a/apps/control-center/src/app/sections/deposits/deposits-routing.module.ts b/apps/control-center/src/app/sections/deposits/deposits-routing.module.ts index f763f92bb..612ed8baf 100644 --- a/apps/control-center/src/app/sections/deposits/deposits-routing.module.ts +++ b/apps/control-center/src/app/sections/deposits/deposits-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { DepositsComponent } from './deposits.component'; import { ROUTING_CONFIG } from './routing-config'; @@ -12,7 +12,7 @@ import { ROUTING_CONFIG } from './routing-config'; { path: '', component: DepositsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, { diff --git a/apps/control-center/src/app/sections/deposits/deposits.component.ts b/apps/control-center/src/app/sections/deposits/deposits.component.ts index 69f5848fb..849ca9db2 100644 --- a/apps/control-center/src/app/sections/deposits/deposits.component.ts +++ b/apps/control-center/src/app/sections/deposits/deposits.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component, DestroyRef, OnInit, inject } from ' import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { NonNullableFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; -import { fistful_stat } from '@vality/fistful-proto'; import { StatDeposit } from '@vality/fistful-proto/fistful_stat'; import { Column, @@ -15,7 +14,6 @@ import { createDateRangeToToday, createMenuColumn, debounceTimeWithFirst, - getEnumKey, getNoTimeZoneIsoString, getValueChanges, isEqualDateRange, @@ -48,6 +46,7 @@ export class DepositsComponent implements OnInit { private debounceTimeMs = inject(DEBOUNCE_TIME_MS); private qp = inject>(QueryParamsService); private dr = inject(DestroyRef); + filtersForm = this.fb.group({ dateRange: createDateRangeToToday(this.dateRangeDays), amount_to: null as number, @@ -99,19 +98,6 @@ export class DepositsComponent implements OnInit { createCurrencyColumn((d) => ({ amount: d.fee, code: d.currency_symbolic_code }), { header: 'Fee', }), - { - field: 'revert_status', - cell: (d) => ({ - value: startCase(getEnumKey(fistful_stat.RevertStatus, d.revert_status)), - color: ( - { - none: 'neutral', - partial: 'pending', - full: 'success', - } as const - )[getEnumKey(fistful_stat.RevertStatus, d.revert_status)], - }), - }, createMenuColumn((d) => ({ items: [ { diff --git a/apps/control-center/src/app/sections/deposits/services/fetch-deposits/fetch-deposits.service.ts b/apps/control-center/src/app/sections/deposits/services/fetch-deposits/fetch-deposits.service.ts index ef65f6fbd..0a71294f7 100644 --- a/apps/control-center/src/app/sections/deposits/services/fetch-deposits/fetch-deposits.service.ts +++ b/apps/control-center/src/app/sections/deposits/services/fetch-deposits/fetch-deposits.service.ts @@ -1,18 +1,17 @@ import { Injectable, inject } from '@angular/core'; -import { StatDeposit } from '@vality/fistful-proto/fistful_stat'; +import { FistfulStatistics, StatDeposit } from '@vality/fistful-proto/fistful_stat'; import { FetchOptions, FetchResult, FetchSuperclass, NotifyLogService } from '@vality/matez'; import { Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { QueryDsl, createDsl } from '../../../../api/fistful-stat'; -import { FistfulStatisticsService } from '../../../../api/fistful-stat/fistful-statistics.service'; @Injectable() export class FetchDepositsService extends FetchSuperclass< StatDeposit, QueryDsl['query']['deposits'] > { - private fistfulStatisticsService = inject(FistfulStatisticsService); + private fistfulStatisticsService = inject(FistfulStatistics); private log = inject(NotifyLogService); protected fetch( diff --git a/apps/control-center/src/app/sections/domain/domain-info/domain-info.component.html b/apps/control-center/src/app/sections/domain/domain-info/domain-info.component.html deleted file mode 100644 index 093309357..000000000 --- a/apps/control-center/src/app/sections/domain/domain-info/domain-info.component.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/apps/control-center/src/app/sections/domain/domain-info/domain-info.component.ts b/apps/control-center/src/app/sections/domain/domain-info/domain-info.component.ts deleted file mode 100644 index ad44df714..000000000 --- a/apps/control-center/src/app/sections/domain/domain-info/domain-info.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Component, DestroyRef, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { DialogService } from '@vality/matez'; -import { BehaviorSubject } from 'rxjs'; -import { first } from 'rxjs/operators'; - -import { DomainStoreService } from '../../../api/domain-config/stores/domain-store.service'; -import { CreateDomainObjectDialogComponent } from '../../../shared/components/thrift-api-crud'; - -@Component({ - templateUrl: './domain-info.component.html', - styleUrls: ['./domain-info.component.scss'], - standalone: false, -}) -export class DomainInfoComponent { - private domainStoreService = inject(DomainStoreService); - private dialogService = inject(DialogService); - private destroyRef = inject(DestroyRef); - version$ = this.domainStoreService.version$; - progress$ = this.domainStoreService.isLoading$; - selectedTypes$ = new BehaviorSubject([]); - - create() { - this.selectedTypes$ - .pipe(first(), takeUntilDestroyed(this.destroyRef)) - .subscribe((types) => { - this.dialogService.open( - CreateDomainObjectDialogComponent, - types?.length === 1 - ? { - objectType: types[0], - } - : undefined, - ); - }); - } -} diff --git a/apps/control-center/src/app/sections/domain/domain-info/domain-info.module.ts b/apps/control-center/src/app/sections/domain/domain-info/domain-info.module.ts deleted file mode 100644 index 7e44148a2..000000000 --- a/apps/control-center/src/app/sections/domain/domain-info/domain-info.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatInputModule } from '@angular/material/input'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { MatSidenavModule } from '@angular/material/sidenav'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { RouterModule } from '@angular/router'; -import { ActionsModule, PipesModule } from '@vality/matez'; -import { ThriftPipesModule, ThriftViewerModule } from '@vality/ng-thrift'; - -import { PageLayoutModule } from '../../../shared'; -import { DomainThriftViewerComponent } from '../../../shared/components/thrift-api-crud'; - -import { DomainInfoComponent } from './domain-info.component'; -import { DomainObjectsTableComponent } from './domain-objects-table'; - -@NgModule({ - declarations: [DomainInfoComponent], - imports: [ - CommonModule, - DomainObjectsTableComponent, - MatCardModule, - MatProgressBarModule, - MatSnackBarModule, - MatSidenavModule, - MatButtonModule, - MatInputModule, - MatProgressSpinnerModule, - ThriftViewerModule, - PipesModule, - RouterModule, - ActionsModule, - ThriftPipesModule, - PageLayoutModule, - DomainThriftViewerComponent, - ], -}) -export class DomainInfoModule {} diff --git a/apps/control-center/src/app/sections/domain/domain-info/domain-objects-table/domain-objects-table.component.html b/apps/control-center/src/app/sections/domain/domain-info/domain-objects-table/domain-objects-table.component.html deleted file mode 100644 index 8808eba2d..000000000 --- a/apps/control-center/src/app/sections/domain/domain-info/domain-objects-table/domain-objects-table.component.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/apps/control-center/src/app/sections/domain/domain-info/domain-objects-table/domain-objects-table.component.ts b/apps/control-center/src/app/sections/domain/domain-info/domain-objects-table/domain-objects-table.component.ts deleted file mode 100644 index 8e789c530..000000000 --- a/apps/control-center/src/app/sections/domain/domain-info/domain-objects-table/domain-objects-table.component.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component, DestroyRef, EventEmitter, OnInit, Output, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormControl, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { DomainObject, Reference } from '@vality/domain-proto/domain'; -import { - ActionsModule, - Column, - DialogService, - QueryParamsService, - SelectFieldModule, - TableModule, - createMenuColumn, - getValueChanges, -} from '@vality/matez'; -import sortBy from 'lodash-es/sortBy'; -import startCase from 'lodash-es/startCase'; -import { Observable, combineLatest, forkJoin, of } from 'rxjs'; -import { map, shareReplay, startWith, switchMap } from 'rxjs/operators'; - -import { DomainStoreService } from '../../../../api/domain-config/stores/domain-store.service'; -import { SidenavInfoService } from '../../../../shared/components/sidenav-info'; -import { - DeleteDomainObjectService, - DomainObjectCardComponent, - EditDomainObjectDialogComponent, - getDomainObjectDetails, -} from '../../../../shared/components/thrift-api-crud'; -import { MetadataService } from '../../services/metadata.service'; - -interface DomainObjectData { - type: string; - ref: Reference; - obj: DomainObject; -} - -@Component({ - selector: 'cc-domain-objects-table', - templateUrl: './domain-objects-table.component.html', - imports: [ - CommonModule, - SelectFieldModule, - TableModule, - ReactiveFormsModule, - ActionsModule, - MatButtonModule, - ], -}) -export class DomainObjectsTableComponent implements OnInit { - private domainStoreService = inject(DomainStoreService); - private metadataService = inject(MetadataService); - private qp = inject>( - QueryParamsService<{ types?: string[] }>, - ); - private sidenavInfoService = inject(SidenavInfoService); - private deleteDomainObjectService = inject(DeleteDomainObjectService); - private destroyRef = inject(DestroyRef); - private dialogService = inject(DialogService); - @Output() selectedChange = new EventEmitter(); - - typesControl = new FormControl( - (this.qp.params.types as (keyof DomainObject)[]) || [], - ); - objects$: Observable = combineLatest([ - this.domainStoreService.domain$, - this.typesControl.valueChanges.pipe(startWith(this.typesControl.value)), - ]).pipe( - switchMap(([objects, types]) => - forkJoin([ - combineLatest( - Array.from(objects).map(([ref, obj]) => - this.metadataService - .getDomainObjectType(ref) - .pipe(map((type) => ({ type, ref, obj }))), - ), - ), - of(types), - ]), - ), - map(([objects, types]) => objects.filter((o) => types.includes(o.type))), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - columns: Column[] = [ - { - field: 'id', - cell: (d) => ({ value: getDomainObjectDetails(d.obj).id }), - sticky: 'start', - }, - { - field: 'name', - cell: (d) => ({ - value: getDomainObjectDetails(d.obj).label, - click: () => this.details(d), - }), - style: { width: 0 }, - }, - { - field: 'description', - cell: (d) => ({ value: getDomainObjectDetails(d.obj).description }), - }, - { - field: 'type', - cell: (d) => ({ value: startCase(d.type) }), - hidden: getValueChanges(this.typesControl).pipe(map((t) => t.length <= 1)), - }, - createMenuColumn((d) => ({ - items: [ - { - label: 'Details', - click: () => { - this.details(d); - }, - }, - { - label: 'Edit', - click: () => { - this.dialogService - .open(EditDomainObjectDialogComponent, { domainObject: d.obj }) - .afterClosed() - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(); - }, - }, - { - label: 'Delete', - click: () => { - this.deleteDomainObjectService.delete(d.ref); - }, - }, - ], - })), - ]; - fields$ = this.metadataService.getDomainFields().pipe( - map((fields) => sortBy(fields, 'type')), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - options$ = this.fields$.pipe( - map((fields) => - fields.map(({ type }) => ({ - label: startCase(String(type)), - value: type, - type: 'Domain object', - })), - ), - ); - isLoading$ = this.domainStoreService.isLoading$; - - ngOnInit() { - this.typesControl.valueChanges - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((types) => { - void this.qp.patch({ types }); - }); - getValueChanges(this.typesControl) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((types) => { - this.selectedChange.emit(types); - }); - } - - update() { - this.domainStoreService.forceReload(); - } - - details(d: DomainObjectData) { - this.sidenavInfoService.toggle(DomainObjectCardComponent, { ref: d.ref }); - } -} diff --git a/apps/control-center/src/app/sections/domain/domain-info/index.ts b/apps/control-center/src/app/sections/domain/domain-info/index.ts deleted file mode 100644 index 2867f737e..000000000 --- a/apps/control-center/src/app/sections/domain/domain-info/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './domain-info.module'; -export * from './domain-info.component'; diff --git a/apps/control-center/src/app/sections/domain/domain-routing.module.ts b/apps/control-center/src/app/sections/domain/domain-routing.module.ts deleted file mode 100644 index ee7ac473a..000000000 --- a/apps/control-center/src/app/sections/domain/domain-routing.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { AppAuthGuardService } from '../../shared/services'; - -import { DomainInfoComponent } from './domain-info'; -import { ROUTING_CONFIG } from './routing-config'; - -@NgModule({ - imports: [ - RouterModule.forChild([ - { - path: '', - canActivate: [AppAuthGuardService], - data: ROUTING_CONFIG, - component: DomainInfoComponent, - }, - ]), - ], - exports: [RouterModule], -}) -export class DomainRoutingModule {} diff --git a/apps/control-center/src/app/sections/domain/domain.module.ts b/apps/control-center/src/app/sections/domain/domain.module.ts deleted file mode 100644 index 90a176974..000000000 --- a/apps/control-center/src/app/sections/domain/domain.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { DomainInfoModule } from './domain-info'; -import { DomainRoutingModule } from './domain-routing.module'; -import { MetadataService } from './services/metadata.service'; - -@NgModule({ - imports: [DomainRoutingModule, DomainInfoModule], - providers: [MetadataService], -}) -export class DomainModule {} diff --git a/apps/control-center/src/app/sections/domain/index.ts b/apps/control-center/src/app/sections/domain/index.ts deleted file mode 100644 index c6b6c450b..000000000 --- a/apps/control-center/src/app/sections/domain/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './domain.module'; diff --git a/apps/control-center/src/app/sections/domain/routing-config.ts b/apps/control-center/src/app/sections/domain/routing-config.ts deleted file mode 100644 index 59ce065d6..000000000 --- a/apps/control-center/src/app/sections/domain/routing-config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { RoutingConfig, Services } from '../../shared/services'; - -export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.Domain], -}; diff --git a/apps/control-center/src/app/sections/domain/services/domain-navigate.service.ts b/apps/control-center/src/app/sections/domain/services/domain-navigate.service.ts deleted file mode 100644 index e5f41679f..000000000 --- a/apps/control-center/src/app/sections/domain/services/domain-navigate.service.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { Router } from '@angular/router'; - -@Injectable({ - providedIn: 'root', -}) -export class DomainNavigateService { - private router = inject(Router); - - toType(type: string) { - return this.router.navigate(['domain'], { queryParams: { types: JSON.stringify([type]) } }); - } -} diff --git a/apps/control-center/src/app/sections/domain/services/metadata.service.ts b/apps/control-center/src/app/sections/domain/services/metadata.service.ts deleted file mode 100644 index eaa9aad65..000000000 --- a/apps/control-center/src/app/sections/domain/services/metadata.service.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ThriftAstMetadata } from '@vality/domain-proto'; -import { Reference } from '@vality/domain-proto/domain'; -import { getImportValue } from '@vality/matez'; -import { Field } from '@vality/thrift-ts'; -import { Observable, of } from 'rxjs'; -import { map, shareReplay, withLatestFrom } from 'rxjs/operators'; - -@Injectable({ providedIn: 'root' }) -export class MetadataService { - private metadata$ = getImportValue( - import('@vality/domain-proto/metadata.json'), - ).pipe(shareReplay({ refCount: true, bufferSize: 1 })); - - getDomainObjectType(ref: Reference): Observable { - if (!ref) { - return of(null); - } - const keys = Object.keys(ref); - if (keys.length !== 1) { - return of(null); - } - const searchName = keys[0]; - return this.getDomainFields().pipe( - map((d) => { - const found = d.find(({ name }) => name === searchName); - return found ? (found.type as string) : null; - }), - ); - } - - getDomainFieldByName(fieldName: string): Observable { - return this.getDomainFields().pipe( - map((fields) => fields.find((f) => f.name === fieldName)), - ); - } - - getDomainFieldByType(fieldType: string): Observable { - return this.getDomainFields().pipe( - map((fields) => fields.find((f) => f.type === fieldType)), - ); - } - - getDomainObjectDataFieldByName(fieldName: string): Observable { - return this.getDomainFields().pipe( - map((fields) => fields.find((f) => f.name === fieldName)), - withLatestFrom(this.metadata$), - map(([field, metadata]) => - metadata - .find(({ name }) => name === 'domain') - .ast.struct[String(field.type)]?.find((f) => f.name === 'data'), - ), - ); - } - - getDomainFields(): Observable { - return this.metadata$.pipe( - map((m) => m.find(({ name }) => name === 'domain').ast.union['DomainObject']), - ); - } -} diff --git a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects-table/index.ts b/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects-table/index.ts deleted file mode 100644 index 565e914ae..000000000 --- a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects-table/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './domain-objects-table.component'; diff --git a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects.component.scss b/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects.component.scss deleted file mode 100644 index b450e6fca..000000000 --- a/apps/control-center/src/app/sections/domain2/domain-objects/domain-objects.component.scss +++ /dev/null @@ -1,13 +0,0 @@ -mat-sidenav { - width: 50%; -} - -.details-container { - padding: 20px; - height: 100%; - - .viewer { - height: 100%; - overflow: scroll; - } -} diff --git a/apps/control-center/src/app/sections/domain2/domain-objects/index.ts b/apps/control-center/src/app/sections/domain2/domain-objects/index.ts deleted file mode 100644 index 45b462543..000000000 --- a/apps/control-center/src/app/sections/domain2/domain-objects/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './domain-objects.component'; diff --git a/apps/control-center/src/app/sections/domain2/domain-routing.module.ts b/apps/control-center/src/app/sections/domain2/domain-routing.module.ts deleted file mode 100644 index 1d1ef5e71..000000000 --- a/apps/control-center/src/app/sections/domain2/domain-routing.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { AppAuthGuardService } from '../../shared/services'; - -import { DomainObjectsComponent } from './domain-objects'; -import { ROUTING_CONFIG } from './routing-config'; - -@NgModule({ - imports: [ - RouterModule.forChild([ - { - path: '', - canActivate: [AppAuthGuardService], - data: ROUTING_CONFIG, - component: DomainObjectsComponent, - }, - ]), - ], - exports: [RouterModule], -}) -export class DomainRoutingModule {} diff --git a/apps/control-center/src/app/sections/domain2/domain.module.ts b/apps/control-center/src/app/sections/domain2/domain.module.ts deleted file mode 100644 index 1bf472db5..000000000 --- a/apps/control-center/src/app/sections/domain2/domain.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { DomainRoutingModule } from './domain-routing.module'; - -@NgModule({ - imports: [DomainRoutingModule], -}) -export class DomainModule {} diff --git a/apps/control-center/src/app/sections/domain2/index.ts b/apps/control-center/src/app/sections/domain2/index.ts deleted file mode 100644 index c6b6c450b..000000000 --- a/apps/control-center/src/app/sections/domain2/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './domain.module'; diff --git a/apps/control-center/src/app/sections/domain2/routing-config.ts b/apps/control-center/src/app/sections/domain2/routing-config.ts deleted file mode 100644 index 59ce065d6..000000000 --- a/apps/control-center/src/app/sections/domain2/routing-config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { RoutingConfig, Services } from '../../shared/services'; - -export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.Domain], -}; diff --git a/apps/control-center/src/app/sections/machines/machines-routing.module.ts b/apps/control-center/src/app/sections/machines/machines-routing.module.ts index dedcf6413..b89c59be2 100644 --- a/apps/control-center/src/app/sections/machines/machines-routing.module.ts +++ b/apps/control-center/src/app/sections/machines/machines-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { MachinesComponent } from './machines.component'; import { ROUTING_CONFIG } from './routing-config'; @@ -12,7 +12,7 @@ import { ROUTING_CONFIG } from './routing-config'; { path: '', component: MachinesComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/party-shops/party-shops-routing.module.ts b/apps/control-center/src/app/sections/party-shops/party-shops-routing.module.ts index ffa7b0697..898a44300 100644 --- a/apps/control-center/src/app/sections/party-shops/party-shops-routing.module.ts +++ b/apps/control-center/src/app/sections/party-shops/party-shops-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { PartyShopsComponent } from './party-shops.component'; import { ROUTING_CONFIG } from './routing-config'; @@ -12,7 +12,7 @@ import { ROUTING_CONFIG } from './routing-config'; { path: '', component: PartyShopsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/party-shops/party-shops.component.html b/apps/control-center/src/app/sections/party-shops/party-shops.component.html index defd830cc..17e30ab72 100644 --- a/apps/control-center/src/app/sections/party-shops/party-shops.component.html +++ b/apps/control-center/src/app/sections/party-shops/party-shops.component.html @@ -1,8 +1,8 @@ diff --git a/apps/control-center/src/app/sections/party-shops/party-shops.component.ts b/apps/control-center/src/app/sections/party-shops/party-shops.component.ts index c0488c23e..523d22eff 100644 --- a/apps/control-center/src/app/sections/party-shops/party-shops.component.ts +++ b/apps/control-center/src/app/sections/party-shops/party-shops.component.ts @@ -1,32 +1,26 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; -import { Observable, withLatestFrom } from 'rxjs'; -import { map, shareReplay } from 'rxjs/operators'; +import { UpdateOptions } from '@vality/matez'; +import { map, switchMap } from 'rxjs/operators'; -import { ShopParty } from '../../shared/components/shops-table'; - -import { PartyShopsService } from './party-shops.service'; +import { PartiesStoreService } from '../../api/payment-processing'; +import { PartyStoreService } from '../party/party-store.service'; @Component({ templateUrl: 'party-shops.component.html', - providers: [PartyShopsService], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) export class PartyShopsComponent { - private partyShopsService = inject(PartyShopsService); - shopsParty$: Observable = this.partyShopsService.shops$.pipe( - withLatestFrom(this.partyShopsService.party$), - map(([shops, party]) => - shops.map((shop) => ({ - shop, - party: { id: party.id, email: party.contact_info?.registration_email }, - })), - ), - shareReplay({ refCount: true, bufferSize: 1 }), + private partiesStoreService = inject(PartiesStoreService); + private partyStoreService = inject(PartyStoreService); + + shopsResource$ = this.partyStoreService.party$.pipe( + map((party) => this.partiesStoreService.getPartyShops(party.id)), ); - progress$ = this.partyShopsService.progress$; + shops$ = this.shopsResource$.pipe(switchMap((res) => res.value$)); + progress$ = this.shopsResource$.pipe(switchMap((res) => res.isLoading$)); - update() { - this.partyShopsService.reload(); + reload(_options: UpdateOptions) { + this.partiesStoreService.reloadParties(); } } diff --git a/apps/control-center/src/app/sections/party-shops/party-shops.service.ts b/apps/control-center/src/app/sections/party-shops/party-shops.service.ts deleted file mode 100644 index 3e932807a..000000000 --- a/apps/control-center/src/app/sections/party-shops/party-shops.service.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { Party } from '@vality/domain-proto/domain'; -import { debounceTimeWithFirst, progressTo } from '@vality/matez'; -import { BehaviorSubject, Observable, Subject, defer, merge } from 'rxjs'; -import { map, shareReplay, startWith, switchMap } from 'rxjs/operators'; - -import { PartyManagementService } from '../../api/payment-processing/party-management.service'; -import { DEBOUNCE_TIME_MS } from '../../tokens'; - -@Injectable() -export class PartyShopsService { - private partyManagementService = inject(PartyManagementService); - private route = inject(ActivatedRoute); - private debounceTimeMs = inject(DEBOUNCE_TIME_MS); - party$: Observable = merge( - this.route.params, - defer(() => this.reload$), - ).pipe( - startWith(null), - debounceTimeWithFirst(this.debounceTimeMs), - map(() => this.route.snapshot.params['partyID']), - switchMap((partyID) => - this.partyManagementService.Get(partyID).pipe(progressTo(this.progress$)), - ), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - shops$ = this.party$.pipe( - map((p) => p.shops), - map((shops) => Array.from(shops.values())), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - progress$ = new BehaviorSubject(0); - - private reload$ = new Subject(); - - reload() { - this.reload$.next(); - } -} diff --git a/apps/control-center/src/app/sections/party-shops/routing-config.ts b/apps/control-center/src/app/sections/party-shops/routing-config.ts index 05afa7529..af803631e 100644 --- a/apps/control-center/src/app/sections/party-shops/routing-config.ts +++ b/apps/control-center/src/app/sections/party-shops/routing-config.ts @@ -1,5 +1,5 @@ import { RoutingConfig, Services } from '../../shared/services'; export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.PartyManagement], + services: [Services.DMT], }; diff --git a/apps/control-center/src/app/sections/party/party-routing.module.ts b/apps/control-center/src/app/sections/party/party-routing.module.ts index ea47669d5..efd445381 100644 --- a/apps/control-center/src/app/sections/party/party-routing.module.ts +++ b/apps/control-center/src/app/sections/party/party-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { PartyComponent } from './party.component'; import { ROUTING_CONFIG } from './routing-config'; @@ -12,7 +12,7 @@ import { ROUTING_CONFIG } from './routing-config'; { path: ':partyID', component: PartyComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, children: [ { @@ -25,23 +25,11 @@ import { ROUTING_CONFIG } from './routing-config'; loadChildren: () => import('../routing-rules').then((m) => m.RoutingRulesModule), }, - { - path: 'claims', - loadChildren: () => import('../claims').then((m) => m.ClaimsModule), - }, - { - path: 'claim', - redirectTo: 'claims', - }, { path: 'wallets', loadChildren: () => import('../wallets/wallets.module').then((m) => m.WalletsModule), }, - { - path: 'wallet', - redirectTo: 'wallets', - }, { path: '', redirectTo: 'shops', diff --git a/apps/control-center/src/app/sections/party/party-store.service.ts b/apps/control-center/src/app/sections/party/party-store.service.ts index 76b566c16..b23eda6d2 100644 --- a/apps/control-center/src/app/sections/party/party-store.service.ts +++ b/apps/control-center/src/app/sections/party/party-store.service.ts @@ -1,28 +1,30 @@ import { Injectable, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Party } from '@vality/domain-proto/domain'; +import { PartyConfig } from '@vality/domain-proto/domain'; import { NotifyLogService } from '@vality/matez'; import { EMPTY, Observable, of } from 'rxjs'; import { catchError, distinctUntilChanged, + map, shareReplay, startWith, switchMap, } from 'rxjs/operators'; -import { PartyManagementService } from '../../api/payment-processing'; +import { PartiesStoreService } from '../../api/payment-processing'; @Injectable() export class PartyStoreService { private route = inject(ActivatedRoute); - private partyManagementService = inject(PartyManagementService); + private partiesStoreService = inject(PartiesStoreService); private log = inject(NotifyLogService); - party$: Observable | null> = this.route.params.pipe( + party$: Observable | null> = this.route.params.pipe( startWith(this.route.snapshot.params), switchMap(({ partyID }) => partyID - ? this.partyManagementService.Get(partyID).pipe( + ? this.partiesStoreService.getParty(partyID).value$.pipe( + map((party) => party.data), catchError((err) => { this.log.error(err); return EMPTY; diff --git a/apps/control-center/src/app/sections/party/party.component.html b/apps/control-center/src/app/sections/party/party.component.html index 1e963de60..1cc191b66 100644 --- a/apps/control-center/src/app/sections/party/party.component.html +++ b/apps/control-center/src/app/sections/party/party.component.html @@ -1,3 +1,3 @@ - + diff --git a/apps/control-center/src/app/sections/party/party.component.ts b/apps/control-center/src/app/sections/party/party.component.ts index b45496006..459a3c666 100644 --- a/apps/control-center/src/app/sections/party/party.component.ts +++ b/apps/control-center/src/app/sections/party/party.component.ts @@ -1,58 +1,21 @@ import { Component, inject } from '@angular/core'; -import { Link } from '@vality/matez'; import { getUnionKey } from '@vality/ng-thrift'; import startCase from 'lodash-es/startCase'; import { map } from 'rxjs/operators'; import { SidenavInfoService } from '../../shared/components/sidenav-info'; -import { AppAuthGuardService, Services } from '../../shared/services'; -import { ROUTING_CONFIG as CLAIMS_CONFIG } from '../claims/routing-config'; -import { ROUTING_CONFIG as RULESET_ROUTING_CONFIG } from '../routing-rules/party-routing-ruleset/routing-config'; -import { SHOPS_ROUTING_CONFIG } from '../shops'; -import { ROUTING_CONFIG as WALLETS_ROUTING_CONFIG } from '../wallets/routing-config'; import { PartyStoreService } from './party-store.service'; -interface PartyLink extends Link { - services?: Services[]; -} - @Component({ templateUrl: 'party.component.html', providers: [PartyStoreService], standalone: false, }) export class PartyComponent { - private appAuthGuardService = inject(AppAuthGuardService); protected sidenavInfoService = inject(SidenavInfoService); private partyStoreService = inject(PartyStoreService); - links: PartyLink[] = [ - { - label: 'Shops', - url: 'shops', - services: SHOPS_ROUTING_CONFIG.services, - }, - { - label: 'Wallets', - url: 'wallets', - services: WALLETS_ROUTING_CONFIG.services, - }, - { - label: 'Claims', - url: 'claims', - services: CLAIMS_CONFIG.services, - }, - { - label: 'Payment Routing Rules', - url: 'routing-rules/payment/main', - services: RULESET_ROUTING_CONFIG.services, - }, - { - label: 'Withdrawal Routing Rules', - url: 'routing-rules/withdrawal/main', - services: RULESET_ROUTING_CONFIG.services, - }, - ].filter((item) => this.appAuthGuardService.userHasSomeServiceMethods(item.services)); + party$ = this.partyStoreService.party$; tags$ = this.party$.pipe( map((party) => [ diff --git a/apps/control-center/src/app/sections/party/routing-config.ts b/apps/control-center/src/app/sections/party/routing-config.ts index bdb23bfad..af803631e 100644 --- a/apps/control-center/src/app/sections/party/routing-config.ts +++ b/apps/control-center/src/app/sections/party/routing-config.ts @@ -1,5 +1,5 @@ import { RoutingConfig, Services } from '../../shared/services'; export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.Deanonimus], + services: [Services.DMT], }; diff --git a/apps/control-center/src/app/sections/payment-details/components/payment-chargebacks/payment-chargebacks.component.ts b/apps/control-center/src/app/sections/payment-details/components/payment-chargebacks/payment-chargebacks.component.ts index 7fbde63e1..a405112f3 100644 --- a/apps/control-center/src/app/sections/payment-details/components/payment-chargebacks/payment-chargebacks.component.ts +++ b/apps/control-center/src/app/sections/payment-details/components/payment-chargebacks/payment-chargebacks.component.ts @@ -33,11 +33,10 @@ export class PaymentChargebacksComponent implements OnInit { } createChargeback() { + const [invoiceID, paymentID] = this.route.snapshot.params['paymentID'].split('.'); + this.dialogService - .open( - CreateChargebackDialogComponent, - this.route.snapshot.params as Record<'invoiceID' | 'paymentID', string>, - ) + .open(CreateChargebackDialogComponent, { invoiceID, paymentID }) .afterClosed() .pipe(takeUntilDestroyed(this.dr)) .subscribe(({ status }) => { diff --git a/apps/control-center/src/app/sections/payment-details/components/payment-details/payment-details.component.ts b/apps/control-center/src/app/sections/payment-details/components/payment-details/payment-details.component.ts index d76d94f0b..5f6165f4e 100644 --- a/apps/control-center/src/app/sections/payment-details/components/payment-details/payment-details.component.ts +++ b/apps/control-center/src/app/sections/payment-details/components/payment-details/payment-details.component.ts @@ -29,7 +29,6 @@ export class PaymentDetailsComponent { metadata$ = metadata$; extensions$: Observable = this.payment$.pipe( map((payment): ThriftViewExtension[] => [ - this.domainMetadataViewExtensionsService.createShopExtension(payment.owner_id), { determinant: (d) => of(isTypeWithAliases(d, 'Amount', 'domain')), extension: (_, amount: number) => diff --git a/apps/control-center/src/app/sections/payment-details/components/payment-events/payment-events.component.ts b/apps/control-center/src/app/sections/payment-details/components/payment-events/payment-events.component.ts index 5901eb4f1..90d00a305 100644 --- a/apps/control-center/src/app/sections/payment-details/components/payment-events/payment-events.component.ts +++ b/apps/control-center/src/app/sections/payment-details/components/payment-events/payment-events.component.ts @@ -2,12 +2,12 @@ import { CommonModule } from '@angular/common'; import { Component, inject } from '@angular/core'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatIcon } from '@angular/material/icon'; +import { Invoicing } from '@vality/domain-proto/payment_processing'; import { ThriftPipesModule } from '@vality/ng-thrift'; import { switchMap } from 'rxjs'; import { map, shareReplay } from 'rxjs/operators'; import { TimelineModule } from '../../../../../components/timeline'; -import { InvoicingService } from '../../../../api/payment-processing'; import { PageLayoutModule } from '../../../../shared'; import { DomainThriftViewerComponent } from '../../../../shared/components/thrift-api-crud'; import { PaymentDetailsService } from '../../payment-details.service'; @@ -32,7 +32,7 @@ import { getInvoiceChangeInfo } from './utils/get-invoice-change-info'; }) export class PaymentEventsComponent { private paymentDetailsService = inject(PaymentDetailsService); - private invoicingService = inject(InvoicingService); + private invoicingService = inject(Invoicing); payment$ = this.paymentDetailsService.payment$; isLoading$ = this.paymentDetailsService.isLoading$; events$ = this.payment$.pipe( diff --git a/apps/control-center/src/app/sections/payment-details/components/payment-events/utils/get-invoice-change-info.ts b/apps/control-center/src/app/sections/payment-details/components/payment-events/utils/get-invoice-change-info.ts index 18369764b..be6debc9d 100644 --- a/apps/control-center/src/app/sections/payment-details/components/payment-events/utils/get-invoice-change-info.ts +++ b/apps/control-center/src/app/sections/payment-details/components/payment-events/utils/get-invoice-change-info.ts @@ -1,4 +1,4 @@ -import { Event, InvoiceChange } from '@vality/domain-proto/internal/payment_processing'; +import { Event, InvoiceChange } from '@vality/domain-proto/payment_processing'; import { getUnionKey, getUnionValue } from '@vality/ng-thrift'; import { upperFirst } from 'lodash-es'; import isEmpty from 'lodash-es/isEmpty'; diff --git a/apps/control-center/src/app/sections/payment-details/create-chargeback-dialog/create-chargeback-dialog.component.ts b/apps/control-center/src/app/sections/payment-details/create-chargeback-dialog/create-chargeback-dialog.component.ts index 609129332..963cd17f8 100644 --- a/apps/control-center/src/app/sections/payment-details/create-chargeback-dialog/create-chargeback-dialog.component.ts +++ b/apps/control-center/src/app/sections/payment-details/create-chargeback-dialog/create-chargeback-dialog.component.ts @@ -1,13 +1,12 @@ import { Component, DestroyRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormControl } from '@angular/forms'; +import { metadata$ } from '@vality/domain-proto'; import { InvoicePaymentChargeback } from '@vality/domain-proto/domain'; -import { InvoicePaymentChargebackParams } from '@vality/domain-proto/payment_processing'; -import { DialogSuperclass, NotifyLogService, getImportValue } from '@vality/matez'; -import { ThriftAstMetadata } from '@vality/ng-thrift'; +import { InvoicePaymentChargebackParams, Invoicing } from '@vality/domain-proto/payment_processing'; +import { DialogSuperclass, NotifyLogService } from '@vality/matez'; import short from 'short-uuid'; -import { InvoicingService } from '../../../api/payment-processing/invoicing.service'; import { DomainMetadataFormExtensionsService } from '../../../shared/services'; @Component({ @@ -20,12 +19,12 @@ export class CreateChargebackDialogComponent extends DialogSuperclass< { invoiceID: string; paymentID: string }, InvoicePaymentChargeback > { - private invoicingService = inject(InvoicingService); + private invoicingService = inject(Invoicing); private domainMetadataFormExtensionsService = inject(DomainMetadataFormExtensionsService); private log = inject(NotifyLogService); private destroyRef = inject(DestroyRef); form = new FormControl>({ id: short().generate() }); - metadata$ = getImportValue(import('@vality/domain-proto/metadata.json')); + metadata$ = metadata$; extensions$ = this.domainMetadataFormExtensionsService.extensions$; create() { diff --git a/apps/control-center/src/app/sections/payment-details/payment-details-routing.module.ts b/apps/control-center/src/app/sections/payment-details/payment-details-routing.module.ts index 1f94c25c1..fd661a926 100644 --- a/apps/control-center/src/app/sections/payment-details/payment-details-routing.module.ts +++ b/apps/control-center/src/app/sections/payment-details/payment-details-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { PaymentDetailsComponent } from './payment-details.component'; import { ROUTING_CONFIG } from './routing-config'; @@ -12,7 +12,7 @@ import { ROUTING_CONFIG } from './routing-config'; { path: '', component: PaymentDetailsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, children: [ { diff --git a/apps/control-center/src/app/sections/payment-details/payment-details.component.html b/apps/control-center/src/app/sections/payment-details/payment-details.component.html index 1e5c78fbe..ef848f55a 100644 --- a/apps/control-center/src/app/sections/payment-details/payment-details.component.html +++ b/apps/control-center/src/app/sections/payment-details/payment-details.component.html @@ -1,10 +1,4 @@ + map(({ paymentID }) => { + const [invoiceId, paymentId] = (paymentID || '').split('.'); + return { invoiceId, paymentId }; + }), + switchMap(({ paymentId, invoiceId }) => this.merchantStatisticsService .SearchPayments( cleanPrimitiveProps({ common_search_query_params: { from_time: new Date('2020-01-01').toISOString(), // TODO to_time: new Date().toISOString(), - party_id: partyID, }, payment_params: { - payment_id: paymentID, + payment_id: paymentId, }, - invoice_ids: [invoiceID], + invoice_ids: [invoiceId], }), ) .pipe( diff --git a/apps/control-center/src/app/sections/payments/components/create-payment-adjustment/create-payment-adjustment.component.ts b/apps/control-center/src/app/sections/payments/components/create-payment-adjustment/create-payment-adjustment.component.ts index c812fa09d..6029ca73a 100644 --- a/apps/control-center/src/app/sections/payments/components/create-payment-adjustment/create-payment-adjustment.component.ts +++ b/apps/control-center/src/app/sections/payments/components/create-payment-adjustment/create-payment-adjustment.component.ts @@ -1,20 +1,18 @@ import { Component, DestroyRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormControl } from '@angular/forms'; -import { InvoicePaymentAdjustmentParams } from '@vality/domain-proto/payment_processing'; +import { metadata$ } from '@vality/domain-proto'; +import { InvoicePaymentAdjustmentParams, Invoicing } from '@vality/domain-proto/payment_processing'; import { StatPayment } from '@vality/magista-proto/magista'; import { DialogSuperclass, ForkJoinErrorResult, NotifyLogService, forkJoinToResult, - getImportValue, splitResultsErrors, } from '@vality/matez'; -import { ThriftAstMetadata } from '@vality/ng-thrift'; import { BehaviorSubject } from 'rxjs'; -import { InvoicingService } from '../../../../api/payment-processing'; import { DomainMetadataFormExtensionsService } from '../../../../shared/services'; @Component({ @@ -27,13 +25,13 @@ export class CreatePaymentAdjustmentComponent extends DialogSuperclass< { payments: StatPayment[] }, { errors?: ForkJoinErrorResult[] } > { - private invoicingService = inject(InvoicingService); + private invoicingService = inject(Invoicing); private log = inject(NotifyLogService); private domainMetadataFormExtensionsService = inject(DomainMetadataFormExtensionsService); private destroyRef = inject(DestroyRef); control = new FormControl(null); progress$ = new BehaviorSubject(0); - metadata$ = getImportValue(import('@vality/domain-proto/metadata.json')); + metadata$ = metadata$; extensions$ = this.domainMetadataFormExtensionsService.extensions$; errors: ForkJoinErrorResult[] = []; diff --git a/apps/control-center/src/app/sections/payments/components/create-payment-adjustments-by-file-dialog/create-payment-adjustments-by-file-dialog.component.ts b/apps/control-center/src/app/sections/payments/components/create-payment-adjustments-by-file-dialog/create-payment-adjustments-by-file-dialog.component.ts index 01da5da43..08a57e524 100644 --- a/apps/control-center/src/app/sections/payments/components/create-payment-adjustments-by-file-dialog/create-payment-adjustments-by-file-dialog.component.ts +++ b/apps/control-center/src/app/sections/payments/components/create-payment-adjustments-by-file-dialog/create-payment-adjustments-by-file-dialog.component.ts @@ -2,7 +2,8 @@ import { CommonModule } from '@angular/common'; import { Component, DestroyRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatButton } from '@angular/material/button'; -import { InvoicePaymentAdjustment } from '@vality/domain-proto/internal/domain'; +import { InvoicePaymentAdjustment } from '@vality/domain-proto/domain'; +import { Invoicing } from '@vality/domain-proto/payment_processing'; import { DEFAULT_DIALOG_CONFIG, DialogModule, @@ -13,7 +14,6 @@ import { import { BehaviorSubject } from 'rxjs'; import { UploadCsvComponent } from '../../../../../components/upload-csv'; -import { InvoicingService } from '../../../../api/payment-processing/invoicing.service'; import { CSV_PAYMENT_ADJUSTMENT_PROPS, CsvPaymentAdjustment } from './types/csv-payment-adjustment'; import { getCreatePaymentAdjustmentsArgs } from './utils/get-create-payment-adjustments-args'; @@ -28,7 +28,7 @@ export class CreatePaymentAdjustmentsByFileDialogComponent extends DialogSupercl void, InvoicePaymentAdjustment[] > { - private invoicingService = inject(InvoicingService); + private invoicingService = inject(Invoicing); private log = inject(NotifyLogService); private destroyRef = inject(DestroyRef); static override defaultDialogConfig = DEFAULT_DIALOG_CONFIG.large; diff --git a/apps/control-center/src/app/sections/payments/components/create-payment-adjustments-by-file-dialog/utils/get-create-payment-adjustments-args.ts b/apps/control-center/src/app/sections/payments/components/create-payment-adjustments-by-file-dialog/utils/get-create-payment-adjustments-args.ts index 3e664919e..e78edbf48 100644 --- a/apps/control-center/src/app/sections/payments/components/create-payment-adjustments-by-file-dialog/utils/get-create-payment-adjustments-args.ts +++ b/apps/control-center/src/app/sections/payments/components/create-payment-adjustments-by-file-dialog/utils/get-create-payment-adjustments-args.ts @@ -1,11 +1,11 @@ -import { CodegenClient } from '@vality/domain-proto/internal/payment_processing-Invoicing'; +import { Invoicing } from '@vality/domain-proto/payment_processing'; import { clean } from '@vality/matez'; import { CsvPaymentAdjustment } from '../types/csv-payment-adjustment'; export function getCreatePaymentAdjustmentsArgs( c: CsvPaymentAdjustment, -): Parameters { +): Parameters { return [ c.invoice_id, c.payment_id, diff --git a/apps/control-center/src/app/sections/payments/components/payments-table/payments-table.component.ts b/apps/control-center/src/app/sections/payments/components/payments-table/payments-table.component.ts index 5640a4752..c52f8b8a5 100644 --- a/apps/control-center/src/app/sections/payments/components/payments-table/payments-table.component.ts +++ b/apps/control-center/src/app/sections/payments/components/payments-table/payments-table.component.ts @@ -90,13 +90,6 @@ export class PaymentsTableComponent { ]; private toDetails(data: StatPayment) { - return void this.router.navigate([ - 'party', - data.owner_id, - 'invoice', - data.invoice_id, - 'payment', - data.id, - ]); + return void this.router.navigate(['payments', `${data.invoice_id}.${data.id}`]); } } diff --git a/apps/control-center/src/app/sections/payments/payments-routing.module.ts b/apps/control-center/src/app/sections/payments/payments-routing.module.ts index 9fd41b2d0..88b0f0a39 100644 --- a/apps/control-center/src/app/sections/payments/payments-routing.module.ts +++ b/apps/control-center/src/app/sections/payments/payments-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { PaymentsComponent } from './payments.component'; import { ROUTING_CONFIG } from './routing-config'; @@ -12,7 +12,7 @@ import { ROUTING_CONFIG } from './routing-config'; { path: '', component: PaymentsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/payments/payments.component.html b/apps/control-center/src/app/sections/payments/payments.component.html index aa0b97125..be4a7017e 100644 --- a/apps/control-center/src/app/sections/payments/payments.component.html +++ b/apps/control-center/src/app/sections/payments/payments.component.html @@ -17,11 +17,7 @@ > - + diff --git a/apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.html b/apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.html similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.html rename to apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.html diff --git a/apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.spec.ts b/apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.spec.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.spec.ts rename to apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.spec.ts diff --git a/apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.ts b/apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.ts similarity index 96% rename from apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.ts rename to apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.ts index a0a09de1c..c0bfabd4e 100644 --- a/apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.ts +++ b/apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.component.ts @@ -4,7 +4,7 @@ import { UntypedFormBuilder } from '@angular/forms'; import { DialogSuperclass } from '@vality/matez'; import { map } from 'rxjs/operators'; -import { RoutingRulesService } from '../services/routing-rules'; +import { RoutingRulesService } from '../../services/routing-rules'; @Component({ selector: 'cc-change-delegate-ruleset-dialog', diff --git a/apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.module.ts b/apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.module.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.module.ts rename to apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/change-delegate-ruleset-dialog.module.ts diff --git a/apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/index.ts b/apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/index.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/change-delegate-ruleset-dialog/index.ts rename to apps/control-center/src/app/sections/routing-rules/components/change-delegate-ruleset-dialog/index.ts diff --git a/apps/control-center/src/app/sections/routing-rules/change-target-dialog/change-target-dialog.component.html b/apps/control-center/src/app/sections/routing-rules/components/change-target-dialog/change-target-dialog.component.html similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/change-target-dialog/change-target-dialog.component.html rename to apps/control-center/src/app/sections/routing-rules/components/change-target-dialog/change-target-dialog.component.html diff --git a/apps/control-center/src/app/sections/routing-rules/change-target-dialog/change-target-dialog.component.ts b/apps/control-center/src/app/sections/routing-rules/components/change-target-dialog/change-target-dialog.component.ts similarity index 93% rename from apps/control-center/src/app/sections/routing-rules/change-target-dialog/change-target-dialog.component.ts rename to apps/control-center/src/app/sections/routing-rules/components/change-target-dialog/change-target-dialog.component.ts index c08d3deba..af7734113 100644 --- a/apps/control-center/src/app/sections/routing-rules/change-target-dialog/change-target-dialog.component.ts +++ b/apps/control-center/src/app/sections/routing-rules/components/change-target-dialog/change-target-dialog.component.ts @@ -3,9 +3,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DialogSuperclass, NotifyLogService } from '@vality/matez'; import { BehaviorSubject } from 'rxjs'; -import { RoutingRulesService } from '../services/routing-rules'; +import { RoutingRulesService } from '../../services/routing-rules'; +import { RoutingRulesType } from '../../types/routing-rules-type'; import { TargetRuleset } from '../target-ruleset-form'; -import { RoutingRulesType } from '../types/routing-rules-type'; @Component({ templateUrl: 'change-target-dialog.component.html', diff --git a/apps/control-center/src/app/sections/routing-rules/change-target-dialog/change-target-dialog.module.ts b/apps/control-center/src/app/sections/routing-rules/components/change-target-dialog/change-target-dialog.module.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/change-target-dialog/change-target-dialog.module.ts rename to apps/control-center/src/app/sections/routing-rules/components/change-target-dialog/change-target-dialog.module.ts diff --git a/apps/control-center/src/app/sections/routing-rules/change-target-dialog/index.ts b/apps/control-center/src/app/sections/routing-rules/components/change-target-dialog/index.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/change-target-dialog/index.ts rename to apps/control-center/src/app/sections/routing-rules/components/change-target-dialog/index.ts diff --git a/apps/control-center/src/app/sections/routing-rules/routing-rules-list/index.ts b/apps/control-center/src/app/sections/routing-rules/components/routing-rules-list/index.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/routing-rules-list/index.ts rename to apps/control-center/src/app/sections/routing-rules/components/routing-rules-list/index.ts diff --git a/apps/control-center/src/app/sections/routing-rules/routing-rules-list/routing-rules-list.component.html b/apps/control-center/src/app/sections/routing-rules/components/routing-rules-list/routing-rules-list.component.html similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/routing-rules-list/routing-rules-list.component.html rename to apps/control-center/src/app/sections/routing-rules/components/routing-rules-list/routing-rules-list.component.html diff --git a/apps/control-center/src/app/sections/routing-rules/routing-rules-list/routing-rules-list.component.ts b/apps/control-center/src/app/sections/routing-rules/components/routing-rules-list/routing-rules-list.component.ts similarity index 98% rename from apps/control-center/src/app/sections/routing-rules/routing-rules-list/routing-rules-list.component.ts rename to apps/control-center/src/app/sections/routing-rules/components/routing-rules-list/routing-rules-list.component.ts index 05dcbf943..b45e9a043 100644 --- a/apps/control-center/src/app/sections/routing-rules/routing-rules-list/routing-rules-list.component.ts +++ b/apps/control-center/src/app/sections/routing-rules/components/routing-rules-list/routing-rules-list.component.ts @@ -24,9 +24,9 @@ import { } from '@vality/matez'; import { catchError, filter, switchMap } from 'rxjs/operators'; +import { RoutingRulesService } from '../../services/routing-rules'; import { ChangeDelegateRulesetDialogComponent } from '../change-delegate-ruleset-dialog'; import { ChangeTargetDialogComponent } from '../change-target-dialog'; -import { RoutingRulesService } from '../services/routing-rules'; type DelegateId = { parentRefId: number; diff --git a/apps/control-center/src/app/sections/routing-rules/routing-rules-list/routing-rules-list.module.ts b/apps/control-center/src/app/sections/routing-rules/components/routing-rules-list/routing-rules-list.module.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/routing-rules-list/routing-rules-list.module.ts rename to apps/control-center/src/app/sections/routing-rules/components/routing-rules-list/routing-rules-list.module.ts diff --git a/apps/control-center/src/app/sections/routing-rules/target-ruleset-form/index.ts b/apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/index.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/target-ruleset-form/index.ts rename to apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/index.ts diff --git a/apps/control-center/src/app/sections/routing-rules/target-ruleset-form/target-ruleset-form.component.html b/apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/target-ruleset-form.component.html similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/target-ruleset-form/target-ruleset-form.component.html rename to apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/target-ruleset-form.component.html diff --git a/apps/control-center/src/app/sections/routing-rules/target-ruleset-form/target-ruleset-form.component.ts b/apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/target-ruleset-form.component.ts similarity index 89% rename from apps/control-center/src/app/sections/routing-rules/target-ruleset-form/target-ruleset-form.component.ts rename to apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/target-ruleset-form.component.ts index 1d2c4d97e..1f2f03c93 100644 --- a/apps/control-center/src/app/sections/routing-rules/target-ruleset-form/target-ruleset-form.component.ts +++ b/apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/target-ruleset-form.component.ts @@ -15,10 +15,10 @@ import { ComponentChanges } from '@vality/matez'; import sortBy from 'lodash-es/sortBy'; import { map, startWith } from 'rxjs/operators'; -import { DomainStoreService } from '../../../api/domain-config/stores/domain-store.service'; -import { RoutingRulesService } from '../services/routing-rules'; -import { RoutingRulesType } from '../types/routing-rules-type'; -import { getPoliciesIdByType } from '../utils/get-policies-id-by-type'; +import { PaymentInstitutionsStoreService } from '../../../../api/domain-config'; +import { RoutingRulesService } from '../../services/routing-rules'; +import { RoutingRulesType } from '../../types/routing-rules-type'; +import { getPoliciesIdByType } from '../../utils/get-policies-id-by-type'; import { Target } from './types/target'; import { TargetRuleset } from './types/target-ruleset'; @@ -31,7 +31,7 @@ import { TargetRuleset } from './types/target-ruleset'; }) export class TargetRulesetFormComponent implements OnChanges { private fb = inject(UntypedFormBuilder); - private domainStoreService = inject(DomainStoreService); + private paymentInstitutionsStoreService = inject(PaymentInstitutionsStoreService); private routingRulesService = inject(RoutingRulesService); private destroyRef = inject(DestroyRef); @Output() valid = new EventEmitter(); @@ -48,9 +48,9 @@ export class TargetRulesetFormComponent implements OnChanges { target = Target; - paymentInstitutions$ = this.domainStoreService - .getObjects('payment_institution') - .pipe(map((r) => sortBy(r, ['ref.id']))); + paymentInstitutions$ = this.paymentInstitutionsStoreService.paymentInstitutions$.pipe( + map((r) => sortBy(r, ['ref.id'])), + ); rulesets$ = this.routingRulesService.rulesets$; get policiesId() { diff --git a/apps/control-center/src/app/sections/routing-rules/target-ruleset-form/target-ruleset-form.module.ts b/apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/target-ruleset-form.module.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/target-ruleset-form/target-ruleset-form.module.ts rename to apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/target-ruleset-form.module.ts diff --git a/apps/control-center/src/app/sections/routing-rules/target-ruleset-form/types/target-ruleset.ts b/apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/types/target-ruleset.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/target-ruleset-form/types/target-ruleset.ts rename to apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/types/target-ruleset.ts diff --git a/apps/control-center/src/app/sections/routing-rules/target-ruleset-form/types/target.ts b/apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/types/target.ts similarity index 100% rename from apps/control-center/src/app/sections/routing-rules/target-ruleset-form/types/target.ts rename to apps/control-center/src/app/sections/routing-rules/components/target-ruleset-form/types/target.ts diff --git a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/attach-new-ruleset-dialog/attach-new-ruleset-dialog.component.ts b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/attach-new-ruleset-dialog/attach-new-ruleset-dialog.component.ts index 18e36e9d0..90dc93a1d 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/attach-new-ruleset-dialog/attach-new-ruleset-dialog.component.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/attach-new-ruleset-dialog/attach-new-ruleset-dialog.component.ts @@ -4,8 +4,8 @@ import { UntypedFormBuilder } from '@angular/forms'; import { DialogSuperclass, NotifyLogService } from '@vality/matez'; import { BehaviorSubject } from 'rxjs'; +import { TargetRuleset } from '../../components/target-ruleset-form'; import { RoutingRulesService } from '../../services/routing-rules'; -import { TargetRuleset } from '../../target-ruleset-form'; import { RoutingRulesType } from '../../types/routing-rules-type'; @Component({ diff --git a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets-routing.module.ts b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets-routing.module.ts index 9f21d8d02..e5c1cab10 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets-routing.module.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../../shared/services'; +import { canActivateAuthRole } from '../../../shared/services'; import { PartyDelegateRulesetsComponent } from './party-delegate-rulesets.component'; import { ROUTING_CONFIG } from './routing-config'; @@ -15,7 +15,7 @@ import { ROUTING_CONFIG } from './routing-config'; { path: '', component: PartyDelegateRulesetsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, { diff --git a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.ts b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.ts index 6d2c4fd90..577518875 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.component.ts @@ -4,9 +4,9 @@ import { ActivatedRoute, Router } from '@angular/router'; import { Column, DialogService, NotifyLogService } from '@vality/matez'; import { catchError, first, map } from 'rxjs/operators'; -import { DomainStoreService } from '../../../api/domain-config/stores/domain-store.service'; +import { RoutingRulesStoreService } from '../../../api/domain-config'; import { createDomainObjectColumn } from '../../../shared'; -import { RoutingRulesListItem } from '../routing-rules-list'; +import { RoutingRulesListItem } from '../components/routing-rules-list'; import { RoutingRulesTypeService } from '../routing-rules-type.service'; import { RoutingRulesService } from '../services/routing-rules'; import { RoutingRulesType } from '../types/routing-rules-type'; @@ -29,11 +29,12 @@ export class PartyDelegateRulesetsComponent { private routingRulesService = inject(RoutingRulesService); private router = inject(Router); private dialogService = inject(DialogService); - private domainStoreService = inject(DomainStoreService); + private routingRulesStoreService = inject(RoutingRulesStoreService); private log = inject(NotifyLogService); private route = inject(ActivatedRoute); private destroyRef = inject(DestroyRef); protected routingRulesTypeService = inject(RoutingRulesTypeService); + columns: Column>[] = [ { field: 'partyDelegate', @@ -56,7 +57,7 @@ export class PartyDelegateRulesetsComponent { { header: 'Main Ruleset' }, ), ]; - isLoading$ = this.domainStoreService.isLoading$; + isLoading$ = this.routingRulesStoreService.isLoading$; data$ = this.partyDelegateRulesetsService.getDelegatesWithPaymentInstitution().pipe( map((rules): RoutingRulesListItem[] => rules.map((item) => ({ @@ -96,7 +97,7 @@ export class PartyDelegateRulesetsComponent { .pipe(first(), takeUntilDestroyed(this.destroyRef)) .subscribe((parent) => { void this.router.navigate([ - 'party', + 'parties', this.partyID, 'routing-rules', this.route.snapshot.params['type'] as RoutingRulesType, diff --git a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.module.ts b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.module.ts index bd241824c..a4c7d9456 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.module.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.module.ts @@ -16,9 +16,9 @@ import { RouterModule } from '@angular/router'; import { DialogModule } from '@vality/matez'; import { PageLayoutModule } from '../../../shared'; -import { ChangeTargetDialogModule } from '../change-target-dialog'; -import { RoutingRulesListModule } from '../routing-rules-list'; -import { TargetRulesetFormModule } from '../target-ruleset-form'; +import { ChangeTargetDialogModule } from '../components/change-target-dialog'; +import { RoutingRulesListModule } from '../components/routing-rules-list'; +import { TargetRulesetFormModule } from '../components/target-ruleset-form'; import { AttachNewRulesetDialogComponent } from './attach-new-ruleset-dialog'; import { PartyDelegateRulesetsRoutingModule } from './party-delegate-rulesets-routing.module'; diff --git a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.service.ts b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.service.ts index 299b443a3..967a837d8 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.service.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/party-delegate-rulesets.service.ts @@ -10,7 +10,7 @@ import isNil from 'lodash-es/isNil'; import { Observable, combineLatest, of } from 'rxjs'; import { map, startWith, switchMap } from 'rxjs/operators'; -import { DomainStoreService } from '../../../api/domain-config/stores/domain-store.service'; +import { PaymentInstitutionsStoreService } from '../../../api/domain-config'; import { RoutingRulesService } from '../services/routing-rules'; import { RoutingRulesType } from '../types/routing-rules-type'; import { getPoliciesIdByType } from '../utils/get-policies-id-by-type'; @@ -23,9 +23,10 @@ export interface DelegateWithPaymentInstitution { @Injectable() export class PartyDelegateRulesetsService { - private domainStoreService = inject(DomainStoreService); + private paymentInstitutionsStoreService = inject(PaymentInstitutionsStoreService); private route = inject(ActivatedRoute); private routingRulesService = inject(RoutingRulesService); + private partyID$ = this.route.params.pipe( startWith(this.route.snapshot.params), map((p) => p['partyID']), @@ -74,7 +75,7 @@ export class PartyDelegateRulesetsService { private getPaymentInstitutionsWithRoutingRule(type?: RoutingRulesType) { return combineLatest([ - this.domainStoreService.getObjects('payment_institution'), + this.paymentInstitutionsStoreService.paymentInstitutions$, this.routingRulesType$.pipe(map((routeType) => type ?? routeType)), ]).pipe( switchMap(([paymentInstitutions, routingRulesType]) => { diff --git a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/routing-config.ts b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/routing-config.ts index 75c3217e6..d6dcad938 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/routing-config.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-delegate-rulesets/routing-config.ts @@ -1,5 +1,5 @@ import { RoutingConfig, Services } from '../../../shared/services'; export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.Domain], + services: [Services.DMT], }; diff --git a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/add-party-routing-rule-dialog/add-party-routing-rule-dialog.component.ts b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/add-party-routing-rule-dialog/add-party-routing-rule-dialog.component.ts index 699803a6f..9bee25350 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/add-party-routing-rule-dialog/add-party-routing-rule-dialog.component.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/add-party-routing-rule-dialog/add-party-routing-rule-dialog.component.ts @@ -1,9 +1,12 @@ import { Component, DestroyRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormBuilder } from '@angular/forms'; -import { Shop } from '@vality/domain-proto/domain'; -import { ShopID, WalletID } from '@vality/domain-proto/internal/domain'; -import { StatWallet } from '@vality/fistful-proto/fistful_stat'; +import { + ShopConfigObject, + ShopID, + WalletConfigObject, + WalletID, +} from '@vality/domain-proto/domain'; import { DialogResponseStatus, DialogSuperclass, NotifyLogService, Option } from '@vality/matez'; import { RoutingRulesService } from '../../services/routing-rules'; @@ -15,12 +18,19 @@ import { RoutingRulesType } from '../../types/routing-rules-type'; }) export class AddPartyRoutingRuleDialogComponent extends DialogSuperclass< AddPartyRoutingRuleDialogComponent, - { refID: number; partyID: string; shops: Shop[]; wallets: StatWallet[]; type: RoutingRulesType } + { + refID: number; + partyID: string; + shops: ShopConfigObject[]; + wallets: WalletConfigObject[]; + type: RoutingRulesType; + } > { private fb = inject(FormBuilder); private routingRulesService = inject(RoutingRulesService); private log = inject(NotifyLogService); - private destroyRef = inject(DestroyRef); + private dr = inject(DestroyRef); + form = this.fb.group<{ shopID: string; walletID: string; name: string; description: string }>({ shopID: null, walletID: null, @@ -29,15 +39,15 @@ export class AddPartyRoutingRuleDialogComponent extends DialogSuperclass< }); shopsOptions: Option[] = this.dialogData.shops.map((s) => ({ - value: s.id, - label: s.details.name, - description: s.id, + value: s.ref.id, + label: s.data.details.name, + description: s.ref.id, })); walletsOptions: Option[] = this.dialogData.wallets.map((s) => ({ - value: s.id, - label: s.name, - description: s.id, + value: s.ref.id, + label: s.data.name, + description: s.ref.id, })); add() { @@ -53,7 +63,7 @@ export class AddPartyRoutingRuleDialogComponent extends DialogSuperclass< ? { shop_is: shopID } : { wallet_is: walletID }, }) - .pipe(takeUntilDestroyed(this.destroyRef)) + .pipe(takeUntilDestroyed(this.dr)) .subscribe({ next: () => this.dialogRef.close({ status: DialogResponseStatus.Success }), error: this.log.error, diff --git a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset-routing.module.ts b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset-routing.module.ts index 02200524e..ad2460871 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset-routing.module.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../../shared/services'; +import { canActivateAuthRole } from '../../../shared/services'; import { PartyRoutingRulesetComponent } from './party-routing-ruleset.component'; import { ROUTING_CONFIG } from './routing-config'; @@ -12,7 +12,7 @@ import { ROUTING_CONFIG } from './routing-config'; { path: ':partyRefID', component: PartyRoutingRulesetComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.html b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.html index 24e09c130..35f5c78aa 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.html +++ b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.component.html @@ -1,12 +1,7 @@ + `/parties/${partyID}/routing-rules/${routingRulesType}`, + ), + shareReplay({ refCount: true, bufferSize: 1 }), + ); shopsDisplayedColumns: Column>[] = [ { @@ -107,6 +122,17 @@ export class PartyRoutingRulesetComponent { shareReplay(1), ); + constructor() { + combineLatest([this.partyRoutingRulesetService.refID$, this.upLink$]) + .pipe(first()) + .subscribe(([refID, upLink]) => { + if (!refID) { + this.log.error('Main ref id not found'); + this.router.navigate([upLink]); + } + }); + } + add() { this.partyRuleset$.pipe(take(1)).subscribe((partyRuleset) => { if (partyRuleset) { @@ -122,7 +148,7 @@ export class PartyRoutingRulesetComponent { .pipe(take(1), takeUntilDestroyed(this.destroyRef)) .subscribe((ruleset) => this.router.navigate([ - 'party', + 'parties', this.route.snapshot.params['partyID'], 'routing-rules', this.route.snapshot.params['type'], @@ -182,21 +208,21 @@ export class PartyRoutingRulesetComponent { ruleset.data.decisions.delegates.every( (d) => d?.allowed?.condition?.party?.definition?.shop_is !== - s.id, + s.ref.id, ), ) .sort((a, b) => - compareDifferentTypes(a.details.name, b.details.name), + compareDifferentTypes(a.data.details.name, b.data.details.name), ), wallets: wallets .filter((w) => ruleset.data.decisions.delegates.every( (d) => d?.allowed?.condition?.party?.definition?.wallet_is !== - w.id, + w.ref.id, ), ) - .sort((a, b) => compareDifferentTypes(a.name, b.name)), + .sort((a, b) => compareDifferentTypes(a.data.name, b.data.name)), type, partyID, }) diff --git a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.module.ts b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.module.ts index 7eddfa90b..b2f137837 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.module.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.module.ts @@ -17,8 +17,8 @@ import { MatSelectModule } from '@angular/material/select'; import { RouterModule } from '@angular/router'; import { PageLayoutModule } from '../../../shared'; -import { ChangeTargetDialogModule } from '../change-target-dialog'; -import { RoutingRulesListModule } from '../routing-rules-list'; +import { ChangeTargetDialogModule } from '../components/change-target-dialog'; +import { RoutingRulesListModule } from '../components/routing-rules-list'; import { AddPartyRoutingRuleDialogModule } from './add-party-routing-rule-dialog'; import { InitializeRoutingRulesDialogModule } from './initialize-routing-rules-dialog'; diff --git a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.service.ts b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.service.ts index c94ddf1fc..dd265df5f 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.service.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/party-routing-ruleset.service.ts @@ -3,12 +3,10 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute } from '@angular/router'; import { NotifyLogService } from '@vality/matez'; import isNil from 'lodash-es/isNil'; -import { Observable, combineLatest, defer, of } from 'rxjs'; +import { Observable, combineLatest, of } from 'rxjs'; import { map, shareReplay, switchMap, take, tap } from 'rxjs/operators'; -import { createDsl } from '../../../api/fistful-stat'; -import { FistfulStatisticsService } from '../../../api/fistful-stat/fistful-statistics.service'; -import { PartyManagementService } from '../../../api/payment-processing/party-management.service'; +import { PartiesStoreService } from '../../../api/payment-processing'; import { PartyDelegateRulesetsService } from '../party-delegate-rulesets'; import { RoutingRulesService } from '../services/routing-rules'; @@ -17,15 +15,15 @@ export const MAIN_REF = 'main'; @Injectable() export class PartyRoutingRulesetService { private route = inject(ActivatedRoute); - private partyManagementService = inject(PartyManagementService); + private partiesStoreService = inject(PartiesStoreService); private routingRulesService = inject(RoutingRulesService); - private fistfulStatistics = inject(FistfulStatisticsService); - private destroyRef = inject(DestroyRef); + private dr = inject(DestroyRef); private partyDelegateRulesetsService = inject(PartyDelegateRulesetsService); private log = inject(NotifyLogService); + partyID$ = this.route.params.pipe( map((r) => r['partyID']), - takeUntilDestroyed(this.destroyRef), + takeUntilDestroyed(this.dr), shareReplay(1), ) as Observable; refID$ = this.route.params.pipe( @@ -43,35 +41,22 @@ export class PartyRoutingRulesetService { this.log.error('Unknown delegate'); } }), - takeUntilDestroyed(this.destroyRef), + takeUntilDestroyed(this.dr), shareReplay(1), ); - shops$ = defer(() => this.party$).pipe( - map((p) => p.shops), - map((shops) => Array.from(shops.values())), + shops$ = this.partyID$.pipe( + switchMap((id) => this.partiesStoreService.getPartyShops(id).value$), shareReplay({ refCount: true, bufferSize: 1 }), ); - wallets$ = defer(() => this.partyID$).pipe( - switchMap((partyID) => - this.fistfulStatistics.GetWallets({ - dsl: createDsl({ wallets: { party_id: partyID } }), - }), - ), - map((v) => v?.data?.wallets), - takeUntilDestroyed(this.destroyRef), - shareReplay(1), + wallets$ = this.partyID$.pipe( + switchMap((id) => this.partiesStoreService.getPartyWallets(id).value$), + shareReplay({ refCount: true, bufferSize: 1 }), ); partyRuleset$ = combineLatest([this.routingRulesService.rulesets$, this.refID$]).pipe( map(([rules, refID]) => rules.find((r) => r?.ref?.id === refID)), - takeUntilDestroyed(this.destroyRef), - shareReplay(1), - ); - - private party$ = this.partyID$.pipe( - switchMap((partyID) => this.partyManagementService.Get(partyID)), - takeUntilDestroyed(this.destroyRef), + takeUntilDestroyed(this.dr), shareReplay(1), ); diff --git a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/routing-config.ts b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/routing-config.ts index 75c3217e6..d6dcad938 100644 --- a/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/routing-config.ts +++ b/apps/control-center/src/app/sections/routing-rules/party-routing-ruleset/routing-config.ts @@ -1,5 +1,5 @@ import { RoutingConfig, Services } from '../../../shared/services'; export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.Domain], + services: [Services.DMT], }; diff --git a/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-config.ts b/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-config.ts index 75c3217e6..d6dcad938 100644 --- a/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-config.ts +++ b/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-config.ts @@ -1,5 +1,5 @@ import { RoutingConfig, Services } from '../../../shared/services'; export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.Domain], + services: [Services.DMT], }; diff --git a/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset-routing.module.ts b/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset-routing.module.ts index a0217a12f..f20c1c0ce 100644 --- a/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset-routing.module.ts +++ b/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../../shared/services'; +import { canActivateAuthRole } from '../../../shared/services'; import { ROUTING_CONFIG } from './routing-config'; import { RoutingRulesetComponent } from './routing-ruleset.component'; @@ -12,7 +12,7 @@ import { RoutingRulesetComponent } from './routing-ruleset.component'; { path: ':partyRefID/delegate/:refID', component: RoutingRulesetComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.html b/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.html index 4d3924a9c..98a085023 100644 --- a/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.html +++ b/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.html @@ -2,7 +2,7 @@ [id]="(ruleset$ | async)?.ref?.id" [title]="((routingRulesType$ | async) === 'payment' ? 'Shop' : 'Wallet') + ' Routing Rules'" [upLink]="[ - '/party/' + + '/parties/' + (partyID$ | async) + '/routing-rules/' + (routingRulesType$ | async) + diff --git a/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.ts b/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.ts index df54afad9..cea8a42cd 100644 --- a/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.ts +++ b/apps/control-center/src/app/sections/routing-rules/routing-ruleset/routing-ruleset.component.ts @@ -17,7 +17,7 @@ import cloneDeep from 'lodash-es/cloneDeep'; import { Observable, combineLatest, filter } from 'rxjs'; import { first, map, switchMap, take, withLatestFrom } from 'rxjs/operators'; -import { DomainStoreService } from '../../../api/domain-config/stores/domain-store.service'; +import { DomainService, RoutingRulesStoreService } from '../../../api/domain-config'; import { createDomainObjectColumn, createPredicateColumn, @@ -26,10 +26,10 @@ import { import { CandidateCardComponent } from '../../../shared/components/candidate-card/candidate-card.component'; import { SidenavInfoService } from '../../../shared/components/sidenav-info'; import { - DomainObjectCardComponent, DomainThriftFormDialogComponent, UpdateThriftDialogComponent, } from '../../../shared/components/thrift-api-crud'; +import { DomainObjectCardComponent } from '../../../shared/components/thrift-api-crud/domain2'; import { RoutingRulesService } from '../services/routing-rules'; import { RoutingRulesType } from '../types/routing-rules-type'; import { changeCandidatesAllowed } from '../utils/toggle-candidate-allowed'; @@ -45,11 +45,13 @@ export class RoutingRulesetComponent { private dialog = inject(DialogService); private routingRulesetService = inject(RoutingRulesetService); private routingRulesService = inject(RoutingRulesService); - private domainStoreService = inject(DomainStoreService); + private routingRulesStoreService = inject(RoutingRulesStoreService); + private domainService = inject(DomainService); private log = inject(NotifyLogService); private route = inject(ActivatedRoute); private sidenavInfoService = inject(SidenavInfoService); private destroyRef = inject(DestroyRef); + ruleset$ = this.routingRulesetService.ruleset$; partyID$ = this.routingRulesetService.partyID$; partyRulesetRefID$ = this.routingRulesetService.partyRulesetRefID$; @@ -57,7 +59,8 @@ export class RoutingRulesetComponent { map((p) => p['type']), ) as Observable; candidates$ = this.routingRulesetService.ruleset$.pipe(map((r) => r.data.decisions.candidates)); - isLoading$ = this.domainStoreService.isLoading$; + isLoading$ = this.routingRulesStoreService.isLoading$; + columns: Column[] = [ { field: 'priority' }, { @@ -86,11 +89,11 @@ export class RoutingRulesetComponent { createPredicateColumn( (d) => combineLatest([ - this.domainStoreService.getObjects('terminal'), + this.domainService.get({ terminal: { id: d.terminal.id } }), this.routingRulesType$, ]).pipe( - map(([terminals, type]) => { - const terms = terminals.find((t) => t.ref.id === d.terminal.id).data?.terms; + map(([terminal, type]) => { + const terms = terminal?.object?.terminal?.data?.terms; return { predicate: type === RoutingRulesType.Payment @@ -190,7 +193,7 @@ export class RoutingRulesetComponent { .subscribe({ next: (res) => { if (res.status === DialogResponseStatus.Success) { - this.domainStoreService.forceReload(); + this.routingRulesStoreService.reload(); this.log.successOperation('create', 'Routing rule'); } }, @@ -212,8 +215,10 @@ export class RoutingRulesetComponent { type: 'RoutingCandidate', title: `Edit routing candidate #${idx + 1}`, object: candidate, - action: (params) => - this.routingRulesService.updateRule(refId, idx, params), + action: (newCandidate) => + this.routingRulesService.updateRules([ + { refId, candidateIdx: idx, newCandidate }, + ]), }) .afterClosed(), ), @@ -222,7 +227,7 @@ export class RoutingRulesetComponent { .subscribe({ next: (res) => { if (res.status === DialogResponseStatus.Success) { - this.domainStoreService.forceReload(); + this.routingRulesStoreService.reload(); this.log.successOperation('update', 'Routing rule'); } }, @@ -254,7 +259,7 @@ export class RoutingRulesetComponent { .subscribe({ next: (res) => { if (res.status === DialogResponseStatus.Success) { - this.domainStoreService.forceReload(); + this.routingRulesStoreService.reload(); this.log.successOperation('create', 'Routing rule'); } }, @@ -314,23 +319,16 @@ export class RoutingRulesetComponent { ]) => { const newRuleset = cloneDeep(ruleset); newRuleset.data.decisions.candidates = candidates as RoutingCandidate[]; - return this.domainStoreService.commit({ - ops: [ - { - update: { - old_object: { routing_rules: ruleset }, - new_object: { routing_rules: newRuleset }, - }, - }, - ], - }); + return this.routingRulesStoreService.commit([ + { update: { object: { routing_rules: newRuleset } } }, + ]); }, ), ) .subscribe({ next: () => { this.log.successOperation('update', 'candidates'); - this.domainStoreService.forceReload(); + this.routingRulesStoreService.reload(); }, error: (err) => { this.log.error(err); diff --git a/apps/control-center/src/app/sections/routing-rules/services/routing-rules/routing-rules.service.ts b/apps/control-center/src/app/sections/routing-rules/services/routing-rules/routing-rules.service.ts index 8b143547b..eac93ac29 100644 --- a/apps/control-center/src/app/sections/routing-rules/services/routing-rules/routing-rules.service.ts +++ b/apps/control-center/src/app/sections/routing-rules/services/routing-rules/routing-rules.service.ts @@ -1,14 +1,18 @@ import { Injectable, inject } from '@angular/core'; -import { RoutingCandidate, RoutingDelegate, RoutingRulesObject } from '@vality/domain-proto/domain'; -import { Version } from '@vality/domain-proto/domain_config'; -import { PartyConditionDefinition } from '@vality/domain-proto/internal/domain'; +import { + PartyConditionDefinition, + RoutingCandidate, + RoutingDelegate, + RoutingRulesObject, + RoutingRuleset, +} from '@vality/domain-proto/domain'; import { uniq } from 'lodash-es'; import cloneDeep from 'lodash-es/cloneDeep'; import { Observable, combineLatest, concat } from 'rxjs'; import { map, shareReplay, switchMap, take } from 'rxjs/operators'; import { createNextId } from '../../../../../utils/create-next-id'; -import { DomainStoreService } from '../../../../api/domain-config/stores/domain-store.service'; +import { RoutingRulesStoreService } from '../../../../api/domain-config'; import { CandidateId } from './types/candidate-id'; import { getDelegate } from './utils/get-delegate'; @@ -18,13 +22,12 @@ import { getUpdateRulesCandidates } from './utils/get-update-rules-candidates'; providedIn: 'root', }) export class RoutingRulesService { - private domainStoreService = inject(DomainStoreService); - rulesets$: Observable = this.domainStoreService - .getObjects('routing_rules') - .pipe( - map((r) => r.sort((a, b) => a.ref.id - b.ref.id)), - shareReplay(1), - ); + private routingRulesStoreService = inject(RoutingRulesStoreService); + + rulesets$: Observable = this.routingRulesStoreService.routingRules$.pipe( + map((r) => r.sort((a, b) => a.ref.id - b.ref.id)), + shareReplay(1), + ); nextRefID$ = this.rulesets$.pipe( map((rulesets) => rulesets.map(({ ref }) => ref.id)), @@ -54,18 +57,15 @@ export class RoutingRulesService { partyID: string; description?: string; delegateDescription?: string; - }): Observable { + }) { return combineLatest([this.getRuleset(mainRulesetRefID), this.nextRefID$]).pipe( take(1), switchMap(([mainRuleset, id]) => { - const ruleset: RoutingRulesObject = { - ref: { id }, - data: { - name, - description, - decisions: { - delegates: [], - }, + const ruleset: RoutingRuleset = { + name, + description, + decisions: { + delegates: [], }, }; const newMainRuleset = this.cloneRulesetAndPushDelegate(mainRuleset, { @@ -79,21 +79,15 @@ export class RoutingRulesService { }, }, }); - return this.domainStoreService.commit({ - ops: [ - { - insert: { - object: { routing_rules: ruleset }, - }, - }, - { - update: { - old_object: { routing_rules: mainRuleset }, - new_object: { routing_rules: newMainRuleset }, - }, + return this.routingRulesStoreService.commit([ + { + insert: { + object: { routing_rules: ruleset }, + force_ref: { routing_rules: { id } }, }, - ], - }); + }, + { update: { object: { routing_rules: newMainRuleset } } }, + ]); }), ); } @@ -110,18 +104,15 @@ export class RoutingRulesService { partyID: string; partyRulesetRefID: number; description?: string; - }): Observable { + }) { return combineLatest([this.getRuleset(partyRulesetRefID), this.nextRefID$]).pipe( take(1), switchMap(([partyRuleset, id]) => { - const ruleset: RoutingRulesObject = { - ref: { id }, - data: { - name, - description, - decisions: { - candidates: [], - }, + const ruleset: RoutingRuleset = { + name, + description, + decisions: { + candidates: [], }, }; const newPartyRuleset = this.cloneRulesetAndPushDelegate(partyRuleset, { @@ -135,83 +126,43 @@ export class RoutingRulesService { }, }, }); - return this.domainStoreService.commit({ - ops: [ - { - insert: { - object: { routing_rules: ruleset }, - }, + return this.routingRulesStoreService.commit([ + { + insert: { + object: { routing_rules: ruleset }, + force_ref: { routing_rules: { id } }, }, - { - update: { - old_object: { routing_rules: partyRuleset }, - new_object: { routing_rules: newPartyRuleset }, - }, - }, - ], - }); + }, + { + update: { object: { routing_rules: newPartyRuleset } }, + }, + ]); }), ); } - addRule(refID: number, params: RoutingCandidate): Observable { + addRule(refID: number, params: RoutingCandidate) { return this.getRuleset(refID).pipe( take(1), switchMap((ruleset) => { const newShopRuleset = this.cloneRulesetAndPushCandidate(ruleset, params); - return this.domainStoreService.commit({ - ops: [ - { - update: { - old_object: { routing_rules: ruleset }, - new_object: { routing_rules: newShopRuleset }, - }, - }, - ], - }); - }), - ); - } - - // @deprecated use updateRules - updateRule( - refID: number, - candidateIdx: number, - candidate: RoutingCandidate, - ): Observable { - return this.getRuleset(refID).pipe( - take(1), - switchMap((ruleset) => { - const newRuleset = cloneDeep(ruleset); - newRuleset.data.decisions.candidates.splice(candidateIdx, 1, candidate); - return this.domainStoreService.commit({ - ops: [ - { - update: { - old_object: { routing_rules: ruleset }, - new_object: { routing_rules: newRuleset }, - }, - }, - ], - }); + return this.routingRulesStoreService.commit([ + { update: { object: { routing_rules: newShopRuleset } } }, + ]); }), ); } - updateRules( - candidates: (CandidateId & { newCandidate: RoutingCandidate })[], - ): Observable { + updateRules(candidates: (CandidateId & { newCandidate: RoutingCandidate })[]) { return combineLatest( uniq(candidates.map((c) => c.refId)).map((refId) => this.getRuleset(refId).pipe(take(1)), ), ).pipe( switchMap((rulesets) => - this.domainStoreService.commit({ - ops: getUpdateRulesCandidates(rulesets, candidates).map((update) => ({ - update, - })), - }), + this.routingRulesStoreService.commit( + getUpdateRulesCandidates(rulesets, candidates).map((update) => ({ update })), + ), ), ); } @@ -226,7 +177,7 @@ export class RoutingRulesService { partyID: string; mainDelegateDescription?: string; ruleset: { name: string; description?: string }; - }): Observable { + }) { return combineLatest([this.getRuleset(mainRulesetRefID), this.nextRefID$]).pipe( take(1), switchMap(([mainRuleset, id]) => { @@ -247,45 +198,30 @@ export class RoutingRulesService { decisions: { delegates: [] }, }, }; - return this.domainStoreService.commit({ - ops: [ - { - insert: { object: { routing_rules: ruleset } }, - }, - { - update: { - old_object: { routing_rules: mainRuleset }, - new_object: { routing_rules: newMainPaymentRoutingRuleset }, - }, + return this.routingRulesStoreService.commit([ + { + insert: { + object: { routing_rules: ruleset.data }, + force_ref: { routing_rules: { id } }, }, - ], - }); + }, + { + update: { object: { routing_rules: newMainPaymentRoutingRuleset } }, + }, + ]); }), ); } - removeShopRule({ - refID, - candidateIdx, - }: { - refID: number; - candidateIdx: number; - }): Observable { + removeShopRule({ refID, candidateIdx }: { refID: number; candidateIdx: number }) { return this.getRuleset(refID).pipe( take(1), switchMap((shopRuleset) => { const newShopRule = cloneDeep(shopRuleset); newShopRule.data.decisions.candidates.splice(candidateIdx, 1); - return this.domainStoreService.commit({ - ops: [ - { - update: { - old_object: { routing_rules: shopRuleset }, - new_object: { routing_rules: newShopRule }, - }, - }, - ], - }); + return this.routingRulesStoreService.commit([ + { update: { object: { routing_rules: newShopRule } } }, + ]); }), ); } @@ -296,22 +232,15 @@ export class RoutingRulesService { }: { mainRulesetRefID: number; delegateIdx: number; - }): Observable { + }) { return this.getRuleset(mainRulesetRefID).pipe( take(1), switchMap((mainRuleset) => { const newMainPaymentRoutingRuleset = cloneDeep(mainRuleset); newMainPaymentRoutingRuleset.data.decisions.delegates.splice(delegateIdx, 1); - return this.domainStoreService.commit({ - ops: [ - { - update: { - old_object: { routing_rules: mainRuleset }, - new_object: { routing_rules: newMainPaymentRoutingRuleset }, - }, - }, - ], - }); + return this.routingRulesStoreService.commit([ + { update: { object: { routing_rules: newMainPaymentRoutingRuleset } } }, + ]); }), ); } @@ -326,7 +255,7 @@ export class RoutingRulesService { mainRulesetRefID: number; delegateIdx: number; mainDelegateDescription?: string; - }): Observable { + }) { return combineLatest([ this.getRuleset(mainRulesetRefID), this.getRuleset(previousMainRulesetRefID), @@ -346,22 +275,14 @@ export class RoutingRulesService { ...delegate, description: mainDelegateDescription, }); - return this.domainStoreService.commit({ - ops: [ - { - update: { - old_object: { routing_rules: previousMainRuleset }, - new_object: { routing_rules: newPreviousMainRuleset }, - }, - }, - { - update: { - old_object: { routing_rules: mainRuleset }, - new_object: { routing_rules: newMainPaymentRoutingRuleset }, - }, - }, - ], - }); + return this.routingRulesStoreService.commit([ + { + update: { object: { routing_rules: newPreviousMainRuleset } }, + }, + { + update: { object: { routing_rules: newMainPaymentRoutingRuleset } }, + }, + ]); }), ); } @@ -376,7 +297,7 @@ export class RoutingRulesService { delegateIdx: number; newDelegateRulesetRefID: number; description?: string; - }): Observable { + }) { return this.getRuleset(mainRulesetRefID).pipe( take(1), switchMap((mainRuleset) => { @@ -386,16 +307,9 @@ export class RoutingRulesService { if (description !== undefined) { newMainRuleset.data.decisions.delegates[delegateIdx].description = description; } - return this.domainStoreService.commit({ - ops: [ - { - update: { - old_object: { routing_rules: mainRuleset }, - new_object: { routing_rules: newMainRuleset }, - }, - }, - ], - }); + return this.routingRulesStoreService.commit([ + { update: { object: { routing_rules: newMainRuleset } } }, + ]); }), ); } @@ -421,32 +335,24 @@ export class RoutingRulesService { const newDelegateRuleset = cloneDeep(delegateRuleset); newDelegateRuleset.ref.id = nextRefID; return concat( - this.domainStoreService.commit({ - ops: [ - { - insert: { - object: { routing_rules: newDelegateRuleset }, - }, - }, - ], - }), - this.domainStoreService.commit({ - ops: [ - { - update: { - old_object: { routing_rules: mainRuleset }, - new_object: { routing_rules: newMainRuleset }, - }, + this.routingRulesStoreService.commit([ + { + insert: { + object: { routing_rules: newDelegateRuleset.data }, + force_ref: { routing_rules: newDelegateRuleset.ref }, }, - ], - }), + }, + ]), + this.routingRulesStoreService.commit([ + { update: { object: { routing_rules: newMainRuleset } } }, + ]), ); }), ); } reload(): void { - this.domainStoreService.forceReload(); + this.routingRulesStoreService.reload(); } private cloneRulesetAndPushDelegate(ruleset: RoutingRulesObject, delegate: RoutingDelegate) { diff --git a/apps/control-center/src/app/sections/routing-rules/services/routing-rules/utils/get-update-rules-candidates.ts b/apps/control-center/src/app/sections/routing-rules/services/routing-rules/utils/get-update-rules-candidates.ts index 68dc0df8e..08be91a05 100644 --- a/apps/control-center/src/app/sections/routing-rules/services/routing-rules/utils/get-update-rules-candidates.ts +++ b/apps/control-center/src/app/sections/routing-rules/services/routing-rules/utils/get-update-rules-candidates.ts @@ -1,5 +1,5 @@ import { RoutingCandidate, RoutingRulesObject } from '@vality/domain-proto/domain'; -import { UpdateOp } from '@vality/domain-proto/domain_config'; +import { UpdateOp } from '@vality/domain-proto/domain_config_v2'; import { cloneDeep } from 'lodash-es'; import { CandidateId } from '../types/candidate-id'; @@ -20,8 +20,7 @@ export function getUpdateRulesCandidates( } } } - return rulesets.map((ruleset, idx) => ({ - old_object: { routing_rules: ruleset }, - new_object: { routing_rules: newRulesets[idx] }, + return rulesets.map((_, idx) => ({ + object: { routing_rules: newRulesets[idx] }, })); } diff --git a/apps/control-center/src/app/sections/routing-rules/utils/toggle-candidate-allowed.ts b/apps/control-center/src/app/sections/routing-rules/utils/toggle-candidate-allowed.ts index 292012476..af7d64c97 100644 --- a/apps/control-center/src/app/sections/routing-rules/utils/toggle-candidate-allowed.ts +++ b/apps/control-center/src/app/sections/routing-rules/utils/toggle-candidate-allowed.ts @@ -5,7 +5,7 @@ import { DialogResponseStatus, DialogService, NotifyLogService } from '@vality/m import { uniq } from 'lodash-es'; import { combineLatest, switchMap, take } from 'rxjs'; -import { DomainStoreService } from '../../../api/domain-config'; +import { RoutingRulesStoreService } from '../../../api/domain-config'; import { UpdateThriftDialogComponent } from '../../../shared/components/thrift-api-crud'; import { RoutingRulesService } from '../services/routing-rules'; import { CandidateId } from '../services/routing-rules/types/candidate-id'; @@ -20,9 +20,10 @@ function getCandidateIdLabel({ refId, candidateIdx }: CandidateId) { export function changeCandidatesAllowed(candidateIds: CandidateId[], allowedOrToggle?: boolean) { const routingRulesService = inject(RoutingRulesService); const dr = inject(DestroyRef); - const domainStoreService = inject(DomainStoreService); + const routingRulesStoreService = inject(RoutingRulesStoreService); const log = inject(NotifyLogService); const dialogService = inject(DialogService); + combineLatest([ combineLatest( candidateIds.map(({ refId, candidateIdx }) => @@ -65,12 +66,10 @@ export function changeCandidatesAllowed(candidateIds: CandidateId[], allowedOrTo ]) : getChangedPredicate(candidates[0].allowed, allowedOrToggle), prevReviewObject: - candidates.length > 1 - ? reviewObjects.map((r) => r.old_object) - : undefined, + candidates.length > 1 ? rulesets.map((r) => r) : undefined, reviewObject: candidates.length > 1 - ? reviewObjects.map((r) => r.new_object) + ? reviewObjects.map((r) => r.object.routing_rules) : undefined, action: () => routingRulesService.updateRules(candidatesForUpdate), }) @@ -80,7 +79,7 @@ export function changeCandidatesAllowed(candidateIds: CandidateId[], allowedOrTo ) .subscribe((res) => { if (res.status === DialogResponseStatus.Success) { - domainStoreService.forceReload(); + routingRulesStoreService.reload(); log.successOperation('update', 'Allowed'); } }); diff --git a/apps/control-center/src/app/sections/search-parties/routing-config.ts b/apps/control-center/src/app/sections/search-parties/routing-config.ts deleted file mode 100644 index bdb23bfad..000000000 --- a/apps/control-center/src/app/sections/search-parties/routing-config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { RoutingConfig, Services } from '../../shared/services'; - -export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.Deanonimus], -}; diff --git a/apps/control-center/src/app/sections/search-parties/search-parties-routing.module.ts b/apps/control-center/src/app/sections/search-parties/search-parties-routing.module.ts deleted file mode 100644 index eac0490e7..000000000 --- a/apps/control-center/src/app/sections/search-parties/search-parties-routing.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { AppAuthGuardService } from '../../shared/services'; - -import { ROUTING_CONFIG } from './routing-config'; -import { SearchPartiesComponent } from './search-parties.component'; - -@NgModule({ - imports: [ - RouterModule.forChild([ - { - path: 'parties', - component: SearchPartiesComponent, - canActivate: [AppAuthGuardService], - data: ROUTING_CONFIG, - }, - { - path: 'party', - redirectTo: 'parties', - }, - ]), - ], - exports: [RouterModule], -}) -export class SearchPartiesRoutingModule {} diff --git a/apps/control-center/src/app/sections/search-parties/search-parties.component.ts b/apps/control-center/src/app/sections/search-parties/search-parties.component.ts deleted file mode 100644 index 1ad41c40d..000000000 --- a/apps/control-center/src/app/sections/search-parties/search-parties.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Component, inject } from '@angular/core'; -import { Router } from '@angular/router'; -import { Party } from '@vality/deanonimus-proto/deanonimus'; -import { - Column, - DebounceTime, - QueryParamsService, - UpdateOptions, - createMenuColumn, -} from '@vality/matez'; -import { getUnionKey } from '@vality/ng-thrift'; -import startCase from 'lodash-es/startCase'; -import { map } from 'rxjs/operators'; - -import { FetchPartiesService } from '../../shared/services/fetch-parties.service'; - -@Component({ - templateUrl: 'search-parties.component.html', - styleUrls: ['search-parties.component.scss'], - providers: [FetchPartiesService], - standalone: false, -}) -export class SearchPartiesComponent { - private qp = inject>(QueryParamsService<{ text: string }>); - private fetchPartiesService = inject(FetchPartiesService); - private router = inject(Router); - initSearchParams$ = this.qp.params$.pipe(map((p) => p?.text ?? '')); - inProgress$ = this.fetchPartiesService.isLoading$; - parties$ = this.fetchPartiesService.result$; - columns: Column[] = [ - { field: 'id' }, - { - field: 'email', - cell: (party) => ({ link: () => `/party/${party.id}` }), - }, - { - field: 'blocking', - cell: (party) => ({ - value: startCase(getUnionKey(party.blocking)), - color: ( - { - blocked: 'warn', - unblocked: 'success', - } as const - )[getUnionKey(party.blocking)], - }), - }, - { - field: 'suspension', - cell: (party) => ({ - value: startCase(getUnionKey(party.suspension)), - color: ( - { - suspended: 'warn', - active: 'success', - } as const - )[getUnionKey(party.suspension)], - }), - }, - { - field: 'shops', - cell: (party) => ({ value: party.shops.size }), - }, - createMenuColumn((party) => ({ - items: [ - { - label: 'Details', - click: () => this.router.navigate([`/party/${party.id}`]), - }, - ], - })), - ]; - - @DebounceTime() - searchParamsUpdated(filter: string) { - void this.qp.set({ text: filter }); - this.fetchPartiesService.load(filter); - } - - reload(options: UpdateOptions) { - this.fetchPartiesService.reload(options); - } -} diff --git a/apps/control-center/src/app/sections/search-parties/search-parties.module.ts b/apps/control-center/src/app/sections/search-parties/search-parties.module.ts deleted file mode 100644 index 4ab6df74f..000000000 --- a/apps/control-center/src/app/sections/search-parties/search-parties.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatCardModule } from '@angular/material/card'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { TableModule } from '@vality/matez'; - -import { PageLayoutModule } from '../../shared'; - -import { SearchPartiesRoutingModule } from './search-parties-routing.module'; -import { SearchPartiesComponent } from './search-parties.component'; - -@NgModule({ - imports: [ - SearchPartiesRoutingModule, - MatCardModule, - CommonModule, - MatProgressBarModule, - PageLayoutModule, - TableModule, - ], - declarations: [SearchPartiesComponent], -}) -export class SearchPartiesModule {} diff --git a/apps/control-center/src/app/sections/sections-routing.module.ts b/apps/control-center/src/app/sections/sections-routing.module.ts index aa1ae3307..0bcbd5819 100644 --- a/apps/control-center/src/app/sections/sections-routing.module.ts +++ b/apps/control-center/src/app/sections/sections-routing.module.ts @@ -1,39 +1,11 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { APP_ROUTES } from '../app-routes'; - const ROUTES: Routes = [ { - path: 'domain', - loadChildren: () => import('./domain').then((m) => m.DomainModule), - }, - APP_ROUTES.domain2.root.getRoute(), - { - path: 'domain2', - loadChildren: () => import('./domain2').then((m) => m.DomainModule), - }, - { - path: 'party', + path: 'parties', loadChildren: () => import('./party/party.module').then((m) => m.PartyModule), }, - { - path: 'claims', - loadChildren: () => import('./claims').then((m) => m.ClaimsModule), - }, - { - path: 'party/:partyID', - children: [ - { - path: 'claim/:claimID', - loadChildren: () => import('./claim').then((m) => m.ClaimModule), - }, - { - path: 'invoice/:invoiceID/payment/:paymentID', - loadChildren: () => import('./payment-details').then((m) => m.PaymentDetailsModule), - }, - ], - }, { path: 'withdrawals', loadChildren: () => @@ -47,6 +19,10 @@ const ROUTES: Routes = [ path: 'payments', loadChildren: () => import('./payments/payments.module').then((m) => m.PaymentsModule), }, + { + path: 'payments/:paymentID', + loadChildren: () => import('./payment-details').then((m) => m.PaymentDetailsModule), + }, { path: 'deposits', loadChildren: () => import('./deposits/deposits.module').then((m) => m.DepositsModule), diff --git a/apps/control-center/src/app/sections/shops/shops-routing-config.ts b/apps/control-center/src/app/sections/shops/shops-routing-config.ts index 1a7a288b2..5f800a746 100644 --- a/apps/control-center/src/app/sections/shops/shops-routing-config.ts +++ b/apps/control-center/src/app/sections/shops/shops-routing-config.ts @@ -1,5 +1,5 @@ import { RoutingConfig, Services } from '../../shared/services'; export const SHOPS_ROUTING_CONFIG: RoutingConfig = { - services: [Services.Deanonimus], + services: [Services.DMT], }; diff --git a/apps/control-center/src/app/sections/shops/shops-routing.module.ts b/apps/control-center/src/app/sections/shops/shops-routing.module.ts index 11d980939..f5d196e3e 100644 --- a/apps/control-center/src/app/sections/shops/shops-routing.module.ts +++ b/apps/control-center/src/app/sections/shops/shops-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { SHOPS_ROUTING_CONFIG } from './shops-routing-config'; import { ShopsComponent } from './shops.component'; @@ -10,7 +10,7 @@ const ROUTES: Routes = [ { path: '', component: ShopsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: SHOPS_ROUTING_CONFIG, }, ]; diff --git a/apps/control-center/src/app/sections/shops/shops.component.html b/apps/control-center/src/app/sections/shops/shops.component.html index 4d38e87e3..ddccc884d 100644 --- a/apps/control-center/src/app/sections/shops/shops.component.html +++ b/apps/control-center/src/app/sections/shops/shops.component.html @@ -1,8 +1,9 @@ diff --git a/apps/control-center/src/app/sections/shops/shops.component.ts b/apps/control-center/src/app/sections/shops/shops.component.ts index 71f4bd62f..3743f7854 100644 --- a/apps/control-center/src/app/sections/shops/shops.component.ts +++ b/apps/control-center/src/app/sections/shops/shops.component.ts @@ -1,55 +1,41 @@ -import { Component, inject } from '@angular/core'; -import { Deanonimus, SearchShopHit } from '@vality/deanonimus-proto/deanonimus'; -import { NotifyLogService, progressTo } from '@vality/matez'; -import { BehaviorSubject, Observable, Subject, combineLatest, defer, of } from 'rxjs'; -import { - catchError, - debounceTime, - distinctUntilChanged, - map, - shareReplay, - switchMap, -} from 'rxjs/operators'; +import { Component, OnInit, inject } from '@angular/core'; +import { DomainObjectType } from '@vality/domain-proto/domain'; +import { DebounceTime, UpdateOptions } from '@vality/matez'; +import { map } from 'rxjs/operators'; -import { ShopParty } from '../../shared/components/shops-table'; +import { FetchFullDomainObjectsService } from '../../api/domain-config'; @Component({ selector: 'cc-shops', templateUrl: './shops.component.html', + providers: [FetchFullDomainObjectsService], standalone: false, }) -export class ShopsComponent { - private deanonimusService = inject(Deanonimus); - private log = inject(NotifyLogService); - filterChange$ = new Subject(); - shopsParty$: Observable = combineLatest([ - this.filterChange$.pipe(distinctUntilChanged(), debounceTime(500)), - defer(() => this.updateShops$), - ]).pipe( - switchMap(([search]) => - search - ? this.deanonimusService.searchShopText(search.trim()).pipe( - progressTo(this.progress$), - catchError((err) => { - this.log.error(err); - return of([]); - }), - ) - : of([]), - ), - map((shops) => - shops.map(({ shop, party }) => ({ - shop: shop as ShopParty['shop'], - party, - })), - ), - shareReplay({ refCount: true, bufferSize: 1 }), +export class ShopsComponent implements OnInit { + private fetchDomainObjectsService = inject(FetchFullDomainObjectsService); + + shops$ = this.fetchDomainObjectsService.result$.pipe( + map((objs) => objs.map((obj) => obj.object.shop_config)), ); - progress$ = new BehaviorSubject(0); + progress$ = this.fetchDomainObjectsService.isLoading$; + + ngOnInit() { + this.searchParamsUpdated(''); + } - private updateShops$ = new BehaviorSubject(undefined); + @DebounceTime() + searchParamsUpdated(search: string) { + this.fetchDomainObjectsService.load({ + query: search.trim(), + type: DomainObjectType.shop_config, + }); + } + + reload(options: UpdateOptions) { + this.fetchDomainObjectsService.reload(options); + } - update() { - this.updateShops$.next(); + more() { + this.fetchDomainObjectsService.more(); } } diff --git a/apps/control-center/src/app/sections/sources/create-source/create-source.component.html b/apps/control-center/src/app/sections/sources/create-source/create-source.component.html index a0e6bc757..3100b722b 100644 --- a/apps/control-center/src/app/sections/sources/create-source/create-source.component.html +++ b/apps/control-center/src/app/sections/sources/create-source/create-source.component.html @@ -1,7 +1,7 @@ diff --git a/apps/control-center/src/app/sections/sources/create-source/create-source.component.ts b/apps/control-center/src/app/sections/sources/create-source/create-source.component.ts index 01b9428e0..556b96543 100644 --- a/apps/control-center/src/app/sections/sources/create-source/create-source.component.ts +++ b/apps/control-center/src/app/sections/sources/create-source/create-source.component.ts @@ -1,21 +1,20 @@ import { Component, inject } from '@angular/core'; import { FormControl } from '@angular/forms'; +import { Management } from '@vality/fistful-proto/source'; import { DialogSuperclass, NotifyLogService } from '@vality/matez'; -import { FistfulAdminService } from '../../../api/fistful-admin'; - @Component({ selector: 'cc-create-source', templateUrl: './create-source.component.html', standalone: false, }) export class CreateSourceComponent extends DialogSuperclass { - private fistfulAdminService = inject(FistfulAdminService); + private sourceManagementService = inject(Management); private log = inject(NotifyLogService); control = new FormControl(); create() { - this.fistfulAdminService.CreateSource(this.control.value).subscribe({ + this.sourceManagementService.Create(this.control.value, new Map()).subscribe({ next: () => { this.closeWithSuccess(); this.log.success('Source created successfully!'); diff --git a/apps/control-center/src/app/sections/sources/fetch-sources.service.ts b/apps/control-center/src/app/sections/sources/fetch-sources.service.ts index 7a19950fb..e9fac97cf 100644 --- a/apps/control-center/src/app/sections/sources/fetch-sources.service.ts +++ b/apps/control-center/src/app/sections/sources/fetch-sources.service.ts @@ -1,18 +1,18 @@ import { Injectable, inject } from '@angular/core'; -import { StatSource } from '@vality/fistful-proto/internal/fistful_stat'; +import { FistfulStatistics, StatSource } from '@vality/fistful-proto/fistful_stat'; import { NotifyLogService, compareDifferentTypes, progressTo } from '@vality/matez'; import { BehaviorSubject, Observable, of, switchMap } from 'rxjs'; import { catchError, map, shareReplay } from 'rxjs/operators'; import { createDsl } from '../../api/fistful-stat'; -import { FistfulStatisticsService } from '../../api/fistful-stat/fistful-statistics.service'; @Injectable({ providedIn: 'root', }) export class FetchSourcesService { - private fistfulStatisticsService = inject(FistfulStatisticsService); + private fistfulStatisticsService = inject(FistfulStatistics); private log = inject(NotifyLogService); + sources$: Observable = this.fetch().pipe( map((s) => s.sort( diff --git a/apps/control-center/src/app/sections/sources/sources-routing.module.ts b/apps/control-center/src/app/sections/sources/sources-routing.module.ts index 4485b5e99..2e42113d8 100644 --- a/apps/control-center/src/app/sections/sources/sources-routing.module.ts +++ b/apps/control-center/src/app/sections/sources/sources-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { ROUTING_CONFIG } from './routing-config'; import { SourcesComponent } from './sources.component'; @@ -12,7 +12,7 @@ import { SourcesComponent } from './sources.component'; { path: '', component: SourcesComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/sources/sources.component.ts b/apps/control-center/src/app/sections/sources/sources.component.ts index 30b60791e..370fa49ce 100644 --- a/apps/control-center/src/app/sections/sources/sources.component.ts +++ b/apps/control-center/src/app/sections/sources/sources.component.ts @@ -1,5 +1,5 @@ import { Component, inject } from '@angular/core'; -import { Source } from '@vality/fistful-proto/internal/source'; +import { StatSource } from '@vality/fistful-proto/fistful_stat'; import { Column, DialogService } from '@vality/matez'; import { CreateSourceComponent } from './create-source/create-source.component'; @@ -12,9 +12,10 @@ import { FetchSourcesService } from './fetch-sources.service'; export class SourcesComponent { private fetchSourcesService = inject(FetchSourcesService); private dialogService = inject(DialogService); + sources$ = this.fetchSourcesService.sources$; progress$ = this.fetchSourcesService.progress$; - columns: Column[] = [ + columns: Column[] = [ { field: 'id' }, { field: 'name' }, { field: 'identity' }, diff --git a/apps/control-center/src/app/sections/terminals/routing-config.ts b/apps/control-center/src/app/sections/terminals/routing-config.ts index 59ce065d6..af803631e 100644 --- a/apps/control-center/src/app/sections/terminals/routing-config.ts +++ b/apps/control-center/src/app/sections/terminals/routing-config.ts @@ -1,5 +1,5 @@ import { RoutingConfig, Services } from '../../shared/services'; export const ROUTING_CONFIG: RoutingConfig = { - services: [Services.Domain], + services: [Services.DMT], }; diff --git a/apps/control-center/src/app/sections/terminals/terminals-routing.module.ts b/apps/control-center/src/app/sections/terminals/terminals-routing.module.ts index 1152ac70a..8409f9ef5 100644 --- a/apps/control-center/src/app/sections/terminals/terminals-routing.module.ts +++ b/apps/control-center/src/app/sections/terminals/terminals-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { ROUTING_CONFIG } from './routing-config'; import { TerminalsComponent } from './terminals.component'; @@ -12,7 +12,7 @@ import { TerminalsComponent } from './terminals.component'; { path: '', component: TerminalsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/terminals/terminals.component.html b/apps/control-center/src/app/sections/terminals/terminals.component.html index 4d9b8dfe9..dc88dee06 100644 --- a/apps/control-center/src/app/sections/terminals/terminals.component.html +++ b/apps/control-center/src/app/sections/terminals/terminals.component.html @@ -5,9 +5,11 @@ diff --git a/apps/control-center/src/app/sections/terminals/terminals.component.ts b/apps/control-center/src/app/sections/terminals/terminals.component.ts index 5aaeb8d7b..49f65c0b4 100644 --- a/apps/control-center/src/app/sections/terminals/terminals.component.ts +++ b/apps/control-center/src/app/sections/terminals/terminals.component.ts @@ -1,9 +1,9 @@ -import { Component, inject } from '@angular/core'; -import { TerminalObject } from '@vality/domain-proto/domain'; -import { Column, DialogService } from '@vality/matez'; +import { Component, OnInit, inject } from '@angular/core'; +import { DomainObjectType, TerminalObject } from '@vality/domain-proto/domain'; +import { Column, DialogService, UpdateOptions } from '@vality/matez'; import { map } from 'rxjs/operators'; -import { DomainStoreService } from '../../api/domain-config'; +import { FetchFullDomainObjectsService, RoutingRulesStoreService } from '../../api/domain-config'; import { AccountBalancesStoreService } from '../../api/terminal-balance'; import { createCurrencyColumn, @@ -13,11 +13,11 @@ import { import { SidenavInfoService } from '../../shared/components/sidenav-info'; import { TerminalBalancesCardComponent } from '../../shared/components/terminal-balances-card/terminal-balances-card.component'; import { TerminalDelegatesCardComponent } from '../../shared/components/terminal-delegates-card/terminal-delegates-card.component'; +import { getDomainObjectDetails } from '../../shared/components/thrift-api-crud'; import { CreateDomainObjectDialogComponent, DomainObjectCardComponent, - getDomainObjectDetails, -} from '../../shared/components/thrift-api-crud'; +} from '../../shared/components/thrift-api-crud/domain2'; import { getTerminalShopWalletDelegates } from './utils/get-terminal-shop-wallet-delegates'; @@ -25,12 +25,15 @@ import { getTerminalShopWalletDelegates } from './utils/get-terminal-shop-wallet selector: 'cc-terminals', templateUrl: './terminals.component.html', standalone: false, + providers: [FetchFullDomainObjectsService], }) -export class TerminalsComponent { - private domainStoreService = inject(DomainStoreService); +export class TerminalsComponent implements OnInit { + private fetchFullDomainObjectsService = inject(FetchFullDomainObjectsService); + private routingRulesStoreService = inject(RoutingRulesStoreService); private sidenavInfoService = inject(SidenavInfoService); private dialogService = inject(DialogService); private accountBalancesStoreService = inject(AccountBalancesStoreService); + columns: Column[] = [ { field: 'ref.id', sticky: 'start' }, { @@ -114,23 +117,34 @@ export class TerminalsComponent { }, ), ]; - data$ = this.domainStoreService.getObjects('terminal'); - progress$ = this.domainStoreService.isLoading$; + data$ = this.fetchFullDomainObjectsService.result$.pipe( + map((d) => d.map((o) => o.object.terminal)), + ); + hasMore$ = this.fetchFullDomainObjectsService.hasMore$; + progress$ = this.fetchFullDomainObjectsService.isLoading$; + + ngOnInit() { + this.fetchFullDomainObjectsService.load({ + type: DomainObjectType.terminal, + }); + } - update() { - this.domainStoreService.forceReload(); + update(options: UpdateOptions) { + this.fetchFullDomainObjectsService.reload(options); + } + + more() { + this.fetchFullDomainObjectsService.more(); } create() { - this.dialogService.open(CreateDomainObjectDialogComponent, { - objectType: 'TerminalObject', - }); + this.dialogService.open(CreateDomainObjectDialogComponent, { objectType: 'terminal' }); } private getTerminalShopWalletDelegates(terminalObj: TerminalObject) { - return this.domainStoreService - .getObjects('routing_rules') - .pipe(map((rules) => getTerminalShopWalletDelegates(rules, terminalObj))); + return this.routingRulesStoreService.routingRules$.pipe( + map((rules) => getTerminalShopWalletDelegates(rules, terminalObj)), + ); } private toggleBalancesCard(d: TerminalObject) { diff --git a/apps/control-center/src/app/sections/terms/components/shops-terms/shops-terms.component.html b/apps/control-center/src/app/sections/terms/components/shops-terms/shops-terms.component.html index a3d5a18b3..0c121ea08 100644 --- a/apps/control-center/src/app/sections/terms/components/shops-terms/shops-terms.component.html +++ b/apps/control-center/src/app/sections/terms/components/shops-terms/shops-terms.component.html @@ -5,11 +5,7 @@ - + diff --git a/apps/control-center/src/app/sections/terms/components/shops-terms/shops-terms.component.ts b/apps/control-center/src/app/sections/terms/components/shops-terms/shops-terms.component.ts index 7f1edde4a..446df2fa4 100644 --- a/apps/control-center/src/app/sections/terms/components/shops-terms/shops-terms.component.ts +++ b/apps/control-center/src/app/sections/terms/components/shops-terms/shops-terms.component.ts @@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'; import { Component, DestroyRef, OnInit, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { NonNullableFormBuilder, ReactiveFormsModule } from '@angular/forms'; -import { TermSetHierarchyRef } from '@vality/domain-proto/internal/domain'; +import { TermSetHierarchyRef } from '@vality/domain-proto/domain'; import { CommonSearchQueryParams, ShopSearchQuery, @@ -30,7 +30,6 @@ import { Overwrite } from 'utility-types'; import { PageLayoutModule, ShopFieldModule, - createContractColumn, createDomainObjectColumn, createPartyColumn, createShopColumn, @@ -76,6 +75,7 @@ export class ShopsTermsComponent implements OnInit { private debounceTimeMs = inject(DEBOUNCE_TIME_MS); private dr = inject(DestroyRef); private sidenavInfoService = inject(SidenavInfoService); + filtersForm = this.fb.group( createControls({ currencies: null, @@ -110,10 +110,6 @@ export class ShopsTermsComponent implements OnInit { { sticky: 'start' }, ), createPartyColumn((d) => ({ id: d.owner_id })), - createContractColumn((d) => ({ - id: d.contract_id, - partyId: d.owner_id, - })), { field: 'currency' }, createDomainObjectColumn((d) => ({ ref: { term_set_hierarchy: d.current_term_set.ref } }), { header: 'Term Set', diff --git a/apps/control-center/src/app/sections/terms/components/shops-terms/utils/shop-fees-columns.ts b/apps/control-center/src/app/sections/terms/components/shops-terms/utils/shop-fees-columns.ts index 8a65fbb4f..8ebc51717 100644 --- a/apps/control-center/src/app/sections/terms/components/shops-terms/utils/shop-fees-columns.ts +++ b/apps/control-center/src/app/sections/terms/components/shops-terms/utils/shop-fees-columns.ts @@ -4,7 +4,7 @@ import { Predicate, ShopID, TermSetHierarchyObject, -} from '@vality/domain-proto/internal/domain'; +} from '@vality/domain-proto/domain'; import { Column } from '@vality/matez'; import { formatCashVolumes } from '../../../../../shared'; diff --git a/apps/control-center/src/app/sections/terms/components/terminals-terms/utils/terminal-fees-columns.ts b/apps/control-center/src/app/sections/terms/components/terminals-terms/utils/terminal-fees-columns.ts index 3f245abd3..ec5a045a9 100644 --- a/apps/control-center/src/app/sections/terms/components/terminals-terms/utils/terminal-fees-columns.ts +++ b/apps/control-center/src/app/sections/terms/components/terminals-terms/utils/terminal-fees-columns.ts @@ -1,4 +1,4 @@ -import { CashFlowPosting, ProvisionTermSet } from '@vality/domain-proto/internal/domain'; +import { CashFlowPosting, ProvisionTermSet } from '@vality/domain-proto/domain'; import { Column, TreeDataItem } from '@vality/matez'; import { createFeesColumns } from '../../../utils/create-fees-columns'; diff --git a/apps/control-center/src/app/sections/terms/components/wallets-terms/wallets-terms.component.ts b/apps/control-center/src/app/sections/terms/components/wallets-terms/wallets-terms.component.ts index 59e4ee3db..c28be1769 100644 --- a/apps/control-center/src/app/sections/terms/components/wallets-terms/wallets-terms.component.ts +++ b/apps/control-center/src/app/sections/terms/components/wallets-terms/wallets-terms.component.ts @@ -2,10 +2,7 @@ import { CommonModule } from '@angular/common'; import { Component, DestroyRef, OnInit, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { NonNullableFormBuilder, ReactiveFormsModule } from '@angular/forms'; -import { - type IdentityProviderRef, - TermSetHierarchyRef, -} from '@vality/domain-proto/internal/domain'; +import { type IdentityProviderRef, TermSetHierarchyRef } from '@vality/domain-proto/domain'; import { type CommonSearchQueryParams, type WalletSearchQuery, @@ -78,6 +75,7 @@ export class WalletsTermsComponent implements OnInit { private debounceTimeMs = inject(DEBOUNCE_TIME_MS); private dr = inject(DestroyRef); private sidenavInfoService = inject(SidenavInfoService); + filtersForm = this.fb.group( createControls({ currencies: null, diff --git a/apps/control-center/src/app/sections/terms/terms-routing.module.ts b/apps/control-center/src/app/sections/terms/terms-routing.module.ts index f9638a2ad..6a4e41c7b 100644 --- a/apps/control-center/src/app/sections/terms/terms-routing.module.ts +++ b/apps/control-center/src/app/sections/terms/terms-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { ShopsTermsComponent } from './components/shops-terms/shops-terms.component'; import { TerminalsTermsComponent } from './components/terminals-terms/terminals-terms.component'; @@ -15,7 +15,7 @@ import { TermsComponent } from './terms.component'; { path: '', component: TermsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, children: [ { diff --git a/apps/control-center/src/app/sections/terms/terms.component.html b/apps/control-center/src/app/sections/terms/terms.component.html index 3af5c72d0..0680b43f9 100644 --- a/apps/control-center/src/app/sections/terms/terms.component.html +++ b/apps/control-center/src/app/sections/terms/terms.component.html @@ -1,15 +1 @@ - - - - - + diff --git a/apps/control-center/src/app/sections/terms/terms.component.ts b/apps/control-center/src/app/sections/terms/terms.component.ts index cc6dfe664..624bdf416 100644 --- a/apps/control-center/src/app/sections/terms/terms.component.ts +++ b/apps/control-center/src/app/sections/terms/terms.component.ts @@ -1,30 +1,9 @@ -import { CommonModule } from '@angular/common'; -import { Component, inject } from '@angular/core'; -import { MatSidenavModule } from '@angular/material/sidenav'; +import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; -import { Link, NavComponent } from '@vality/matez'; - -import { SidenavInfoService } from '../../shared/components/sidenav-info/sidenav-info.service'; @Component({ selector: 'cc-terms', - imports: [CommonModule, RouterOutlet, MatSidenavModule, NavComponent], + imports: [RouterOutlet], templateUrl: './terms.component.html', }) -export class TermsComponent { - public sidenavInfoService = inject(SidenavInfoService); - links: Link[] = [ - { - label: 'Shops', - url: 'shops', - }, - { - label: 'Wallets', - url: 'wallets', - }, - { - label: 'Terminals', - url: 'terminals', - }, - ]; -} +export class TermsComponent {} diff --git a/apps/control-center/src/app/sections/terms/utils/create-fees-columns.ts b/apps/control-center/src/app/sections/terms/utils/create-fees-columns.ts index 4e3c7ba73..f6458bf40 100644 --- a/apps/control-center/src/app/sections/terms/utils/create-fees-columns.ts +++ b/apps/control-center/src/app/sections/terms/utils/create-fees-columns.ts @@ -1,4 +1,4 @@ -import { CashFlowPosting } from '@vality/domain-proto/internal/domain'; +import { CashFlowPosting } from '@vality/domain-proto/domain'; import { Column } from '@vality/matez'; import { formatCashVolumes, getCashVolumeParts } from '../../../shared'; diff --git a/apps/control-center/src/app/sections/terms/utils/is-that-currency.ts b/apps/control-center/src/app/sections/terms/utils/is-that-currency.ts index a5f545b9c..d0e5e1e15 100644 --- a/apps/control-center/src/app/sections/terms/utils/is-that-currency.ts +++ b/apps/control-center/src/app/sections/terms/utils/is-that-currency.ts @@ -1,4 +1,4 @@ -import { Predicate } from '@vality/domain-proto/internal/domain'; +import { Predicate } from '@vality/domain-proto/domain'; export function isThatCurrency(predicate: Predicate, currency: string) { return predicate?.condition?.currency_is?.symbolic_code === currency; diff --git a/apps/control-center/src/app/sections/wallets/fetch-wallets-text.service.ts b/apps/control-center/src/app/sections/wallets/fetch-wallets-text.service.ts deleted file mode 100644 index b6c1fe919..000000000 --- a/apps/control-center/src/app/sections/wallets/fetch-wallets-text.service.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { Deanonimus, SearchWalletHit } from '@vality/deanonimus-proto/deanonimus'; -import { FetchSuperclass } from '@vality/matez'; -import { map } from 'rxjs/operators'; - -@Injectable() -export class FetchWalletsTextService extends FetchSuperclass { - private deanonimusService = inject(Deanonimus); - - protected fetch(text: string) { - return this.deanonimusService.searchWalletText(text).pipe( - map((result) => ({ - result, - })), - ); - } -} diff --git a/apps/control-center/src/app/sections/wallets/fetch-wallets.service.ts b/apps/control-center/src/app/sections/wallets/fetch-wallets.service.ts deleted file mode 100644 index ea4ca3f85..000000000 --- a/apps/control-center/src/app/sections/wallets/fetch-wallets.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { StatWallet } from '@vality/fistful-proto/fistful_stat'; -import { FetchOptions, FetchSuperclass } from '@vality/matez'; -import { map } from 'rxjs/operators'; - -import { FistfulStatisticsService, createDsl } from '../../api/fistful-stat'; -import { WalletParams } from '../../api/fistful-stat/query-dsl/types/wallet'; - -@Injectable() -export class FetchWalletsService extends FetchSuperclass { - private fistfulStatisticsService = inject(FistfulStatisticsService); - - protected fetch(params: WalletParams, { size, continuationToken }: FetchOptions) { - return this.fistfulStatisticsService - .GetWallets({ - dsl: createDsl({ wallets: { size, ...params } }), - continuation_token: continuationToken, - }) - .pipe( - map((res) => ({ - result: res.data.wallets, - continuationToken: res.continuation_token, - })), - ); - } -} diff --git a/apps/control-center/src/app/sections/wallets/wallets-routing.module.ts b/apps/control-center/src/app/sections/wallets/wallets-routing.module.ts index 16d1b15b6..2133bad5b 100644 --- a/apps/control-center/src/app/sections/wallets/wallets-routing.module.ts +++ b/apps/control-center/src/app/sections/wallets/wallets-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { ROUTING_CONFIG } from './routing-config'; import { WalletsComponent } from './wallets.component'; @@ -12,7 +12,7 @@ import { WalletsComponent } from './wallets.component'; { path: '', component: WalletsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/wallets/wallets.component.html b/apps/control-center/src/app/sections/wallets/wallets.component.html index 41f8d4053..929917169 100644 --- a/apps/control-center/src/app/sections/wallets/wallets.component.html +++ b/apps/control-center/src/app/sections/wallets/wallets.component.html @@ -1,54 +1,13 @@ - - @if (filters) { - - } - @if (!(party$ | async)) { - - } - - @if (isFilterTable$ | async) { - - - @if (!(party$ | async)) { - - } - - - Identity ID - - - - - - } - - @if (isFilterTable$ | async) { - - } @else { - - } + diff --git a/apps/control-center/src/app/sections/wallets/wallets.component.ts b/apps/control-center/src/app/sections/wallets/wallets.component.ts index 14e4bb0e0..cd57d3483 100644 --- a/apps/control-center/src/app/sections/wallets/wallets.component.ts +++ b/apps/control-center/src/app/sections/wallets/wallets.component.ts @@ -1,222 +1,90 @@ -import { Component, DestroyRef, OnInit, ViewChild, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormControl, NonNullableFormBuilder } from '@angular/forms'; -import { SearchWalletHit } from '@vality/deanonimus-proto/deanonimus'; -import { IdentityState } from '@vality/fistful-proto/identity'; -import { AccountBalance } from '@vality/fistful-proto/internal/account'; -import { StatWallet } from '@vality/fistful-proto/internal/fistful_stat'; -import { - Column, - DebounceTime, - FiltersComponent, - QueryParamsService, - UpdateOptions, - clean, - countChanged, - debounceTimeWithFirst, - getValueChanges, -} from '@vality/matez'; -import isNil from 'lodash-es/isNil'; -import { combineLatest, of } from 'rxjs'; -import { catchError, map, shareReplay, take } from 'rxjs/operators'; +import { Component, OnInit, inject } from '@angular/core'; +import { DomainObjectType, WalletConfig } from '@vality/domain-proto/domain'; +import { Column, DebounceTime, UpdateOptions } from '@vality/matez'; +import { of } from 'rxjs'; +import { map } from 'rxjs/operators'; import { MemoizeExpiring } from 'typescript-memoize'; -import { WalletParams } from '../../api/fistful-stat/query-dsl/types/wallet'; -import { IdentityManagementService } from '../../api/identity'; -import { ManagementService } from '../../api/wallet/management.service'; -import { createCurrencyColumn, createPartyColumn } from '../../shared'; -import { DEBOUNCE_TIME_MS } from '../../tokens'; +import { FetchFullDomainObjectsService } from '../../api/domain-config'; +import { createPartyColumn } from '../../shared'; import { PartyStoreService } from '../party'; -import { FetchWalletsTextService } from './fetch-wallets-text.service'; -import { FetchWalletsService } from './fetch-wallets.service'; - @Component({ selector: 'cc-wallets', templateUrl: './wallets.component.html', - providers: [FetchWalletsService, FetchWalletsTextService, PartyStoreService], + providers: [PartyStoreService, FetchFullDomainObjectsService], standalone: false, }) export class WalletsComponent implements OnInit { - private fetchWalletsService = inject(FetchWalletsService); - private fetchWalletsTextService = inject(FetchWalletsTextService); - private qp = inject>(QueryParamsService); - private fb = inject(NonNullableFormBuilder); - private walletManagementService = inject(ManagementService); - private debounceTimeMs = inject(DEBOUNCE_TIME_MS); - private destroyRef = inject(DestroyRef); - private identityManagementService = inject(IdentityManagementService); + private fetchFullDomainObjectsService = inject(FetchFullDomainObjectsService); private partyStoreService = inject(PartyStoreService); - isFilterControl = new FormControl(0); - - filterWallets$ = this.fetchWalletsService.result$; - filtersLoading$ = this.fetchWalletsService.isLoading$; - filterHasMore$ = this.fetchWalletsService.hasMore$; - fullTextSearchWallets$ = this.fetchWalletsTextService.result$; - fullTextSearchLoading$ = this.fetchWalletsTextService.isLoading$; + wallets$ = this.fetchFullDomainObjectsService.result$.pipe( + map((objs) => objs.map((obj) => obj.object.wallet_config.data)), + ); + isLoading$ = this.fetchFullDomainObjectsService.isLoading$; - filterColumns: Column[] = [ + columns: Column[] = [ { field: 'id' }, { field: 'name' }, - { field: 'currency_symbolic_code' }, - { field: 'identity_id' }, - { field: 'created_at', cell: { type: 'datetime' } }, - createCurrencyColumn( - (d) => - this.getBalance(d.id).pipe( - map((b) => ({ amount: b.current, code: b.currency.symbolic_code })), - ), - { header: 'Balance', isLazyCell: true }, - ), - createCurrencyColumn( - (d) => - this.getBalance(d.id).pipe( - map((b) => ({ - amount: b.current - b.expected_min, - code: b.currency.symbolic_code, - })), - ), - { header: 'Hold', isLazyCell: true }, - ), - createCurrencyColumn( - (d) => - this.getBalance(d.id).pipe( - map((b) => ({ amount: b.expected_min, code: b.currency.symbolic_code })), - ), - { header: 'Expected Min', isLazyCell: true }, - ), - { - field: 'contract_id', - lazyCell: (d) => - this.getIdentity(d.identity_id).pipe( - map((identity) => ({ value: identity.contract_id })), - ), - }, - createPartyColumn( - (d) => - this.getIdentity(d.identity_id).pipe( - map((identity) => ({ id: identity.party_id })), - ), - { hidden: this.partyStoreService.party$.pipe(map((p) => !p)) }, - ), + createPartyColumn((d) => ({ id: d.party_id })), + // createCurrencyColumn( + // (d) => + // this.getBalance(d.id).pipe( + // map((b) => ({ amount: b.current, code: b.currency.symbolic_code })), + // ), + // { header: 'Balance', isLazyCell: true }, + // ), + // createCurrencyColumn( + // (d) => + // this.getBalance(d.id).pipe( + // map((b) => ({ + // amount: b.current - b.expected_min, + // code: b.currency.symbolic_code, + // })), + // ), + // { header: 'Hold', isLazyCell: true }, + // ), + // createCurrencyColumn( + // (d) => + // this.getBalance(d.id).pipe( + // map((b) => ({ amount: b.expected_min, code: b.currency.symbolic_code })), + // ), + // { header: 'Expected Min', isLazyCell: true }, + // ), ]; - fullTextSearchColumns: Column[] = [ - { field: 'wallet.id' }, - { field: 'wallet.name' }, - createPartyColumn((d) => ({ - id: d.party.id, - partyName: d.party.email, - })), - createCurrencyColumn( - (d) => - this.getBalance(d.wallet.id).pipe( - map((b) => ({ amount: b.current, code: b.currency.symbolic_code })), - ), - { header: 'Balance', isLazyCell: true }, - ), - createCurrencyColumn( - (d) => - this.getBalance(d.wallet.id).pipe( - map((b) => ({ - amount: b.current - b.expected_min, - code: b.currency.symbolic_code, - })), - ), - { header: 'Hold', isLazyCell: true }, - ), - createCurrencyColumn( - (d) => - this.getBalance(d.wallet.id).pipe( - map((b) => ({ amount: b.expected_min, code: b.currency.symbolic_code })), - ), - { header: 'Expected Min', isLazyCell: true }, - ), - ]; - filtersForm = this.fb.group({ - party_id: null as string, - identity_id: null as string, - currency_code: null as string, - wallet_id: [null as string[]], - }); - active$ = getValueChanges(this.filtersForm).pipe( - map((v) => countChanged(this.initFilters, v)), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - @ViewChild(FiltersComponent) filters!: FiltersComponent; - typeQp = this.qp.createNamespace<{ isFilter: boolean }>('type'); party$ = this.partyStoreService.party$; - isFilterTable$ = combineLatest([getValueChanges(this.isFilterControl), this.party$]).pipe( - map(([isFilterControl, party]) => isFilterControl || !!party), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - - private initFilters = this.filtersForm.value; ngOnInit() { - this.filtersForm.patchValue(this.qp.params); - const isFilter = this.typeQp.params.isFilter; - if (!isNil(isFilter)) { - this.isFilterControl.setValue(Number(isFilter)); - } - getValueChanges(this.isFilterControl) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((value) => { - void this.typeQp.set({ isFilter: !!value }); - }); - getValueChanges(this.filtersForm) - .pipe(debounceTimeWithFirst(this.debounceTimeMs), takeUntilDestroyed(this.destroyRef)) - .subscribe((value) => { - void this.qp.set(clean(value)); - this.filterSearch(); - }); - } - - filterSearch(opts?: UpdateOptions) { - const props = clean(this.filtersForm.value); - this.partyStoreService.party$.pipe(take(1)).subscribe((p) => { - this.fetchWalletsService.load( - clean({ - party_id: p ? p.id : undefined, - ...props, - }), - opts, - ); + this.fetchFullDomainObjectsService.load({ + type: DomainObjectType.wallet_config, + query: '', }); } - filterMore() { - this.fetchWalletsService.more(); - } - @DebounceTime() - fullTextSearch(text: string) { - this.fetchWalletsTextService.load(text); + search(query: string) { + this.fetchFullDomainObjectsService.load({ query, type: DomainObjectType.wallet_config }); } - fullTextSearchReload() { - this.fetchWalletsTextService.reload(); + reload(options: UpdateOptions) { + this.fetchFullDomainObjectsService.reload(options); } - @MemoizeExpiring(5 * 60_000) - getBalance(walletId: string) { - return this.walletManagementService.GetAccountBalance(walletId).pipe( - catchError((err) => { - console.error(err); - return of>({}); - }), - shareReplay({ refCount: true, bufferSize: 1 }), - ); + more() { + this.fetchFullDomainObjectsService.more(); } @MemoizeExpiring(5 * 60_000) - getIdentity(id: string) { - return this.identityManagementService.Get(id, {}).pipe( - catchError((err) => { - console.error(err); - return of>({}); - }), - shareReplay({ refCount: true, bufferSize: 1 }), - ); + getBalance(_walletId: string) { + // TODO + return of({}); + // return this.walletManagementService.GetAccountBalance(walletId).pipe( + // catchError((err) => { + // console.error(err); + // return of>({}); + // }), + // shareReplay({ refCount: true, bufferSize: 1 }), + // ); } } diff --git a/apps/control-center/src/app/sections/withdrawals/components/create-adjustment-dialog/create-adjustment-dialog.component.ts b/apps/control-center/src/app/sections/withdrawals/components/create-adjustment-dialog/create-adjustment-dialog.component.ts index b46d31e9c..2105c1c38 100644 --- a/apps/control-center/src/app/sections/withdrawals/components/create-adjustment-dialog/create-adjustment-dialog.component.ts +++ b/apps/control-center/src/app/sections/withdrawals/components/create-adjustment-dialog/create-adjustment-dialog.component.ts @@ -2,14 +2,13 @@ import { Component, DestroyRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormControl, Validators } from '@angular/forms'; import { StatWithdrawal } from '@vality/fistful-proto/fistful_stat'; +import { Management } from '@vality/fistful-proto/withdrawal'; import { AdjustmentParams } from '@vality/fistful-proto/withdrawal_adjustment'; import { DialogSuperclass, NotifyLogService, forkJoinToResult } from '@vality/matez'; import { ThriftFormExtension, isTypeWithAliases } from '@vality/ng-thrift'; import { BehaviorSubject, of } from 'rxjs'; import short from 'short-uuid'; -import { ManagementService } from '../../../../api/withdrawal/management.service'; - @Component({ templateUrl: './create-adjustment-dialog.component.html', standalone: false, @@ -18,7 +17,7 @@ export class CreateAdjustmentDialogComponent extends DialogSuperclass< CreateAdjustmentDialogComponent, { withdrawals: StatWithdrawal[] } > { - private managementService = inject(ManagementService); + private managementService = inject(Management); private log = inject(NotifyLogService); private destroyRef = inject(DestroyRef); control = new FormControl>( diff --git a/apps/control-center/src/app/sections/withdrawals/services/fetch-withdrawals.service.ts b/apps/control-center/src/app/sections/withdrawals/services/fetch-withdrawals.service.ts index 8db7a78e5..ba4db5594 100644 --- a/apps/control-center/src/app/sections/withdrawals/services/fetch-withdrawals.service.ts +++ b/apps/control-center/src/app/sections/withdrawals/services/fetch-withdrawals.service.ts @@ -1,15 +1,18 @@ import { Injectable, inject } from '@angular/core'; -import { StatResponse, StatWithdrawal } from '@vality/fistful-proto/fistful_stat'; +import { + FistfulStatistics, + StatResponse, + StatWithdrawal, +} from '@vality/fistful-proto/fistful_stat'; import { FetchOptions, FetchResult, FetchSuperclass, NotifyLogService } from '@vality/matez'; import { Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { WithdrawalParams, createDsl } from '../../../api/fistful-stat'; -import { FistfulStatisticsService } from '../../../api/fistful-stat/fistful-statistics.service'; @Injectable() export class FetchWithdrawalsService extends FetchSuperclass { - private fistfulStatisticsService = inject(FistfulStatisticsService); + private fistfulStatisticsService = inject(FistfulStatistics); private log = inject(NotifyLogService); protected fetch( diff --git a/apps/control-center/src/app/sections/withdrawals/withdrawals-routing.module.ts b/apps/control-center/src/app/sections/withdrawals/withdrawals-routing.module.ts index 399a58e55..ff5107fa5 100644 --- a/apps/control-center/src/app/sections/withdrawals/withdrawals-routing.module.ts +++ b/apps/control-center/src/app/sections/withdrawals/withdrawals-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { AppAuthGuardService } from '../../shared/services'; +import { canActivateAuthRole } from '../../shared/services'; import { ROUTING_CONFIG } from './routing-config'; import { WithdrawalsComponent } from './withdrawals.component'; @@ -12,7 +12,7 @@ import { WithdrawalsComponent } from './withdrawals.component'; { path: '', component: WithdrawalsComponent, - canActivate: [AppAuthGuardService], + canActivate: [canActivateAuthRole], data: ROUTING_CONFIG, }, ]), diff --git a/apps/control-center/src/app/sections/withdrawals/withdrawals.component.ts b/apps/control-center/src/app/sections/withdrawals/withdrawals.component.ts index aeb13fcc2..f091e3326 100644 --- a/apps/control-center/src/app/sections/withdrawals/withdrawals.component.ts +++ b/apps/control-center/src/app/sections/withdrawals/withdrawals.component.ts @@ -110,7 +110,7 @@ export class WithdrawalsComponent implements OnInit { createDomainObjectColumn((d) => ({ ref: { provider: { id: d.provider_id } } }), { header: 'Provider', }), - createFailureColumn((d) => ({ failure: d.status?.failed?.base_failure })), + createFailureColumn((d) => ({ failure: d.status?.failed?.failure })), ]; selected: StatWithdrawal[] = []; statuses: WithdrawalParams['status'][] = ['Pending', 'Succeeded', 'Failed']; diff --git a/apps/control-center/src/app/shared/components/candidate-card/candidate-card.component.ts b/apps/control-center/src/app/shared/components/candidate-card/candidate-card.component.ts index 13d7a55f9..5c150917c 100644 --- a/apps/control-center/src/app/shared/components/candidate-card/candidate-card.component.ts +++ b/apps/control-center/src/app/shared/components/candidate-card/candidate-card.component.ts @@ -5,7 +5,7 @@ import { ComponentChanges } from '@vality/matez'; import { ReplaySubject, defer, switchMap } from 'rxjs'; import { map, shareReplay } from 'rxjs/operators'; -import { DomainStoreService } from '../../../api/domain-config'; +import { RoutingRulesStoreService } from '../../../api/domain-config'; import { CardComponent } from '../sidenav-info/components/card/card.component'; import { DomainThriftViewerComponent } from '../thrift-api-crud'; @@ -15,16 +15,14 @@ import { DomainThriftViewerComponent } from '../thrift-api-crud'; templateUrl: './candidate-card.component.html', }) export class CandidateCardComponent implements OnChanges { - private domainStoreService = inject(DomainStoreService); + private routingRulesStoreService = inject(RoutingRulesStoreService); @Input() idx: number; @Input() ref: RoutingRulesetRef; - progress$ = this.domainStoreService.isLoading$; + progress$ = this.routingRulesStoreService.isLoading$; candidate$ = defer(() => this.ref$).pipe( - switchMap((ref) => this.domainStoreService.getObject({ routing_rules: ref })), - switchMap((s) => - this.idx$.pipe(map((idx) => s.routing_rules.data.decisions.candidates[idx])), - ), + switchMap((ref) => this.routingRulesStoreService.get(ref)), + switchMap((s) => this.idx$.pipe(map((idx) => s.data.decisions.candidates[idx]))), shareReplay({ refCount: true, bufferSize: 1 }), ); diff --git a/apps/control-center/src/app/shared/components/change-chargebacks-status-dialog/change-chargebacks-status-dialog.component.ts b/apps/control-center/src/app/shared/components/change-chargebacks-status-dialog/change-chargebacks-status-dialog.component.ts index 9b4aec339..5082c18de 100644 --- a/apps/control-center/src/app/shared/components/change-chargebacks-status-dialog/change-chargebacks-status-dialog.component.ts +++ b/apps/control-center/src/app/shared/components/change-chargebacks-status-dialog/change-chargebacks-status-dialog.component.ts @@ -5,6 +5,8 @@ import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; +import { metadata$ } from '@vality/domain-proto'; +import { Invoicing } from '@vality/domain-proto/payment_processing'; import { StatChargeback } from '@vality/magista-proto/magista'; import { DialogModule, @@ -13,12 +15,10 @@ import { EnumKeysPipe, NotifyLogService, forkJoinToResult, - getImportValue, } from '@vality/matez'; -import { ThriftAstMetadata, ThriftFormModule } from '@vality/ng-thrift'; +import { ThriftFormModule } from '@vality/ng-thrift'; import { BehaviorSubject } from 'rxjs'; -import { InvoicingService } from '../../../api/payment-processing/invoicing.service'; import { DomainMetadataFormExtensionsService } from '../../services'; enum Action { @@ -56,11 +56,11 @@ export class ChangeChargebacksStatusDialogComponent > implements OnInit { - private invoicingService = inject(InvoicingService); + private invoicingService = inject(Invoicing); private log = inject(NotifyLogService); private domainMetadataFormExtensionsService = inject(DomainMetadataFormExtensionsService); private destroyRef = inject(DestroyRef); - metadata$ = getImportValue(import('@vality/domain-proto/metadata.json')); + metadata$ = metadata$; extensions$ = this.domainMetadataFormExtensionsService.extensions$; control = new FormControl(); actionControl = new FormControl(null, Validators.required); diff --git a/apps/control-center/src/app/shared/components/chargebacks-table/chargebacks-table.component.ts b/apps/control-center/src/app/shared/components/chargebacks-table/chargebacks-table.component.ts index 1b5d9b338..d2860beeb 100644 --- a/apps/control-center/src/app/shared/components/chargebacks-table/chargebacks-table.component.ts +++ b/apps/control-center/src/app/shared/components/chargebacks-table/chargebacks-table.component.ts @@ -100,7 +100,7 @@ export class ChargebacksTableComponent { }, cell: (d) => ({ description: d.payment_id, - link: () => `/party/${d.party_id}/invoice/${d.invoice_id}/payment/${d.payment_id}`, + link: () => `/payments/${d.invoice_id}.${d.payment_id}`, }), hidden: toObservable(this.onePayment), }, diff --git a/apps/control-center/src/app/shared/components/contract-card/contract-card.component.html b/apps/control-center/src/app/shared/components/contract-card/contract-card.component.html deleted file mode 100644 index cb0fc9456..000000000 --- a/apps/control-center/src/app/shared/components/contract-card/contract-card.component.html +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/apps/control-center/src/app/shared/components/contract-card/contract-card.component.ts b/apps/control-center/src/app/shared/components/contract-card/contract-card.component.ts deleted file mode 100644 index a2762a440..000000000 --- a/apps/control-center/src/app/shared/components/contract-card/contract-card.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component, Input, OnChanges, inject } from '@angular/core'; -import { ComponentChanges, progressTo } from '@vality/matez'; -import { BehaviorSubject, ReplaySubject, combineLatest, defer } from 'rxjs'; -import { shareReplay, switchMap } from 'rxjs/operators'; - -import { PartyManagementService } from '../../../api/payment-processing'; -import { CardComponent } from '../sidenav-info/components/card/card.component'; -import { DomainThriftViewerComponent } from '../thrift-api-crud'; - -@Component({ - selector: 'cc-contract-card', - imports: [CommonModule, CardComponent, DomainThriftViewerComponent], - templateUrl: './contract-card.component.html', -}) -export class ContractCardComponent implements OnChanges { - private partyManagementService = inject(PartyManagementService); - @Input() partyId: string; - @Input() id: string; - - progress$ = new BehaviorSubject(0); - contract$ = combineLatest([defer(() => this.partyId$), defer(() => this.id$)]).pipe( - switchMap(([partyID, id]) => - this.partyManagementService.GetContract(partyID, id).pipe(progressTo(this.progress$)), - ), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - - private id$ = new ReplaySubject(1); - private partyId$ = new ReplaySubject(1); - - ngOnChanges(changes: ComponentChanges) { - if (changes.id) { - this.id$.next(this.id); - } - if (changes.partyId) { - this.partyId$.next(this.partyId); - } - } -} diff --git a/apps/control-center/src/app/shared/components/fistful-thrift-form/fistful-thrift-form.component.ts b/apps/control-center/src/app/shared/components/fistful-thrift-form/fistful-thrift-form.component.ts index 9f09f3812..44da27d12 100644 --- a/apps/control-center/src/app/shared/components/fistful-thrift-form/fistful-thrift-form.component.ts +++ b/apps/control-center/src/app/shared/components/fistful-thrift-form/fistful-thrift-form.component.ts @@ -1,8 +1,8 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { ThriftAstMetadata } from '@vality/fistful-proto'; -import { createControlProviders, getImportValue } from '@vality/matez'; +import { metadata$ } from '@vality/fistful-proto'; +import { createControlProviders } from '@vality/matez'; import { ThriftEditorModule, ThriftFormExtension, isTypeWithAliases } from '@vality/ng-thrift'; import { of } from 'rxjs'; import short from 'short-uuid'; @@ -16,7 +16,7 @@ import { BaseThriftFormSuperclass } from '../thrift-api-crud/thrift-forms/utils/ imports: [CommonModule, ReactiveFormsModule, ThriftEditorModule], }) export class FistfulThriftFormComponent extends BaseThriftFormSuperclass { - metadata$ = getImportValue(import('@vality/fistful-proto/metadata.json')); + metadata$ = metadata$; override internalExtensions$ = of([ { determinant: (data) => of(isTypeWithAliases(data, 'SourceID', 'fistful')), diff --git a/apps/control-center/src/app/shared/components/merchant-field/merchant-field.component.html b/apps/control-center/src/app/shared/components/merchant-field/merchant-field.component.html index d1a74184b..45d55f878 100644 --- a/apps/control-center/src/app/shared/components/merchant-field/merchant-field.component.html +++ b/apps/control-center/src/app/shared/components/merchant-field/merchant-field.component.html @@ -8,5 +8,5 @@ [required]="required" [size]="size" externalSearch - (searchChange)="searchChange$.next($event)" + (searchChange)="search($event)" > diff --git a/apps/control-center/src/app/shared/components/merchant-field/merchant-field.component.ts b/apps/control-center/src/app/shared/components/merchant-field/merchant-field.component.ts index 863e05fbf..798ca9bdb 100644 --- a/apps/control-center/src/app/shared/components/merchant-field/merchant-field.component.ts +++ b/apps/control-center/src/app/shared/components/merchant-field/merchant-field.component.ts @@ -1,87 +1,46 @@ -import { - AfterViewInit, - Component, - DestroyRef, - Input, - booleanAttribute, - inject, -} from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { Deanonimus } from '@vality/deanonimus-proto/deanonimus'; -import { PartyID } from '@vality/domain-proto/domain'; +import { Component, Input, booleanAttribute, inject } from '@angular/core'; +import { DomainObjectType, PartyID } from '@vality/domain-proto/domain'; import { FormControlSuperclass, - NotifyLogService, Option, SelectFieldComponent, createControlProviders, - debounceTimeWithFirst, - getValueChanges, } from '@vality/matez'; -import { BehaviorSubject, Observable, ReplaySubject, Subject, merge, of } from 'rxjs'; -import { catchError, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; -import { DEBOUNCE_TIME_MS } from '../../../tokens'; +import { FetchDomainObjectsService } from '../../../api/domain-config'; @Component({ selector: 'cc-merchant-field', templateUrl: 'merchant-field.component.html', - providers: createControlProviders(() => MerchantFieldComponent), + providers: [...createControlProviders(() => MerchantFieldComponent), FetchDomainObjectsService], standalone: false, }) -export class MerchantFieldComponent - extends FormControlSuperclass - implements AfterViewInit -{ - private deanonimusService = inject(Deanonimus); - private log = inject(NotifyLogService); - private destroyRef = inject(DestroyRef); +export class MerchantFieldComponent extends FormControlSuperclass { + private fetchDomainObjectsService = inject(FetchDomainObjectsService); + @Input() label: string; @Input({ transform: booleanAttribute }) required: boolean; @Input() size?: SelectFieldComponent['size']; @Input() appearance?: SelectFieldComponent['appearance']; @Input() hint?: string; - options$ = new ReplaySubject[]>(1); - searchChange$ = new Subject(); - progress$ = new BehaviorSubject(false); - - private debounceTimeMs = inject(DEBOUNCE_TIME_MS); - - ngAfterViewInit() { - merge(getValueChanges(this.control), this.searchChange$) - .pipe( - distinctUntilChanged(), - tap(() => { - this.options$.next([]); - this.progress$.next(true); - }), - debounceTimeWithFirst(this.debounceTimeMs), - switchMap((term) => this.searchOptions(term)), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe((options) => { - this.options$.next(options); - this.progress$.next(false); - }); - } + options$: Observable[]> = this.fetchDomainObjectsService.result$.pipe( + map((objs) => + objs.map((obj) => ({ + value: obj.ref.party_config.id, + label: obj.name || `#${obj.ref.party_config.id}`, + description: obj.description, + })), + ), + ); + progress$ = this.fetchDomainObjectsService.isLoading$; - private searchOptions(str: string): Observable[]> { - if (!str) { - return of([]); - } - return this.deanonimusService.searchParty(str).pipe( - map((parties) => - parties.map((p) => ({ - label: p.party.email, - value: p.party.id, - description: p.party.id, - })), - ), - catchError((err) => { - this.log.error(err, 'Search error'); - return of([]); - }), + search(search: string) { + this.fetchDomainObjectsService.load( + { type: DomainObjectType.party_config, query: search }, + { size: 1000 }, ); } } diff --git a/apps/control-center/src/app/shared/components/page-layout/components/sub-page-layout/sub-page-layout.component.html b/apps/control-center/src/app/shared/components/page-layout/components/sub-page-layout/sub-page-layout.component.html index 4994da408..93d96935f 100644 --- a/apps/control-center/src/app/shared/components/page-layout/components/sub-page-layout/sub-page-layout.component.html +++ b/apps/control-center/src/app/shared/components/page-layout/components/sub-page-layout/sub-page-layout.component.html @@ -13,16 +13,4 @@ } - - - - - + diff --git a/apps/control-center/src/app/shared/components/page-layout/components/sub-page-layout/sub-page-layout.component.ts b/apps/control-center/src/app/shared/components/page-layout/components/sub-page-layout/sub-page-layout.component.ts index 0fd0a40fe..623090908 100644 --- a/apps/control-center/src/app/shared/components/page-layout/components/sub-page-layout/sub-page-layout.component.ts +++ b/apps/control-center/src/app/shared/components/page-layout/components/sub-page-layout/sub-page-layout.component.ts @@ -1,23 +1,13 @@ import { CommonModule } from '@angular/common'; import { Component, inject, input } from '@angular/core'; -import { MatSidenav, MatSidenavContainer, MatSidenavContent } from '@angular/material/sidenav'; import { MatToolbar } from '@angular/material/toolbar'; -import { ActionsModule, Color, Link, NavComponent, TagModule } from '@vality/matez'; +import { ActionsModule, Color, TagModule } from '@vality/matez'; import { SidenavInfoService } from '../../../sidenav-info'; @Component({ selector: 'cc-sub-page-layout', - imports: [ - CommonModule, - NavComponent, - MatSidenav, - MatSidenavContent, - MatToolbar, - TagModule, - MatSidenavContainer, - ActionsModule, - ], + imports: [CommonModule, MatToolbar, TagModule, ActionsModule], templateUrl: './sub-page-layout.component.html', styles: ``, }) @@ -25,6 +15,5 @@ export class SubPageLayoutComponent { protected sidenavInfoService = inject(SidenavInfoService); title = input(); id = input(); - links = input([]); tags = input<{ value: string; color: Color }[] | null>([]); } diff --git a/apps/control-center/src/app/shared/components/page-layout/page-layout.component.scss b/apps/control-center/src/app/shared/components/page-layout/page-layout.component.scss index 7cbfd056b..33033961e 100644 --- a/apps/control-center/src/app/shared/components/page-layout/page-layout.component.scss +++ b/apps/control-center/src/app/shared/components/page-layout/page-layout.component.scss @@ -7,7 +7,7 @@ $offset: 24px; padding: $offset; &__full-height { - height: calc(100vh - var(--mat-toolbar-standard-height) - ($offset * 2)); + height: calc(100vh - ($offset * 2)); } .header { diff --git a/apps/control-center/src/app/shared/components/shop-card/shop-card.component.html b/apps/control-center/src/app/shared/components/shop-card/shop-card.component.html deleted file mode 100644 index 288e56a62..000000000 --- a/apps/control-center/src/app/shared/components/shop-card/shop-card.component.html +++ /dev/null @@ -1,31 +0,0 @@ - - - -
- -
-
- -
- -
-
- -
- -
-
-
-
diff --git a/apps/control-center/src/app/shared/components/shop-card/shop-card.component.ts b/apps/control-center/src/app/shared/components/shop-card/shop-card.component.ts deleted file mode 100644 index 29135bda8..000000000 --- a/apps/control-center/src/app/shared/components/shop-card/shop-card.component.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component, inject, input } from '@angular/core'; -import { toObservable } from '@angular/core/rxjs-interop'; -import { MatTab, MatTabGroup } from '@angular/material/tabs'; -import { PartyID, ShopID } from '@vality/domain-proto/internal/domain'; -import { ThriftViewerModule } from '@vality/ng-thrift'; -import { combineLatest } from 'rxjs'; -import { shareReplay, switchMap } from 'rxjs/operators'; - -import { PartiesStoreService } from '../../../api/payment-processing'; -import { CardComponent } from '../sidenav-info/components/card/card.component'; -import { DomainThriftViewerComponent } from '../thrift-api-crud'; - -@Component({ - selector: 'cc-shop-card', - imports: [ - CommonModule, - CardComponent, - DomainThriftViewerComponent, - ThriftViewerModule, - MatTabGroup, - MatTab, - ], - templateUrl: './shop-card.component.html', -}) -export class ShopCardComponent { - private partiesStoreService = inject(PartiesStoreService); - partyId = input.required(); - id = input.required(); - - progress$ = this.partiesStoreService.progress$; - shop$ = combineLatest([toObservable(this.partyId), toObservable(this.id)]).pipe( - switchMap(([partyId, id]) => this.partiesStoreService.getShop(id, partyId)), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - contractor$ = combineLatest([toObservable(this.partyId), toObservable(this.id)]).pipe( - switchMap(([partyId, id]) => this.partiesStoreService.getContractor(id, partyId)), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - contract$ = combineLatest([toObservable(this.partyId), toObservable(this.id)]).pipe( - switchMap(([partyId, id]) => this.partiesStoreService.getContract(id, partyId)), - shareReplay({ refCount: true, bufferSize: 1 }), - ); -} diff --git a/apps/control-center/src/app/shared/components/shop-contract-card/shop-contract-card.component.html b/apps/control-center/src/app/shared/components/shop-contract-card/shop-contract-card.component.html deleted file mode 100644 index a65b22b55..000000000 --- a/apps/control-center/src/app/shared/components/shop-contract-card/shop-contract-card.component.html +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/apps/control-center/src/app/shared/components/shop-contract-card/shop-contract-card.component.ts b/apps/control-center/src/app/shared/components/shop-contract-card/shop-contract-card.component.ts deleted file mode 100644 index 83b1c31cc..000000000 --- a/apps/control-center/src/app/shared/components/shop-contract-card/shop-contract-card.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component, Input, OnChanges, inject } from '@angular/core'; -import { ComponentChanges, progressTo } from '@vality/matez'; -import { BehaviorSubject, ReplaySubject, combineLatest, defer } from 'rxjs'; -import { shareReplay, switchMap } from 'rxjs/operators'; - -import { PartyManagementService } from '../../../api/payment-processing'; -import { CardComponent } from '../sidenav-info/components/card/card.component'; -import { DomainThriftViewerComponent } from '../thrift-api-crud'; - -@Component({ - selector: 'cc-shop-contract-card', - imports: [CommonModule, CardComponent, DomainThriftViewerComponent], - templateUrl: './shop-contract-card.component.html', -}) -export class ShopContractCardComponent implements OnChanges { - private partyManagementService = inject(PartyManagementService); - @Input() partyId: string; - @Input() id: string; - - progress$ = new BehaviorSubject(0); - contract$ = combineLatest([defer(() => this.partyId$), defer(() => this.id$)]).pipe( - switchMap(([partyID, id]) => - this.partyManagementService - .GetShopContract(partyID, id) - .pipe(progressTo(this.progress$)), - ), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - - private id$ = new ReplaySubject(1); - private partyId$ = new ReplaySubject(1); - - ngOnChanges(changes: ComponentChanges) { - if (changes.id) { - this.id$.next(this.id); - } - if (changes.partyId) { - this.partyId$.next(this.partyId); - } - } -} diff --git a/apps/control-center/src/app/shared/components/shop-field/shop-field.component.html b/apps/control-center/src/app/shared/components/shop-field/shop-field.component.html index 88261380d..083292a94 100644 --- a/apps/control-center/src/app/shared/components/shop-field/shop-field.component.html +++ b/apps/control-center/src/app/shared/components/shop-field/shop-field.component.html @@ -2,12 +2,12 @@ [appearance]="appearance" [formControl]="control" [hint]="hint" - [label]="label || (multiple ? 'Shops' : 'Shop')" - [multiple]="multiple" + [label]="label || (multiple() ? 'Shops' : 'Shop')" + [multiple]="multiple()" [options]="options$ | async" [progress]="!!(progress$ | async)" [required]="required" [size]="size" externalSearch - (searchChange)="this.searchChange$.next($event)" + (searchChange)="search($event)" > diff --git a/apps/control-center/src/app/shared/components/shop-field/shop-field.component.ts b/apps/control-center/src/app/shared/components/shop-field/shop-field.component.ts index c523532df..2bfd90bf5 100644 --- a/apps/control-center/src/app/shared/components/shop-field/shop-field.component.ts +++ b/apps/control-center/src/app/shared/components/shop-field/shop-field.component.ts @@ -1,157 +1,48 @@ -import { - AfterViewInit, - Component, - DestroyRef, - Injector, - Input, - booleanAttribute, - inject, - input, -} from '@angular/core'; -import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; -import { Deanonimus } from '@vality/deanonimus-proto/deanonimus'; -import { Party, PartyID, Shop } from '@vality/domain-proto/internal/domain'; +import { Component, Input, booleanAttribute, inject, input } from '@angular/core'; +import { DomainObjectType, PartyID } from '@vality/domain-proto/domain'; import { ShopID } from '@vality/domain-proto/payment_processing'; import { FormControlSuperclass, - NotifyLogService, Option, SelectFieldComponent, createControlProviders, - debounceTimeWithFirst, - progressTo, } from '@vality/matez'; -import { - BehaviorSubject, - Observable, - Subject, - asyncScheduler, - combineLatest, - concat, - concatMap, - of, - scheduled, -} from 'rxjs'; -import { catchError, distinctUntilChanged, map, switchMap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; -import { PartyManagementService } from '../../../api/payment-processing'; -import { DEBOUNCE_TIME_MS } from '../../../tokens'; +import { FetchDomainObjectsService } from '../../../api/domain-config'; @Component({ selector: 'cc-shop-field', templateUrl: './shop-field.component.html', - providers: createControlProviders(() => ShopFieldComponent), + providers: [...createControlProviders(() => ShopFieldComponent), FetchDomainObjectsService], standalone: false, }) -export class ShopFieldComponent - extends FormControlSuperclass - implements AfterViewInit -{ - private partyManagementService = inject(PartyManagementService); - private deanonimusService = inject(Deanonimus); - private log = inject(NotifyLogService); - private dr = inject(DestroyRef); - private injector = inject(Injector); +export class ShopFieldComponent extends FormControlSuperclass { + private fetchDomainObjectsService = inject(FetchDomainObjectsService); + @Input() label: string; @Input({ transform: booleanAttribute }) required: boolean; @Input() size?: SelectFieldComponent['size']; @Input() appearance?: SelectFieldComponent['appearance']; @Input() hint?: string; - @Input({ transform: booleanAttribute }) multiple = false; - partyId = input(); - - options$ = new BehaviorSubject[]>([]); - searchChange$ = new Subject(); - progress$ = new BehaviorSubject(0); - - private debounceTimeMs = inject(DEBOUNCE_TIME_MS); - - ngAfterViewInit() { - const initValues = this.getCurrentValues(); - combineLatest([ - concat( - scheduled(initValues.length ? initValues : [''], asyncScheduler), - this.searchChange$.pipe( - distinctUntilChanged(), - debounceTimeWithFirst(this.debounceTimeMs), - ), - ), - toObservable(this.partyId, { injector: this.injector }).pipe( - switchMap((partyId) => - partyId - ? this.partyManagementService.Get(partyId).pipe(progressTo(this.progress$)) - : of(null), - ), - ), - ]) - .pipe( - concatMap(([term, party]) => - party - ? of(this.searchShopsByParty(party, term)) - : this.searchShops(term).pipe(progressTo(this.progress$)), - ), - takeUntilDestroyed(this.dr), - ) - .subscribe((options) => { - const oldOptions = this.options$.value; - this.options$.next( - this.getCurrentValues().reduce((acc, v) => { - if (acc.every((o) => o.value !== v)) { - acc.push( - oldOptions.find((f) => f.value === v) ?? { - label: `#${v}`, - value: v, - description: v, - }, - ); - } - return acc; - }, options), - ); - }); - } - - private searchShops(search: string): Observable[]> { - return this.deanonimusService.searchShopText(search).pipe( - map((partyShops) => - partyShops.map((p) => ({ - label: p.shop.details.name, - value: p.shop.id, - description: p.shop.id, - })), - ), - catchError((err) => { - this.log.error(err, 'Search error'); - return of([]); - }), + multiple = input(false, { transform: booleanAttribute }); + + options$: Observable[]> = this.fetchDomainObjectsService.result$.pipe( + map((objs) => + objs.map((obj) => ({ + value: obj.ref.shop_config.id, + label: obj.name || `#${obj.ref.shop_config.id}`, + description: obj.description, + })), + ), + ); + progress$ = this.fetchDomainObjectsService.isLoading$; + + search(search: string) { + this.fetchDomainObjectsService.load( + { type: DomainObjectType.shop_config, query: search }, + { size: 1000 }, ); } - - private searchShopsByParty(party: Party, search: string): Option[] { - const searchStr = search.trim().toLowerCase(); - return Array.from(party.shops.values()) - .map((shop) => ({ party, shop })) - .sort( - (a, b) => - +this.includeSearchStr(a, searchStr) - +this.includeSearchStr(b, searchStr), - ) - .map((p) => ({ - label: p.shop.details.name, - value: p.shop.id, - description: p.shop.id, - })); - } - - private includeSearchStr({ shop, party }: { party: Party; shop: Shop }, searchStr: string) { - return [shop.id, shop.details.name, party.id, party.party_name].some((v) => - String(v ?? '') - .toLowerCase() - .includes(searchStr), - ); - } - - private getCurrentValues() { - const v = this.control.value; - return v ? (Array.isArray(v) ? v : [v]) : []; - } } diff --git a/apps/control-center/src/app/shared/components/shops-table/shops-table.component.html b/apps/control-center/src/app/shared/components/shops-table/shops-table.component.html index 2f2de2d13..74c3e2786 100644 --- a/apps/control-center/src/app/shared/components/shops-table/shops-table.component.html +++ b/apps/control-center/src/app/shared/components/shops-table/shops-table.component.html @@ -6,5 +6,5 @@ name="shops" standaloneFilter (filterChange)="filterChange.emit($event)" - (update)="update.emit()" + (update)="update.emit($event)" > diff --git a/apps/control-center/src/app/shared/components/shops-table/shops-table.component.ts b/apps/control-center/src/app/shared/components/shops-table/shops-table.component.ts index 2f6e7f21a..922da572f 100644 --- a/apps/control-center/src/app/shared/components/shops-table/shops-table.component.ts +++ b/apps/control-center/src/app/shared/components/shops-table/shops-table.component.ts @@ -11,7 +11,8 @@ import { import { toObservable } from '@angular/core/rxjs-interop'; import { MatCardModule } from '@angular/material/card'; import { Router } from '@angular/router'; -import { Party, PartyID, RoutingRulesetRef, Shop } from '@vality/domain-proto/domain'; +import { RoutingRulesetRef, ShopConfigObject } from '@vality/domain-proto/domain'; +import { PartyManagement } from '@vality/domain-proto/payment_processing'; import { Column, ConfirmDialogComponent, @@ -20,35 +21,24 @@ import { InputFieldModule, NotifyLogService, TableModule, + UpdateOptions, createMenuColumn, } from '@vality/matez'; import { getUnionKey } from '@vality/ng-thrift'; import { isNil } from 'lodash-es'; import startCase from 'lodash-es/startCase'; import { combineLatest, map, of, switchMap } from 'rxjs'; -import { filter, first, shareReplay, startWith, take } from 'rxjs/operators'; -import { MemoizeExpiring } from 'typescript-memoize'; +import { filter, startWith } from 'rxjs/operators'; -import { DomainStoreService } from '../../../api/domain-config'; -import { PartyManagementService } from '../../../api/payment-processing'; +import { DomainObjectsStoreService } from '../../../api/domain-config'; import { DelegateWithPaymentInstitution, PartyDelegateRulesetsService, } from '../../../sections/routing-rules/party-delegate-rulesets'; import { RoutingRulesType } from '../../../sections/routing-rules/types/routing-rules-type'; import { createPartyColumn } from '../../utils'; -import { ShopCardComponent } from '../shop-card/shop-card.component'; -import { ShopContractCardComponent } from '../shop-contract-card/shop-contract-card.component'; import { SidenavInfoService } from '../sidenav-info'; -import { DomainObjectCardComponent, getDomainObjectDetails } from '../thrift-api-crud'; - -export interface ShopParty { - shop: Shop; - party: { - id: PartyID; - email: Party['contact_info']['registration_email']; - }; -} +import { DomainObjectCardComponent } from '../thrift-api-crud/domain2'; @Component({ selector: 'cc-shops-table', @@ -58,106 +48,85 @@ export interface ShopParty { }) export class ShopsTableComponent { private sidenavInfoService = inject(SidenavInfoService); - private partyManagementService = inject(PartyManagementService); + private partyManagementService = inject(PartyManagement); private dialogService = inject(DialogService); private log = inject(NotifyLogService); private router = inject(Router); private partyDelegateRulesetsService = inject(PartyDelegateRulesetsService); - private domainStoreService = inject(DomainStoreService); + private domainStoreService = inject(DomainObjectsStoreService); private injector = inject(Injector); - shops = input([]); + + shops = input([]); @Input() progress: number | boolean = false; - @Output() update = new EventEmitter(); + @Output() update = new EventEmitter(); @Output() filterChange = new EventEmitter(); noPartyColumn = input(false, { transform: booleanAttribute }); - columns: Column[] = [ + columns: Column[] = [ { - field: 'shop.id', + field: 'ref.id', }, { - field: 'shop.details.name', + field: 'details.name', cell: (d) => ({ - description: d.shop.details.description, + description: d.data.details.description, click: () => { - this.sidenavInfoService.toggle(ShopCardComponent, { - partyId: d.party.id, - id: d.shop.id, - }); - }, - }), - }, - createPartyColumn( - (d) => ({ - id: d.party.id, - partyName: d.party.email, - }), - { - hidden: toObservable(this.noPartyColumn), - }, - ), - { - field: 'shop.contract_id', - header: 'Contract', - cell: (d) => ({ - click: () => { - this.sidenavInfoService.toggle(ShopContractCardComponent, { - partyId: d.party.id, - id: d.shop.id, + this.sidenavInfoService.toggle(DomainObjectCardComponent, { + ref: { shop_config: { id: d.ref.id } }, }); }, }), }, + createPartyColumn((d) => ({ id: d.data.party_id }), { + hidden: toObservable(this.noPartyColumn), + }), { field: 'terms', lazyCell: (d) => - this.getTerms(d.party.id, d.shop.id).pipe( - map((terms) => getDomainObjectDetails(terms)), - map((details) => ({ - value: details.label, - description: details.description, - click: () => { - this.getTerms(d.party.id, d.shop.id).subscribe((terms) => { + this.domainStoreService + .getLimitedObject({ term_set_hierarchy: { id: d.data.terms.id } }) + .value$.pipe( + map((obj) => ({ + value: obj?.name, + description: obj?.description, + click: () => { this.sidenavInfoService.toggle(DomainObjectCardComponent, { - ref: { - term_set_hierarchy: terms.term_set_hierarchy.ref, - }, + ref: obj.ref, }); - }); - }, - })), - ), + }, + })), + ), }, { - field: 'shop.location.url', + field: 'location.url', }, { - field: 'shop.account.currency.symbolic_code', + field: 'account.currency.symbolic_code', header: 'Currency', }, { - field: 'shop.blocking', + field: 'blocking', cell: (d) => ({ - value: startCase(getUnionKey(d.shop.blocking)), + value: startCase(getUnionKey(d.data.blocking)), color: ( { blocked: 'warn', unblocked: 'success', } as const - )[getUnionKey(d.shop.blocking)], + )[getUnionKey(d.data.blocking)], }), }, { - field: 'shop.suspension', + field: 'suspension', cell: (d) => ({ - value: startCase(getUnionKey(d.shop.suspension)), + value: startCase(getUnionKey(d.data.suspension)), color: ( { suspended: 'warn', active: 'success', } as const - )[getUnionKey(d.shop.suspension)], + )[getUnionKey(d.data.suspension)], }), }, createMenuColumn((d) => @@ -167,19 +136,19 @@ export class ShopsTableComponent { ...delegatesByParty.rulesetIds.map((id) => { const rulesetId = delegatesByParty.delegatesWithPaymentInstitutionByParty - ?.get?.(d.party.id) + ?.get?.(d.data.party_id) ?.find?.((v) => v?.partyDelegate?.ruleset?.id === id) ?.partyDelegate?.ruleset?.id; return { label: `Routing rules #${id}`, click: () => - this.openRoutingRules(rulesetId, d.shop.id, d.party.id), + this.openRoutingRules(rulesetId, d.ref.id, d.data.party_id), disabled: isNil(rulesetId), }; }), { label: - getUnionKey(d.shop.suspension) === 'suspended' + getUnionKey(d.data.suspension) === 'suspended' ? 'Activate' : 'Suspend', click: () => { @@ -187,7 +156,7 @@ export class ShopsTableComponent { }, }, { - label: getUnionKey(d.shop.blocking) === 'blocked' ? 'Unblock' : 'Block', + label: getUnionKey(d.data.blocking) === 'blocked' ? 'Unblock' : 'Block', click: () => { this.toggleBlocking(d); }, @@ -198,19 +167,28 @@ export class ShopsTableComponent { ), ]; - toggleBlocking({ party, shop }: ShopParty) { + toggleBlocking(shop: ShopConfigObject) { this.dialogService .open(ConfirmDialogComponent, { - title: getUnionKey(shop.blocking) === 'unblocked' ? 'Block shop' : 'Unblock shop', + title: + getUnionKey(shop.data.blocking) === 'unblocked' ? 'Block shop' : 'Unblock shop', hasReason: true, }) .afterClosed() .pipe( filter((r) => r.status === DialogResponseStatus.Success), switchMap((r) => - getUnionKey(shop.blocking) === 'unblocked' - ? this.partyManagementService.BlockShop(party.id, shop.id, r.data.reason) - : this.partyManagementService.UnblockShop(party.id, shop.id, r.data.reason), + getUnionKey(shop.data.blocking) === 'unblocked' + ? this.partyManagementService.BlockShop( + shop.data.party_id, + shop.ref.id, + r.data.reason, + ) + : this.partyManagementService.UnblockShop( + shop.data.party_id, + shop.ref.id, + r.data.reason, + ), ), ) .subscribe({ @@ -224,18 +202,21 @@ export class ShopsTableComponent { }); } - toggleSuspension({ shop, party }: ShopParty) { + toggleSuspension(shop: ShopConfigObject) { this.dialogService .open(ConfirmDialogComponent, { - title: getUnionKey(shop.suspension) === 'active' ? 'Suspend shop' : 'Activate shop', + title: + getUnionKey(shop.data.suspension) === 'active' + ? 'Suspend shop' + : 'Activate shop', }) .afterClosed() .pipe( filter((r) => r.status === DialogResponseStatus.Success), switchMap(() => - getUnionKey(shop.suspension) === 'active' - ? this.partyManagementService.SuspendShop(party.id, shop.id) - : this.partyManagementService.ActivateShop(party.id, shop.id), + getUnionKey(shop.data.suspension) === 'active' + ? this.partyManagementService.SuspendShop(shop.data.party_id, shop.ref.id) + : this.partyManagementService.ActivateShop(shop.data.party_id, shop.ref.id), ), ) .subscribe({ @@ -249,42 +230,23 @@ export class ShopsTableComponent { }); } - @MemoizeExpiring(5 * 60_000, (...args) => JSON.stringify(args)) - getTerms(partyId: string, shopId: string) { - return this.partyManagementService.GetShopContract(partyId, shopId).pipe( - map((c) => { - if (c.contract.adjustments?.length) { - return c.contract.adjustments.at(-1).terms.id; - } - return c.contract.terms.id; - }), - switchMap((id) => - this.domainStoreService.getObject({ term_set_hierarchy: { id } }).pipe(first()), - ), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - } - private openRoutingRules( partyDelegateRulesetId: RoutingRulesetRef['id'], shopId: string, partyId: string, ) { this.domainStoreService - .getObjects('routing_rules') - .pipe( - take(1), - map((rules) => rules.find((r) => r.ref.id === partyDelegateRulesetId)), - ) + .getObject({ routing_rules: { id: partyDelegateRulesetId } }) + .getFirstValue() .subscribe((ruleset) => { const delegates = - ruleset.data?.decisions?.delegates?.filter?.( + ruleset?.object?.routing_rules?.data?.decisions?.delegates?.filter?.( (delegate) => delegate?.allowed?.condition?.party?.id === partyId && delegate?.allowed?.condition?.party?.definition?.shop_is === shopId, ) || []; const paymentRulesCommands = [ - '/party', + '/parties', partyId, 'routing-rules', 'payment', @@ -318,7 +280,7 @@ export class ShopsTableComponent { return toObservable(this.shops, { injector: this.injector }).pipe( startWith(null), map((shops) => - shops?.length ? Array.from(new Set(shops.map((s) => s.party.id))) : [], + shops?.length ? Array.from(new Set(shops.map((s) => s.data.party_id))) : [], ), switchMap((parties) => parties?.length diff --git a/apps/control-center/src/app/shared/components/terminal-delegates-card/terminal-delegates-card.component.ts b/apps/control-center/src/app/shared/components/terminal-delegates-card/terminal-delegates-card.component.ts index e9e87adac..cfc648c1d 100644 --- a/apps/control-center/src/app/shared/components/terminal-delegates-card/terminal-delegates-card.component.ts +++ b/apps/control-center/src/app/shared/components/terminal-delegates-card/terminal-delegates-card.component.ts @@ -14,13 +14,13 @@ import { MatBadgeModule } from '@angular/material/badge'; import { MatButtonModule } from '@angular/material/button'; import { MatTooltipModule } from '@angular/material/tooltip'; import { TerminalRef } from '@vality/domain-proto/domain'; -import { Column, ComponentChanges, NotifyLogService, TableModule } from '@vality/matez'; -import { getUnionKey, getUnionValue } from '@vality/ng-thrift'; +import { Column, ComponentChanges, TableModule } from '@vality/matez'; +import { getUnionKey } from '@vality/ng-thrift'; import startCase from 'lodash-es/startCase'; import { ReplaySubject, combineLatest, defer, of, switchMap } from 'rxjs'; import { map, shareReplay, take } from 'rxjs/operators'; -import { DomainStoreService } from '../../../api/domain-config'; +import { DomainService, RoutingRulesStoreService } from '../../../api/domain-config'; import { PartiesStoreService } from '../../../api/payment-processing'; import { changeCandidatesAllowed } from '../../../sections/routing-rules/utils/toggle-candidate-allowed'; import { @@ -30,7 +30,7 @@ import { import { createPartyColumn, createPredicateColumn, getPredicateBoolean } from '../../utils'; import { SidenavInfoService } from '../sidenav-info'; import { CardComponent } from '../sidenav-info/components/card/card.component'; -import { DomainObjectCardComponent } from '../thrift-api-crud'; +import { DomainObjectCardComponent } from '../thrift-api-crud/domain2'; @Component({ selector: 'cc-terminal-delegates-card', @@ -46,14 +46,15 @@ import { DomainObjectCardComponent } from '../thrift-api-crud'; }) export class TerminalDelegatesCardComponent implements OnChanges { private partiesStoreService = inject(PartiesStoreService); - private domainStoreService = inject(DomainStoreService); + private routingRulesStoreService = inject(RoutingRulesStoreService); + private domainService = inject(DomainService); private sidenavInfoService = inject(SidenavInfoService); private injector = inject(Injector); - private log = inject(NotifyLogService); private dr = inject(DestroyRef); + @Input() ref: TerminalRef; - progress$ = this.domainStoreService.isLoading$; + progress$ = this.routingRulesStoreService.isLoading$; columns: Column[] = [ { header: 'Routing Rule', @@ -106,51 +107,51 @@ export class TerminalDelegatesCardComponent implements OnChanges { }, { field: 'definition', - cell: (d) => - this.partiesStoreService.get(d.delegate.allowed.condition?.party?.id).pipe( - map((p) => ({ - value: - (getUnionKey(d.delegate.allowed.condition?.party?.definition) === - 'shop_is' - ? p.shops.get( - getUnionValue( - d.delegate.allowed.condition?.party?.definition, - ), - )?.details?.name - : p.wallets.get( - getUnionValue( - d.delegate.allowed.condition?.party?.definition, - ), - )?.name) ?? - `#${getUnionValue(d.delegate.allowed.condition?.party?.definition)}`, - - description: getUnionValue(d.delegate.allowed.condition?.party?.definition), - link: () => - `/party/${d.delegate.allowed.condition.party.id}/routing-rules/${ - getUnionKey(d.delegate.allowed.condition?.party?.definition) === - 'shop_is' - ? 'payment' - : 'withdrawal' - }/${d.rule.ref.id}/delegate/${d.delegate.ruleset.id}`, - })), - ), + cell: (d) => { + const party = d.delegate.allowed.condition?.party; + switch (getUnionKey(party?.definition)) { + case 'shop_is': + return this.partiesStoreService + .getShop(party?.definition?.shop_is) + .value$.pipe( + map((shop) => ({ + value: shop.data.details.name, + description: shop.ref.id, + link: () => + `/parties/${party.id}/routing-rules/payment/${d.rule.ref.id}/delegate/${d.delegate.ruleset.id}`, + })), + ); + case 'wallet_is': + return this.partiesStoreService + .getShop(party?.definition?.wallet_is) + .value$.pipe( + map((wallet) => ({ + value: wallet.data.details.name, + description: wallet.ref.id, + link: () => + `/parties/${party.id}/routing-rules/withdrawal/${d.rule.ref.id}/delegate/${d.delegate.ruleset.id}`, + })), + ); + case 'contract_is': + return { + value: party.definition.contract_is, + }; + } + }, }, ]; terminalObj$ = defer(() => this.ref$).pipe( - switchMap((ref) => this.domainStoreService.getObject({ terminal: ref })), + switchMap((ref) => this.domainService.get({ terminal: ref })), + map((obj) => obj.object), shareReplay({ refCount: true, bufferSize: 1 }), ); rules$ = this.terminalObj$.pipe( switchMap((terminalObj) => - this.domainStoreService - .getObjects('routing_rules') - .pipe( - map((rules) => - terminalObj - ? getTerminalShopWalletDelegates(rules, terminalObj.terminal) - : [], - ), + this.routingRulesStoreService.routingRules$.pipe( + map((rules) => + terminalObj ? getTerminalShopWalletDelegates(rules, terminalObj.terminal) : [], ), + ), ), shareReplay({ refCount: true, bufferSize: 1 }), ); diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/create-domain-object-dialog/create-domain-object-dialog.component.html b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/create-domain-object-dialog/create-domain-object-dialog.component.html deleted file mode 100644 index 44aea8c4e..000000000 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/create-domain-object-dialog/create-domain-object-dialog.component.html +++ /dev/null @@ -1,29 +0,0 @@ - - @if (isReview) { - - } @else { - - } - - - @if (isReview) { - - - } @else { - - } - - diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/create-domain-object-dialog/create-domain-object-dialog.component.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/create-domain-object-dialog/create-domain-object-dialog.component.ts deleted file mode 100644 index 03b4eee9a..000000000 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/create-domain-object-dialog/create-domain-object-dialog.component.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component, DestroyRef, OnInit, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { DomainObject } from '@vality/domain-proto/domain'; -import { - DEFAULT_DIALOG_CONFIG, - DEFAULT_DIALOG_CONFIG_FULL_HEIGHT, - DialogConfig, - DialogModule, - DialogSuperclass, - NotifyLogService, - progressTo, -} from '@vality/matez'; -import { getUnionKey } from '@vality/ng-thrift'; -import { BehaviorSubject, EMPTY, switchMap } from 'rxjs'; -import { catchError, first, map } from 'rxjs/operators'; -import { ValuesType } from 'utility-types'; - -import { DomainStoreService } from '../../../../../api/domain-config'; -import { DomainNavigateService } from '../../../../../sections/domain/services/domain-navigate.service'; -import { MetadataService } from '../../../../../sections/domain/services/metadata.service'; -import { DomainThriftFormComponent } from '../domain-thrift-form'; -import { DomainThriftViewerComponent } from '../domain-thrift-viewer'; - -@Component({ - selector: 'cc-create-domain-object-dialog', - imports: [ - CommonModule, - DialogModule, - MatButtonModule, - DomainThriftFormComponent, - ReactiveFormsModule, - DomainThriftViewerComponent, - ], - templateUrl: './create-domain-object-dialog.component.html', -}) -export class CreateDomainObjectDialogComponent - extends DialogSuperclass - implements OnInit -{ - private domainStoreService = inject(DomainStoreService); - private destroyRef = inject(DestroyRef); - private log = inject(NotifyLogService); - private domainNavigateService = inject(DomainNavigateService); - private metadataService = inject(MetadataService); - static override defaultDialogConfig: ValuesType = { - ...DEFAULT_DIALOG_CONFIG.large, - minHeight: DEFAULT_DIALOG_CONFIG_FULL_HEIGHT, - }; - - control = new FormControl(null, [Validators.required]); - progress$ = new BehaviorSubject(0); - isReview = false; - - ngOnInit() { - if (this.dialogData) { - this.metadataService - .getDomainFieldByType(this.dialogData.objectType) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((type) => { - this.control.setValue({ [type.name]: {} }); - }); - } - } - - create(attempts = 1) { - this.domainStoreService - .commit({ ops: [{ insert: { object: this.control.value } }] }) - .pipe( - catchError((err) => { - if (err?.name === 'ObsoleteCommitVersion' && attempts !== 0) { - this.domainStoreService.forceReload(); - this.create(attempts - 1); - this.log.error(err, `Domain config is out of date, one more attempt...`); - return EMPTY; - } - throw err; - }), - switchMap(() => this.getType()), - progressTo(this.progress$), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe({ - next: (type) => { - this.log.successOperation('create', 'domain object'); - void this.domainNavigateService.toType(String(type)); - this.closeWithSuccess(); - }, - error: (err) => { - this.log.errorOperation(err, 'create', 'domain object'); - }, - }); - } - - private getType() { - return this.metadataService.getDomainFieldByName(getUnionKey(this.control.value)).pipe( - map((f) => String(f.type)), - first(), - ); - } -} diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/create-domain-object-dialog/index.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/create-domain-object-dialog/index.ts deleted file mode 100644 index 47b87ae2c..000000000 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/create-domain-object-dialog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './create-domain-object-dialog.component'; diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-card/domain-object-card.component.html b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-card/domain-object-card.component.html deleted file mode 100644 index 5c429b4d7..000000000 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-card/domain-object-card.component.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-card/domain-object-card.component.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-card/domain-object-card.component.ts deleted file mode 100644 index 88dfe7d0e..000000000 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-card/domain-object-card.component.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component, DestroyRef, Input, OnChanges, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { MatButtonModule } from '@angular/material/button'; -import { Reference } from '@vality/domain-proto/internal/domain'; -import { - ComponentChanges, - DialogService, - UnionEnum, - createStorageValue, - enumHasValue, -} from '@vality/matez'; -import { ViewerKind, isEqualThrift } from '@vality/ng-thrift'; -import { ReplaySubject, combineLatest, switchMap } from 'rxjs'; -import { first, map, shareReplay } from 'rxjs/operators'; - -import { DomainStoreService } from '../../../../../api/domain-config/stores/domain-store.service'; -import { SidenavInfoModule } from '../../../sidenav-info'; -import { CardComponent } from '../../../sidenav-info/components/card/card.component'; -import { DomainThriftViewerComponent } from '../domain-thrift-viewer'; -import { EditDomainObjectDialogComponent } from '../edit-domain-object-dialog'; -import { DeleteDomainObjectService } from '../services'; -import { getDomainObjectDetails } from '../utils'; - -@Component({ - selector: 'cc-domain-object-card', - imports: [ - CommonModule, - DomainThriftViewerComponent, - CardComponent, - SidenavInfoModule, - MatButtonModule, - ], - templateUrl: './domain-object-card.component.html', -}) -export class DomainObjectCardComponent implements OnChanges { - private deleteDomainObjectService = inject(DeleteDomainObjectService); - private domainStoreService = inject(DomainStoreService); - private destroyRef = inject(DestroyRef); - private dialogService = inject(DialogService); - @Input() ref!: Reference; - - ref$ = new ReplaySubject(1); - progress$ = this.domainStoreService.isLoading$; - domainObject$ = combineLatest([this.domainStoreService.domain$, this.ref$]).pipe( - map(([domain, ref]) => - domain.get(Array.from(domain.keys()).find((k) => isEqualThrift(k, ref))), - ), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - title$ = this.domainObject$.pipe( - map((domainObject) => getDomainObjectDetails(domainObject)?.label), - ); - - kind = createStorageValue>('domain-object-card-view', { - deserialize: (v) => (enumHasValue(ViewerKind, v) ? v : ViewerKind.Component), - }); - - ngOnChanges(changes: ComponentChanges) { - if (changes.ref) { - this.ref$.next(this.ref); - } - } - - edit() { - this.domainObject$ - .pipe( - first(), - switchMap((domainObject) => - this.dialogService - .open(EditDomainObjectDialogComponent, { domainObject }) - .afterClosed(), - ), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe(); - } - - delete() { - this.deleteDomainObjectService.delete(this.ref); - } -} diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-field/domain-object-field.component.html b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-field/domain-object-field.component.html index 5d938d5e4..e24d37241 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-field/domain-object-field.component.html +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-field/domain-object-field.component.html @@ -1,6 +1,13 @@ diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-field/domain-object-field.component.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-field/domain-object-field.component.ts index 7e1d2867f..9dba87572 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-field/domain-object-field.component.ts +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-object-field/domain-object-field.component.ts @@ -1,57 +1,57 @@ import { CommonModule } from '@angular/common'; -import { Component, Input, OnChanges, inject } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { DomainObject } from '@vality/domain-proto/internal/domain'; +import { Component, Input, booleanAttribute, inject, input } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { DomainObjectType, Reference } from '@vality/domain-proto/domain'; import { - ComponentChanges, FormControlSuperclass, Option, + SelectFieldComponent, SelectFieldModule, createControlProviders, } from '@vality/matez'; -import { ReplaySubject, defer, switchMap } from 'rxjs'; -import { map, shareReplay } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; -import { DomainStoreService } from '../../../../../api/domain-config/stores/domain-store.service'; -import { getDomainObjectValueDetailsFn } from '../utils'; - -type DomainObjectID = unknown; +import { FetchDomainObjectsService } from '../../../../../api/domain-config/services/fetch-domain-objects.service'; +import { ReferenceId, getReferenceId } from '../utils/get-reference-id'; @Component({ selector: 'cc-domain-object-field', templateUrl: './domain-object-field.component.html', - providers: createControlProviders(() => DomainObjectFieldComponent), - imports: [CommonModule, ReactiveFormsModule, SelectFieldModule], + providers: [ + ...createControlProviders(() => DomainObjectFieldComponent), + FetchDomainObjectsService, + ], + imports: [SelectFieldModule, CommonModule, ReactiveFormsModule, FormsModule], }) -export class DomainObjectFieldComponent - extends FormControlSuperclass - implements OnChanges -{ - private domainStoreService = inject(DomainStoreService); +export class DomainObjectFieldComponent extends FormControlSuperclass< + ReferenceId | ReferenceId[] +> { + private fetchDomainObjectsService = inject(FetchDomainObjectsService); + @Input() name: T; + @Input() label: string; + @Input({ transform: booleanAttribute }) required: boolean; + @Input() size?: SelectFieldComponent['size']; + @Input() appearance?: SelectFieldComponent['appearance']; + @Input() hint?: string; + multiple = input(false, { transform: booleanAttribute }); - options$ = defer(() => this.name$).pipe( - switchMap((name) => this.domainStoreService.getObjects(name)), - map((objs) => { - const domainObjectToOption = getDomainObjectValueDetailsFn(this.name); - return objs.map(domainObjectToOption).map( - (o): Option => ({ - label: o.label, - value: o.id, - description: String(o.id), - }), - ); - }), - shareReplay({ bufferSize: 1, refCount: true }), + options$: Observable[]> = this.fetchDomainObjectsService.result$.pipe( + map((objs) => + objs.map((obj) => ({ + value: getReferenceId(obj.ref), + label: obj.name || `#${getReferenceId(obj.ref)}`, + description: obj.description, + })), + ), ); - isLoading$ = this.domainStoreService.isLoading$; - - private name$ = new ReplaySubject(1); + progress$ = this.fetchDomainObjectsService.isLoading$; - override ngOnChanges(changes: ComponentChanges>) { - super.ngOnChanges(changes); - if (changes.name) { - this.name$.next(this.name); - } + search(search: string) { + this.fetchDomainObjectsService.load( + { type: DomainObjectType[this.name], query: search }, + { size: 1000 }, + ); } } diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-form/domain-thrift-form.component.html b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-editor/domain-thrift-editor.component.html similarity index 100% rename from apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-form/domain-thrift-form.component.html rename to apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-editor/domain-thrift-editor.component.html diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-form/domain-thrift-form.component.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-editor/domain-thrift-editor.component.ts similarity index 64% rename from apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-form/domain-thrift-form.component.ts rename to apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-editor/domain-thrift-editor.component.ts index 695c6b4e9..f4e23d065 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-form/domain-thrift-form.component.ts +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-editor/domain-thrift-editor.component.ts @@ -2,10 +2,9 @@ import { CommonModule } from '@angular/common'; import { Component, inject, input } from '@angular/core'; import { toObservable } from '@angular/core/rxjs-interop'; import { ReactiveFormsModule } from '@angular/forms'; -import { Claim } from '@vality/domain-proto/claim_management'; +import { metadata$ } from '@vality/domain-proto'; import { Party } from '@vality/domain-proto/domain'; -import { ThriftAstMetadata } from '@vality/fistful-proto'; -import { createControlProviders, getImportValue } from '@vality/matez'; +import { createControlProviders } from '@vality/matez'; import { ThriftEditorModule, ThriftFormModule } from '@vality/ng-thrift'; import { combineLatest, filter } from 'rxjs'; import { map, shareReplay, startWith } from 'rxjs/operators'; @@ -14,24 +13,21 @@ import { DomainMetadataFormExtensionsService } from '../../../../services'; import { BaseThriftFormSuperclass } from '../../thrift-forms/utils/thrift-form-superclass'; @Component({ - selector: 'cc-domain-thrift-form', - templateUrl: './domain-thrift-form.component.html', + selector: 'cc-domain-thrift-editor', + templateUrl: './domain-thrift-editor.component.html', providers: createControlProviders(() => DomainThriftFormComponent), imports: [CommonModule, ReactiveFormsModule, ThriftFormModule, ThriftEditorModule], }) export class DomainThriftFormComponent extends BaseThriftFormSuperclass { private domainMetadataFormExtensionsService = inject(DomainMetadataFormExtensionsService); party = input(); - claim = input(); - metadata$ = getImportValue(import('@vality/domain-proto/metadata.json')); + metadata$ = metadata$; override internalExtensions$ = combineLatest([ this.domainMetadataFormExtensionsService.extensions$, - combineLatest([toObservable(this.party), toObservable(this.claim)]).pipe( - filter(([party, claim]) => !!party && !!claim), - map(([party, claim]) => - this.domainMetadataFormExtensionsService.createPartyClaimExtensions(party, claim), - ), + toObservable(this.party).pipe( + filter(Boolean), + map((party) => this.domainMetadataFormExtensionsService.createPartyExtensions(party)), startWith([]), ), ]).pipe( diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-editor/index.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-editor/index.ts new file mode 100644 index 000000000..9ed1e7a10 --- /dev/null +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-editor/index.ts @@ -0,0 +1 @@ +export * from './domain-thrift-editor.component'; diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-form-dialog/domain-thrift-form-dialog.component.html b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-form-dialog/domain-thrift-form-dialog.component.html index 75fe81e1c..73f34f15b 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-form-dialog/domain-thrift-form-dialog.component.html +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/domain-thrift-form-dialog/domain-thrift-form-dialog.component.html @@ -1,9 +1,9 @@ - + > - } - @if ((hasConflict$ | async) && step !== stepEnum.SourceReview) { - - } - @if (step !== stepEnum.Review) { - - } - @if (step === stepEnum.Review) { - - } -
diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/edit-domain-object-dialog/edit-domain-object-dialog.component.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/edit-domain-object-dialog/edit-domain-object-dialog.component.ts deleted file mode 100644 index 67f4a7512..000000000 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/edit-domain-object-dialog/edit-domain-object-dialog.component.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component, DestroyRef, inject } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { DomainObject } from '@vality/domain-proto/domain'; -import { - DEFAULT_DIALOG_CONFIG, - DEFAULT_DIALOG_CONFIG_FULL_HEIGHT, - DialogConfig, - DialogModule, - DialogSuperclass, - NotifyLogService, - UnionEnum, - createStorageValue, - enumHasValue, - getValueChanges, - inProgressFrom, - progressTo, -} from '@vality/matez'; -import { - EditorKind, - ThriftPipesModule, - getUnionKey, - getUnionValue, - isEqualThrift, -} from '@vality/ng-thrift'; -import { BehaviorSubject, EMPTY, Observable, combineLatest, switchMap } from 'rxjs'; -import { catchError, distinctUntilChanged, first, map, shareReplay } from 'rxjs/operators'; -import { ValuesType } from 'utility-types'; - -import { DomainSecretService, DomainStoreService } from '../../../../../api/domain-config'; -import { DomainNavigateService } from '../../../../../sections/domain/services/domain-navigate.service'; -import { MetadataService } from '../../../../../sections/domain/services/metadata.service'; -import { DomainThriftFormComponent } from '../domain-thrift-form'; -import { DomainThriftViewerComponent } from '../domain-thrift-viewer'; - -enum Step { - Edit, - Review, - SourceReview, -} - -@Component({ - selector: 'cc-edit-domain-object-dialog', - imports: [ - CommonModule, - DialogModule, - MatButtonModule, - DomainThriftFormComponent, - ReactiveFormsModule, - DomainThriftViewerComponent, - ThriftPipesModule, - ], - templateUrl: './edit-domain-object-dialog.component.html', -}) -export class EditDomainObjectDialogComponent extends DialogSuperclass< - EditDomainObjectDialogComponent, - { domainObject: DomainObject } -> { - private domainStoreService = inject(DomainStoreService); - private destroyRef = inject(DestroyRef); - private log = inject(NotifyLogService); - private domainNavigateService = inject(DomainNavigateService); - private metadataService = inject(MetadataService); - private domainSecretService = inject(DomainSecretService); - static override defaultDialogConfig: ValuesType = { - ...DEFAULT_DIALOG_CONFIG.large, - minHeight: DEFAULT_DIALOG_CONFIG_FULL_HEIGHT, - }; - - control = new FormControl['data']>( - getUnionValue(this.dialogData.domainObject).data, - [Validators.required], - ); - step: Step = Step.Edit; - stepEnum = Step; - type$ = this.metadataService - .getDomainFieldByName(getUnionKey(this.dialogData.domainObject)) - .pipe( - map((f) => String(f.type)), - first(), - ); - dataType$ = this.metadataService - .getDomainObjectDataFieldByName(getUnionKey(this.dialogData.domainObject)) - .pipe( - map((f) => String(f.type)), - first(), - ); - currentObject$ = this.getCurrentObject().pipe(shareReplay({ refCount: true, bufferSize: 1 })); - newObject$ = getValueChanges(this.control).pipe( - map(() => this.getNewObject()), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - hasConflict$ = this.currentObject$.pipe( - map((currentObject) => !isEqualThrift(currentObject, this.dialogData.domainObject)), - distinctUntilChanged(), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - hasChanges$ = combineLatest([this.currentObject$, this.newObject$]).pipe( - map(([a, b]) => !isEqualThrift(a, b)), - distinctUntilChanged(), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - isLoading$ = inProgressFrom([this.domainStoreService.isLoading$, () => this.progress$]); - - private progress$ = new BehaviorSubject(0); - kind = createStorageValue>('edit-domain-object-dialog-kind', { - deserialize: (v) => (enumHasValue(EditorKind, v) ? v : EditorKind.Form), - }); - - update(isRepeat = false) { - combineLatest([this.getCurrentObject(), this.getCurrentObject(true)]) - .pipe( - first(), - switchMap(([currentObject, currentObjectRaw]) => { - if (isRepeat && !isEqualThrift(currentObject, this.dialogData.domainObject)) { - this.log.error( - new Error('The object has changed'), - 'The original object has been modified. View changes in the original object before committing your own.', - ); - this.step = Step.Edit; - return EMPTY; - } - return this.domainStoreService.commit({ - ops: [ - { - update: { - old_object: currentObjectRaw, - new_object: this.domainSecretService.restoreDomain( - currentObjectRaw, - this.getNewObject(), - ), - }, - }, - ], - }); - }), - catchError((err) => { - if (err?.name === 'ObsoleteCommitVersion') { - if (!isRepeat) { - this.domainStoreService.forceReload(); - this.update(true); - this.log.error( - err, - `Domain config is out of date, one more attempt...`, - ); - } else { - this.log.error(err, `Domain config is out of date, please try again`); - } - return EMPTY; - } - throw err; - }), - switchMap(() => this.type$), - progressTo(this.progress$), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe({ - next: (type) => { - this.log.successOperation('update', 'domain object'); - void this.domainNavigateService.toType(type); - this.closeWithSuccess(); - }, - error: (err) => { - this.log.errorOperation(err, 'update', 'domain object'); - }, - }); - } - - private getCurrentObject(raw = false): Observable { - return this.domainStoreService.getObject( - { - [getUnionKey(this.dialogData.domainObject)]: getUnionValue( - this.dialogData.domainObject, - ).ref, - }, - raw, - ); - } - - private getNewObject(): DomainObject { - return { - [getUnionKey(this.dialogData.domainObject)]: { - ref: getUnionValue(this.dialogData.domainObject).ref, - data: this.control.value, - }, - }; - } -} diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/edit-domain-object-dialog/index.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/edit-domain-object-dialog/index.ts deleted file mode 100644 index 7a67e9424..000000000 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/edit-domain-object-dialog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './edit-domain-object-dialog.component'; diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/index.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/index.ts index 637236d65..4ea952d06 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/index.ts +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/index.ts @@ -1,9 +1,5 @@ export * from './domain-thrift-viewer'; export * from './domain-object-field'; export * from './domain-thrift-form-dialog'; -export * from './domain-object-card/domain-object-card.component'; -export * from './domain-thrift-form'; +export * from './domain-thrift-editor'; export * from './utils'; -export * from './services'; -export * from './create-domain-object-dialog'; -export * from './edit-domain-object-dialog'; diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/services/delete-domain-object.service.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/services/delete-domain-object.service.ts deleted file mode 100644 index 30504ce97..000000000 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/services/delete-domain-object.service.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { Reference } from '@vality/domain-proto/domain'; -import { - ConfirmDialogComponent, - DialogResponseStatus, - DialogService, - NotifyLogService, -} from '@vality/matez'; -import { filter, first, switchMap } from 'rxjs/operators'; - -import { DomainStoreService } from '../../../../../api/domain-config'; - -@Injectable({ - providedIn: 'root', -}) -export class DeleteDomainObjectService { - private dialogService = inject(DialogService); - private domainStoreService = inject(DomainStoreService); - private log = inject(NotifyLogService); - - delete(domainRef: Reference) { - return this.dialogService - .open(ConfirmDialogComponent, { title: 'Delete object' }) - .afterClosed() - .pipe( - filter((r) => r.status === DialogResponseStatus.Success), - switchMap(() => this.domainStoreService.getObject(domainRef, true).pipe(first())), - switchMap((obj) => - this.domainStoreService.commit({ ops: [{ remove: { object: obj } }] }), - ), - ) - .subscribe({ - next: () => { - this.log.successOperation('delete', 'domain object'); - }, - error: this.log.error, - }); - } -} diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/services/index.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/services/index.ts deleted file mode 100644 index bfbb8305e..000000000 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './delete-domain-object.service'; diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/utils/get-domain-object-details.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/utils/get-domain-object-details.ts index 14b2435bc..da4e17847 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/utils/get-domain-object-details.ts +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/utils/get-domain-object-details.ts @@ -1,10 +1,13 @@ import { DomainObject } from '@vality/domain-proto/domain'; +import { LimitedVersionedObject } from '@vality/domain-proto/domain_config_v2'; import { inlineJson } from '@vality/matez'; import { getUnionKey, getUnionValue } from '@vality/ng-thrift'; import isNil from 'lodash-es/isNil'; import startCase from 'lodash-es/startCase'; import { PickByValue, ValuesType } from 'utility-types'; +import { getReferenceId } from './get-reference-id'; + export interface DomainObjectDetails { id: number | string; label: string; @@ -124,3 +127,12 @@ export function getDomainObjectDetails(o: DomainObject): DomainObjectDetails { type: startCase(getUnionKey(o)), }; } + +export function getLimitedDomainObjectDetails(o: LimitedVersionedObject): DomainObjectDetails { + const id = getReferenceId(o.ref); + const type = startCase(getUnionKey(o.ref)); + const label = o.name || `#${id}`; + const description = o.description; + + return { id, label, description, type }; +} diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/utils/get-reference-id.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/utils/get-reference-id.ts index 460ea005e..440eef727 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain/utils/get-reference-id.ts +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain/utils/get-reference-id.ts @@ -4,7 +4,7 @@ import { getUnionKey, getUnionValue } from '@vality/ng-thrift'; import startCase from 'lodash-es/startCase'; import { PickByValue, ValuesType } from 'utility-types'; -type ReferenceId = number | string | null; +export type ReferenceId = number | string | null; type IdReferences = PickByValue; type NoIdReferences = Omit; diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/create-domain-object-dialog/create-domain-object-dialog.component.html b/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/create-domain-object-dialog/create-domain-object-dialog.component.html index d58618348..c505987a4 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/create-domain-object-dialog/create-domain-object-dialog.component.html +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/create-domain-object-dialog/create-domain-object-dialog.component.html @@ -6,13 +6,15 @@ } @else { - + namespace="domain_config_v2" + type="InsertOp" + > } diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/create-domain-object-dialog/create-domain-object-dialog.component.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/create-domain-object-dialog/create-domain-object-dialog.component.ts index 5f6b09f7d..33813b85c 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/create-domain-object-dialog/create-domain-object-dialog.component.ts +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/create-domain-object-dialog/create-domain-object-dialog.component.ts @@ -3,7 +3,9 @@ import { Component, DestroyRef, OnInit, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; +import { MatDividerModule } from '@angular/material/divider'; import { ReflessDomainObject } from '@vality/domain-proto/domain'; +import { InsertOp } from '@vality/domain-proto/domain_config_v2'; import { DEFAULT_DIALOG_CONFIG, DEFAULT_DIALOG_CONFIG_FULL_HEIGHT, @@ -20,7 +22,7 @@ import { ValuesType } from 'utility-types'; import { DomainService } from '../../../../../api/domain-config'; import { APP_ROUTES } from '../../../../../app-routes'; import { NavigateService } from '../../../../services'; -import { DomainThriftFormComponent } from '../../domain/domain-thrift-form'; +import { DomainThriftFormComponent } from '../../domain/domain-thrift-editor'; import { DomainThriftViewerComponent } from '../../domain/domain-thrift-viewer'; @Component({ @@ -32,6 +34,7 @@ import { DomainThriftViewerComponent } from '../../domain/domain-thrift-viewer'; DomainThriftFormComponent, ReactiveFormsModule, DomainThriftViewerComponent, + MatDividerModule, ], templateUrl: './create-domain-object-dialog.component.html', }) @@ -51,23 +54,23 @@ export class CreateDomainObjectDialogComponent minHeight: DEFAULT_DIALOG_CONFIG_FULL_HEIGHT, }; - control = new FormControl(null, [Validators.required]); + control = new FormControl(null, [Validators.required]); progress$ = new BehaviorSubject(0); isReview = false; ngOnInit() { if (this.dialogData && this.dialogData.objectType) { - this.control.setValue({ [this.dialogData.objectType]: {} }); + this.control.setValue({ object: { [this.dialogData.objectType]: {} } }); } } create() { this.domainService - .insert([this.control.value]) + .commit([{ insert: this.control.value }]) .pipe(progressTo(this.progress$), takeUntilDestroyed(this.destroyRef)) .subscribe(() => { this.log.successOperation('create', 'domain object'); - void this.navigateService.navigate(APP_ROUTES.domain2.root, { + void this.navigateService.navigate(APP_ROUTES.domain.root, { type: getUnionKey(this.control.value), }); this.closeWithSuccess(); diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/domain-object-card/domain-object-card.component.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/domain-object-card/domain-object-card.component.ts index 9eeb5e0bb..9649f493f 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/domain-object-card/domain-object-card.component.ts +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/domain-object-card/domain-object-card.component.ts @@ -1,7 +1,7 @@ import { Component, computed, inject, input } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { MatButtonModule } from '@angular/material/button'; -import { Reference } from '@vality/domain-proto/internal/domain'; +import { Reference } from '@vality/domain-proto/domain'; import { NotifyLogService, UnionEnum, createStorageValue, enumHasValue } from '@vality/matez'; import { ThriftPipesModule, ViewerKind } from '@vality/ng-thrift'; import { catchError } from 'rxjs'; @@ -28,6 +28,7 @@ export class DomainObjectCardComponent { private domainObjectService = inject(DomainObjectService); private domainService = inject(DomainService); private log = inject(NotifyLogService); + ref = input(); version = input(); diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/edit-domain-object-dialog/edit-domain-object-dialog.component.html b/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/edit-domain-object-dialog/edit-domain-object-dialog.component.html index 406baa03d..2205d7732 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/edit-domain-object-dialog/edit-domain-object-dialog.component.html +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/edit-domain-object-dialog/edit-domain-object-dialog.component.html @@ -1,13 +1,13 @@ @switch (step) { @case (stepEnum.Edit) { - + > @@ -18,7 +18,7 @@ = { ...DEFAULT_DIALOG_CONFIG.large, minHeight: DEFAULT_DIALOG_CONFIG_FULL_HEIGHT, }; control = new FormControl['data']>( - getUnionValue(this.dialogData.domainObject.object).data, + getUnionValue(this.sourceObject).data, [Validators.required], ); step: Step = Step.Edit; stepEnum = Step; - get srcObject(): DomainObject { + get sourceObject(): DomainObject { return this.dialogData.domainObject.object; } + get ref() { + return getDomainObjectReference(this.sourceObject); + } get type() { - return getUnionKey(this.dialogData.domainObject.object); + return getUnionKey(this.sourceObject); } - dataType$ = this.metadataService.getDomainObjectDataFieldByName(this.type).pipe( - map((f) => String(f.type)), + dataType$ = metadata$.pipe( + map((metadata) => + getThriftObjectFieldType( + metadata, + 'domain', + getThriftObjectFieldType(metadata, 'domain', 'DomainObject', this.type), + 'data', + ), + ), shareReplay({ refCount: true, bufferSize: 1 }), ); - currentObject = signal(this.dialogData.domainObject.object); + currentObject = signal(this.dialogData.domainObject); newObject$ = getValueChanges(this.control).pipe( map(() => this.getNewObject()), shareReplay({ refCount: true, bufferSize: 1 }), ); hasConflict = computed(() => { - return !isEqualThrift(this.currentObject(), this.dialogData.domainObject.object); + return !isEqualThrift(this.currentObject().object, this.sourceObject); }); - hasChanges$ = combineLatest([toObservable(this.currentObject), this.newObject$]).pipe( + hasChanges$ = combineLatest([ + toObservable(this.currentObject).pipe(map((o) => o.object)), + this.newObject$, + ]).pipe( map(([a, b]) => !isEqualThrift(a, b)), distinctUntilChanged(), shareReplay({ refCount: true, bufferSize: 1 }), @@ -108,18 +126,23 @@ export class EditDomainObjectDialogComponent extends DialogSuperclass< update() { this.domainService - .update([this.getNewObject()], this.dialogData.domainObject.info.version) + .commit( + [{ update: { object: this.getNewObject() } }], + this.currentObject().info.version, + ) .pipe(progressTo(this.isLoading), takeUntilDestroyed(this.dr)) .subscribe({ next: () => { this.log.successOperation('update', 'domain object'); - void this.navigateService.navigate(APP_ROUTES.domain2.root, { + void this.navigateService.navigate(APP_ROUTES.domain.root, { type: this.type, }); this.closeWithSuccess(); }, error: (err) => { - this.log.errorOperation(err, 'update', 'domain object'); + if (err instanceof DomainServiceObsoleteCommitVersionError) { + this.currentObject.set(err.newObject); + } }, }); } @@ -127,7 +150,7 @@ export class EditDomainObjectDialogComponent extends DialogSuperclass< private getNewObject(): DomainObject { return { [this.type]: { - ref: getUnionValue(this.dialogData.domainObject.object).ref, + ref: getUnionValue(this.sourceObject).ref, data: this.control.value, }, }; diff --git a/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/services/domain-object.service.ts b/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/services/domain-object.service.ts index 48b07a9cb..eb5f7dc74 100644 --- a/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/services/domain-object.service.ts +++ b/apps/control-center/src/app/shared/components/thrift-api-crud/domain2/services/domain-object.service.ts @@ -30,7 +30,7 @@ export class DomainObjectService { .afterClosed() .pipe( filter((r) => r.status === DialogResponseStatus.Success), - switchMap(() => this.domainService.remove([ref])), + switchMap(() => this.domainService.commit([{ remove: { ref } }])), takeUntilDestroyed(this.dr), ), { diff --git a/apps/control-center/src/app/shared/components/wallet-field/wallet-field.component.html b/apps/control-center/src/app/shared/components/wallet-field/wallet-field.component.html index 1bf0eb93e..e491f6fb8 100644 --- a/apps/control-center/src/app/shared/components/wallet-field/wallet-field.component.html +++ b/apps/control-center/src/app/shared/components/wallet-field/wallet-field.component.html @@ -9,5 +9,5 @@ [required]="required" [size]="size" externalSearch - (searchChange)="this.searchChange$.next($event)" + (searchChange)="search($event)" > diff --git a/apps/control-center/src/app/shared/components/wallet-field/wallet-field.component.ts b/apps/control-center/src/app/shared/components/wallet-field/wallet-field.component.ts index e90aa693f..0900dc16f 100644 --- a/apps/control-center/src/app/shared/components/wallet-field/wallet-field.component.ts +++ b/apps/control-center/src/app/shared/components/wallet-field/wallet-field.component.ts @@ -1,41 +1,25 @@ -import { - AfterViewInit, - Component, - DestroyRef, - Input, - booleanAttribute, - inject, -} from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { Deanonimus } from '@vality/deanonimus-proto/deanonimus'; -import { WalletID } from '@vality/domain-proto/domain'; +import { Component, Input, booleanAttribute, inject } from '@angular/core'; +import { DomainObjectType, WalletID } from '@vality/domain-proto/domain'; import { FormControlSuperclass, - NotifyLogService, Option, SelectFieldComponent, createControlProviders, - debounceTimeWithFirst, - progressTo, } from '@vality/matez'; -import { BehaviorSubject, Observable, ReplaySubject, Subject, concat, forkJoin, of } from 'rxjs'; -import { catchError, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; -import { DEBOUNCE_TIME_MS } from '../../../tokens'; +import { FetchDomainObjectsService } from '../../../api/domain-config'; @Component({ selector: 'cc-wallet-field', templateUrl: 'wallet-field.component.html', - providers: createControlProviders(() => WalletFieldComponent), + providers: [...createControlProviders(() => WalletFieldComponent), FetchDomainObjectsService], standalone: false, }) -export class WalletFieldComponent - extends FormControlSuperclass - implements AfterViewInit -{ - private deanonimusService = inject(Deanonimus); - private log = inject(NotifyLogService); - private destroyRef = inject(DestroyRef); +export class WalletFieldComponent extends FormControlSuperclass { + private fetchDomainObjectsService = inject(FetchDomainObjectsService); + @Input() label: string; @Input({ transform: booleanAttribute }) required: boolean; @Input() size?: SelectFieldComponent['size']; @@ -43,60 +27,21 @@ export class WalletFieldComponent @Input() hint?: string; @Input({ transform: booleanAttribute }) multiple = false; - options$ = new ReplaySubject[]>(1); - searchChange$ = new Subject(); - progress$ = new BehaviorSubject(0); - - private debounceTimeMs = inject(DEBOUNCE_TIME_MS); - - ngAfterViewInit() { - concat( - of(this.control.value).pipe( - switchMap((term) => - forkJoin( - (Array.isArray(term) ? term : [term ?? '']).map((t) => this.findOption(t)), - ), - ), - map((o) => o.filter(Boolean)), - ), - this.searchChange$.pipe( - distinctUntilChanged(), - tap(() => { - this.options$.next([]); - }), - debounceTimeWithFirst(this.debounceTimeMs), - switchMap((term) => this.searchOptions(term)), - ), - ) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((options) => { - this.options$.next(options); - }); - } - - private searchOptions(str: string): Observable[]> { - if (!str) { - return of([]); - } - return this.deanonimusService.searchWalletText(str).pipe( - map((wallets) => - wallets.map((p) => ({ - label: p.wallet.name, - value: p.wallet.id, - description: p.wallet.id, - })), - ), - catchError((err) => { - this.log.error(err, 'Search error'); - return of([]); - }), - progressTo(this.progress$), - ); - } + options$: Observable[]> = this.fetchDomainObjectsService.result$.pipe( + map((objs) => + objs.map((obj) => ({ + value: obj.ref.wallet_config.id, + label: obj.name || `#${obj.ref.wallet_config.id}`, + description: obj.description, + })), + ), + ); + progress$ = this.fetchDomainObjectsService.isLoading$; - private findOption(id: WalletID) { - return this.searchOptions(id).pipe( - map((options) => (options?.length ? options.find((o) => o.value === id) : null)), + search(search: string) { + this.fetchDomainObjectsService.load( + { type: DomainObjectType.wallet_config, query: search }, + { size: 1000 }, ); } } diff --git a/apps/control-center/src/app/shared/components/wallet-info/services/receive-wallet/receive-wallet.service.ts b/apps/control-center/src/app/shared/components/wallet-info/services/receive-wallet/receive-wallet.service.ts index d33f4726b..e91524237 100644 --- a/apps/control-center/src/app/shared/components/wallet-info/services/receive-wallet/receive-wallet.service.ts +++ b/apps/control-center/src/app/shared/components/wallet-info/services/receive-wallet/receive-wallet.service.ts @@ -1,13 +1,12 @@ import { DestroyRef, Injectable, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { Management } from '@vality/fistful-proto/withdrawal'; import { BehaviorSubject, NEVER, ReplaySubject, Subject } from 'rxjs'; import { catchError, shareReplay, switchMap, tap } from 'rxjs/operators'; -import { ManagementService } from '../../../../../api/wallet/management.service'; - @Injectable() export class ReceiveWalletService { - private walletManagementService = inject(ManagementService); + private walletManagementService = inject(Management); private destroyRef = inject(DestroyRef); private receiveWallet$ = new ReplaySubject(); private error$ = new Subject(); diff --git a/apps/control-center/src/app/shared/services/amount-currency.service.ts b/apps/control-center/src/app/shared/services/amount-currency.service.ts index 1c9f26e8e..06c99a597 100644 --- a/apps/control-center/src/app/shared/services/amount-currency.service.ts +++ b/apps/control-center/src/app/shared/services/amount-currency.service.ts @@ -1,5 +1,4 @@ import { Injectable, inject } from '@angular/core'; -import { toObservable } from '@angular/core/rxjs-interop'; import { toMajorByExponent, toMinorByExponent } from '@vality/matez'; import { first, map, shareReplay } from 'rxjs/operators'; @@ -10,7 +9,7 @@ import { CurrenciesStoreService } from '../../api/domain-config'; }) export class AmountCurrencyService { private currenciesStoreService = inject(CurrenciesStoreService); - currencies$ = toObservable(this.currenciesStoreService.currencies).pipe( + currencies$ = this.currenciesStoreService.currencies$.pipe( map((currencies) => new Map(currencies.map((c) => [c.symbolic_code, c]))), shareReplay(1), ); diff --git a/apps/control-center/src/app/shared/services/app-auth-guard/app-auth-guard.service.ts b/apps/control-center/src/app/shared/services/app-auth-guard/app-auth-guard.service.ts deleted file mode 100644 index 8f517e74f..000000000 --- a/apps/control-center/src/app/shared/services/app-auth-guard/app-auth-guard.service.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; -import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular'; - -import { environment } from '../../../../environments/environment'; - -@Injectable({ providedIn: 'root' }) -export class AppAuthGuardService extends KeycloakAuthGuard { - constructor( - // eslint-disable-next-line @angular-eslint/prefer-inject - protected override router: Router, - // eslint-disable-next-line @angular-eslint/prefer-inject - protected override keycloakAngular: KeycloakService, - ) { - super(router, keycloakAngular); - } - - async isAccessAllowed( - route: ActivatedRouteSnapshot, - _state: RouterStateSnapshot, - ): Promise { - return ( - this.userHasSomeServiceMethods(route.data['services']) || - this.router.createUrlTree(['404']) - ); - } - - userHasSomeServiceMethods(serviceMethods: string[]): boolean { - if (this.ignoreRoles() || !serviceMethods?.length) { - return true; - } - const allowedServiceMethods = this.keycloakAngular - .getUserRoles(true) - .map((r) => r.split(':')); - return serviceMethods.some((serviceMethod) => { - const [service, method] = serviceMethod.split(':'); - return allowedServiceMethods.some( - ([s, m]) => service === s && (!method || method === m), - ); - }); - } - - userHasRoles(roles: string[]): boolean { - return ( - this.ignoreRoles() || - roles.every((role) => this.keycloakAngular.getUserRoles(true).includes(role)) - ); - } - - private ignoreRoles(): boolean { - return !environment.production && environment.ignoreRoles; - } -} diff --git a/apps/control-center/src/app/shared/services/app-auth-guard/can-activate-auth-role.ts b/apps/control-center/src/app/shared/services/app-auth-guard/can-activate-auth-role.ts new file mode 100644 index 000000000..66f1a442e --- /dev/null +++ b/apps/control-center/src/app/shared/services/app-auth-guard/can-activate-auth-role.ts @@ -0,0 +1,32 @@ +import { inject } from '@angular/core'; +import { + ActivatedRouteSnapshot, + CanActivateFn, + Router, + RouterStateSnapshot, + UrlTree, +} from '@angular/router'; +import { AuthGuardData, createAuthGuard } from 'keycloak-angular'; + +import { KeycloakUserService } from './keycloak-user.service'; +import { RoutingConfig } from './types/routing-config'; + +const isAccessAllowed = async ( + route: ActivatedRouteSnapshot, + _: RouterStateSnapshot, + authData: AuthGuardData, +): Promise => { + const keycloakUserService = inject(KeycloakUserService); + const { authenticated } = authData; + const data = route.data as RoutingConfig; + const services = data.services || []; + + if (authenticated && keycloakUserService.hasRole(...services)) { + return true; + } + + const router = inject(Router); + return router.parseUrl('/404'); +}; + +export const canActivateAuthRole = createAuthGuard(isAccessAllowed); diff --git a/apps/control-center/src/app/shared/services/app-auth-guard/index.ts b/apps/control-center/src/app/shared/services/app-auth-guard/index.ts index c6a18c4d3..f1161f4a4 100644 --- a/apps/control-center/src/app/shared/services/app-auth-guard/index.ts +++ b/apps/control-center/src/app/shared/services/app-auth-guard/index.ts @@ -1,4 +1,4 @@ -export * from './app-auth-guard.service'; -export * from './roles'; +export * from './can-activate-auth-role'; export * from './services'; export * from './types/routing-config'; +export * from './keycloak-user.service'; diff --git a/apps/control-center/src/app/shared/services/app-auth-guard/keycloak-user.service.ts b/apps/control-center/src/app/shared/services/app-auth-guard/keycloak-user.service.ts new file mode 100644 index 000000000..c188ac949 --- /dev/null +++ b/apps/control-center/src/app/shared/services/app-auth-guard/keycloak-user.service.ts @@ -0,0 +1,61 @@ +import { Injectable, computed, inject, isDevMode } from '@angular/core'; +import { observableResource } from '@vality/matez'; +import Keycloak, { KeycloakProfile } from 'keycloak-js'; +import { from, map } from 'rxjs'; + +export type KeycloakUserProfile = KeycloakProfile & + Required>; + +@Injectable({ + providedIn: 'root', +}) +export class KeycloakUserService { + private keycloak = inject(Keycloak); + + get roles() { + return [ + ...(this.keycloak.realmAccess?.roles || []), + ...Object.values(this.keycloak.resourceAccess || {}) + .map((resourceAccess) => resourceAccess.roles) + .flat(), + ]; + } + + user = observableResource({ + loader: () => + from(this.keycloak.loadUserProfile()).pipe( + map((profile) => { + if (!profile.id || !profile.email) { + throw new Error('User profile does not contain id or email'); + } + return { + ...profile, + username: profile.username ?? profile.email, + } as KeycloakUserProfile; + }), + ), + }); + + username = computed(() => (this.user.value()?.username || '').split('@')[0]); + + logout() { + return this.keycloak.logout(); + } + + hasRole(...roles: string[]): boolean { + return isDevMode() || roles.every((r) => this.roles.includes(r)); + } + + hasServiceRole(...serviceMethods: string[]): boolean { + if (isDevMode() || !serviceMethods?.length) { + return true; + } + const allowedServiceMethods = this.roles.map((r) => r.split(':')); + return serviceMethods.some((serviceMethod) => { + const [service, method] = serviceMethod.split(':'); + return allowedServiceMethods.some( + ([s, m]) => service === s && (!method || method === m), + ); + }); + } +} diff --git a/apps/control-center/src/app/shared/services/app-auth-guard/roles.ts b/apps/control-center/src/app/shared/services/app-auth-guard/roles.ts deleted file mode 100644 index 601c261ff..000000000 --- a/apps/control-center/src/app/shared/services/app-auth-guard/roles.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum ClaimManagementRole { - RequestClaimReview = 'request_claim_review', - RequestClaimChanges = 'request_claim_changes', - DenyClaim = 'deny_claim', - RevokeClaim = 'revoke_claim', - AcceptClaim = 'accept_claim', -} diff --git a/apps/control-center/src/app/shared/services/app-auth-guard/services.ts b/apps/control-center/src/app/shared/services/app-auth-guard/services.ts index 58afd8908..cc2c8a9bd 100644 --- a/apps/control-center/src/app/shared/services/app-auth-guard/services.ts +++ b/apps/control-center/src/app/shared/services/app-auth-guard/services.ts @@ -1,9 +1,7 @@ export enum Services { - Domain = 'Domain', - PartyManagement = 'PartyManagement', + DMT = 'DMT', FistfulStatistics = 'FistfulStatistics', MerchantStatistics = 'MerchantStatistics', - Deanonimus = 'Deanonimus', ClaimManagement = 'ClaimManagement', Invoicing = 'Invoicing', RepairManagement = 'RepairManagement', diff --git a/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/domain-metadata-form-extensions.service.ts b/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/domain-metadata-form-extensions.service.ts index 92db4af54..c2a4b76db 100644 --- a/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/domain-metadata-form-extensions.service.ts +++ b/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/domain-metadata-form-extensions.service.ts @@ -1,29 +1,26 @@ import { Injectable, inject } from '@angular/core'; -import { ThriftAstMetadata } from '@vality/domain-proto'; -import { Claim } from '@vality/domain-proto/claim_management'; +import { ThriftAstMetadata, metadata$ } from '@vality/domain-proto'; import { DomainObject, Party } from '@vality/domain-proto/domain'; -import { getImportValue, getNoTimeZoneIsoString } from '@vality/matez'; +import { getNoTimeZoneIsoString } from '@vality/matez'; import { ThriftData, ThriftFormExtension, isTypeWithAliases } from '@vality/ng-thrift'; import { Observable, of } from 'rxjs'; import { map, shareReplay } from 'rxjs/operators'; import short from 'short-uuid'; -import { DomainStoreService } from '../../../api/domain-config/stores/domain-store.service'; -import { FistfulStatisticsService, createDsl } from '../../../api/fistful-stat'; +import { DomainObjectsStoreService, DomainService } from '../../../api/domain-config'; import { createDomainObjectExtension } from './utils/create-domain-object-extension'; -import { createPartyClaimDomainMetadataFormExtensions } from './utils/create-party-claim-domain-metadata-form-extensions'; -import { getDomainObjectValueOptionFn } from './utils/get-domain-object-option'; +import { createPartyDomainMetadataFormExtensions } from './utils/create-party-claim-domain-metadata-form-extensions'; +import { getDomainObjectOption } from './utils/get-domain-object-option'; @Injectable({ providedIn: 'root', }) export class DomainMetadataFormExtensionsService { - private domainStoreService = inject(DomainStoreService); - private fistfulStatisticsService = inject(FistfulStatisticsService); - extensions$: Observable = getImportValue( - import('@vality/domain-proto/metadata.json'), - ).pipe( + private domainStoreService = inject(DomainObjectsStoreService); + private domainService = inject(DomainService); + + extensions$: Observable = metadata$.pipe( map((metadata): ThriftFormExtension[] => [ ...this.createDomainObjectsOptions(metadata), { @@ -31,12 +28,13 @@ export class DomainMetadataFormExtensionsService { extension: () => of({ generate: () => of(short().generate()), isIdentifier: true }), }, { - determinant: (data) => of(isTypeWithAliases(data, 'WalletID', 'claim_management')), - extension: () => - of({ - generate: () => this.generateNextWalletId(), - isIdentifier: true, - }), + determinant: (data) => + of( + isTypeWithAliases(data, 'PartyID', 'domain') || + isTypeWithAliases(data, 'WalletConfigID', 'domain') || + isTypeWithAliases(data, 'ShopConfigID', 'domain'), + ), + extension: () => of({ generate: () => of(short().uuid()), isIdentifier: true }), }, { determinant: (data) => of(isTypeWithAliases(data, 'Timestamp', 'base')), @@ -79,35 +77,16 @@ export class DomainMetadataFormExtensionsService { { determinant: (data) => of(isTypeWithAliases(data, 'DataRevision', 'domain')), extension: () => - this.domainStoreService.version$.pipe( - map(() => ({ - generate: () => this.domainStoreService.version$, - })), - ), + of({ + generate: () => of(this.domainService.version.value()), + }), }, ]), shareReplay({ refCount: true, bufferSize: 1 }), ); - createPartyClaimExtensions(party: Party, claim: Claim) { - return createPartyClaimDomainMetadataFormExtensions(party, claim); - } - - generateNextWalletId() { - return this.fistfulStatisticsService - .GetWallets({ - dsl: createDsl({ wallets: {} }), - }) - .pipe( - map((res) => - String( - Math.max( - 1, - ...res.data.wallets.map((w) => Number(w.id)).filter((id) => !isNaN(id)), - ) + 1, - ), - ), - ); + createPartyExtensions(party: Party) { + return createPartyDomainMetadataFormExtensions(party); } private createDomainObjectsOptions(metadata: ThriftAstMetadata[]): ThriftFormExtension[] { @@ -126,12 +105,9 @@ export class DomainMetadataFormExtensionsService { const objectFields = new ThriftData(metadata, 'domain', objectType).ast; const refType = objectFields.find((n) => n.name === 'ref').type as string; return createDomainObjectExtension(refType, () => - this.domainStoreService.getObjects(objectKey).pipe( - map((objects) => { - const domainObjectToOption = getDomainObjectValueOptionFn(objectKey); - return objects.map(domainObjectToOption); - }), - ), + this.domainStoreService + .getLimitedObjects(objectKey) + .value$.pipe(map((objects) => objects.map((obj) => getDomainObjectOption(obj)))), ); } } diff --git a/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/utils/create-party-claim-domain-metadata-form-extensions.ts b/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/utils/create-party-claim-domain-metadata-form-extensions.ts index 0dfaa7746..c19b7a0d7 100644 --- a/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/utils/create-party-claim-domain-metadata-form-extensions.ts +++ b/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/utils/create-party-claim-domain-metadata-form-extensions.ts @@ -1,11 +1,9 @@ -import { Claim, PayoutToolModificationUnit } from '@vality/domain-proto/claim_management'; import { Party } from '@vality/domain-proto/domain'; import { ThriftFormExtension, ThriftFormExtensionOption, isTypeWithAliases, } from '@vality/ng-thrift'; -import uniqBy from 'lodash-es/uniqBy'; import { of } from 'rxjs'; import short from 'short-uuid'; @@ -17,72 +15,17 @@ function createPartyOptions(values: IterableIterator<{ id: string }>): ThriftFor })); } -function createClaimOptions( - modificationUnits: { id: string; modification: unknown }[], -): ThriftFormExtensionOption[] { - return uniqBy( - modificationUnits.filter(Boolean).map((unit) => ({ - label: 'From claim', - details: unit.modification as object, - value: unit.id, - color: 'primary', - })), - 'value', - ); -} - -function createClaimPayoutToolOptions( - modificationUnits: PayoutToolModificationUnit[], -): ThriftFormExtensionOption[] { - return uniqBy( - modificationUnits.map((unit) => ({ - label: 'From claim', - details: unit.modification, - value: unit.payout_tool_id, - color: 'primary', - })), - 'value', - ); -} - -function mergeClaimAndPartyOptions( - claimOptions: ThriftFormExtensionOption[], - partyOptions: ThriftFormExtensionOption[], -) { - return partyOptions.reduce((acc, partyOpt) => { - const claimPartyOpt = acc.find((o) => o.value === partyOpt.value); - if (claimPartyOpt) { - claimPartyOpt.label = 'From claim and party'; - claimPartyOpt.details = { claim: claimPartyOpt.details, party: partyOpt.details }; - } else { - acc.push(partyOpt); - } - return acc; - }, claimOptions); -} - function generate() { return of(short().uuid()); } -export function createPartyClaimDomainMetadataFormExtensions( - party: Party, - claim: Claim, -): ThriftFormExtension[] { +export function createPartyDomainMetadataFormExtensions(party: Party): ThriftFormExtension[] { return [ { determinant: (data) => of(isTypeWithAliases(data, 'ContractorID', 'domain')), extension: () => of({ - options: mergeClaimAndPartyOptions( - createClaimOptions( - claim.changeset.map( - (unit) => - unit.modification.party_modification?.contractor_modification, - ), - ), - createPartyOptions(party.contractors.values()), - ), + options: createPartyOptions(party.contractors.values()), generate, isIdentifier: true, }), @@ -91,15 +34,7 @@ export function createPartyClaimDomainMetadataFormExtensions( determinant: (data) => of(isTypeWithAliases(data, 'ContractID', 'domain')), extension: () => of({ - options: mergeClaimAndPartyOptions( - createClaimOptions( - claim.changeset.map( - (unit) => - unit.modification.party_modification?.contract_modification, - ), - ), - createPartyOptions(party.contracts.values()), - ), + options: createPartyOptions(party.contracts.values()), generate, isIdentifier: true, }), @@ -108,14 +43,7 @@ export function createPartyClaimDomainMetadataFormExtensions( determinant: (data) => of(isTypeWithAliases(data, 'ShopID', 'domain')), extension: () => of({ - options: mergeClaimAndPartyOptions( - createClaimOptions( - claim.changeset.map( - (unit) => unit.modification.party_modification?.shop_modification, - ), - ), - createPartyOptions(party.shops.values()), - ), + options: createPartyOptions(party.shops.values()), generate, isIdentifier: true, }), @@ -124,34 +52,10 @@ export function createPartyClaimDomainMetadataFormExtensions( determinant: (data) => of(isTypeWithAliases(data, 'WalletID', 'domain')), extension: () => of({ - options: mergeClaimAndPartyOptions( - createClaimOptions( - claim.changeset.map( - (unit) => unit.modification.party_modification?.wallet_modification, - ), - ), - createPartyOptions(party.wallets.values()), - ), + options: createPartyOptions(party.wallets.values()), generate, isIdentifier: true, }), }, - { - determinant: (data) => of(isTypeWithAliases(data, 'PayoutToolID', 'domain')), - extension: () => - of({ - options: createClaimPayoutToolOptions( - claim.changeset - .map( - (unit) => - unit.modification.party_modification?.contract_modification - ?.modification?.payout_tool_modification, - ) - .filter(Boolean), - ), - generate: () => of(short().generate()), - isIdentifier: true, - }), - }, ]; } diff --git a/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/utils/get-domain-object-option.ts b/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/utils/get-domain-object-option.ts index c1333edf4..b4ca1ffb1 100644 --- a/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/utils/get-domain-object-option.ts +++ b/apps/control-center/src/app/shared/services/domain-metadata-form-extensions/utils/get-domain-object-option.ts @@ -1,26 +1,12 @@ -import { DomainObject } from '@vality/domain-proto/domain'; -import { ThriftFormExtensionOption, getUnionKey, getUnionValue } from '@vality/ng-thrift'; -import { ValuesType } from 'utility-types'; +import { LimitedVersionedObject } from '@vality/domain-proto/domain_config_v2'; +import { ThriftFormExtensionOption } from '@vality/ng-thrift'; -import { getDomainObjectValueDetailsFn } from '../../../components/thrift-api-crud'; +import { getReferenceId } from '../../../components/thrift-api-crud'; -export function getDomainObjectValueOptionFn( - key: keyof DomainObject, -): (o: ValuesType) => ThriftFormExtensionOption { - const getDomainObjectDetails = getDomainObjectValueDetailsFn(key); - return (o) => { - const details = getDomainObjectDetails(o); - return { - value: details.id, - label: - String(details.id) === details.label - ? (details.description ?? details.label) - : details.label, - details: o, - }; +export function getDomainObjectOption(o: LimitedVersionedObject): ThriftFormExtensionOption { + return { + value: getReferenceId(o.ref), + label: o.name, + details: o, }; } - -export function getDomainObjectOption(o: DomainObject): ThriftFormExtensionOption { - return getDomainObjectValueOptionFn(getUnionKey(o))(getUnionValue(o)); -} diff --git a/apps/control-center/src/app/shared/services/fetch-parties.service.ts b/apps/control-center/src/app/shared/services/fetch-parties.service.ts deleted file mode 100644 index 37315f5e8..000000000 --- a/apps/control-center/src/app/shared/services/fetch-parties.service.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { Deanonimus, Party } from '@vality/deanonimus-proto/deanonimus'; -import { FetchOptions, NotifyLogService, SingleFetchSuperclass, handleError } from '@vality/matez'; -import { of } from 'rxjs'; -import { map } from 'rxjs/operators'; -@Injectable() -export class FetchPartiesService extends SingleFetchSuperclass { - private deanonimusService = inject(Deanonimus); - private log = inject(NotifyLogService); - - protected fetch(text: string, _options: FetchOptions) { - return text - ? this.deanonimusService.searchParty(text).pipe( - map((hits) => ({ result: hits.map((hit) => hit.party) })), - handleError(this.log.error, { result: [] }), - ) - : of({ result: [] }); - } -} diff --git a/apps/control-center/src/app/shared/services/index.ts b/apps/control-center/src/app/shared/services/index.ts index 559190d4d..77d83d677 100644 --- a/apps/control-center/src/app/shared/services/index.ts +++ b/apps/control-center/src/app/shared/services/index.ts @@ -1,8 +1,6 @@ export * from './app-auth-guard'; -export * from './fetch-parties.service'; export * from './keycloak-token-info'; export * from './user-info-based-id-generator'; export * from './domain-metadata-form-extensions'; export * from './amount-currency.service'; -export * from './keycloak-user.service'; export * from './navigate'; diff --git a/apps/control-center/src/app/shared/services/keycloak-token-info/create-wachter-headers.ts b/apps/control-center/src/app/shared/services/keycloak-token-info/create-wachter-headers.ts new file mode 100644 index 000000000..b33658bb3 --- /dev/null +++ b/apps/control-center/src/app/shared/services/keycloak-token-info/create-wachter-headers.ts @@ -0,0 +1,22 @@ +import { generateId } from '@vality/domain-proto'; + +export const createWachterHeaders = ( + service: string, + user: { token: string; id: string; username?: string; email?: string }, +) => ({ + service, + authorization: `Bearer ${user.token}`, + 'x-woody-meta-user-identity-email': user.email, + 'x-woody-meta-user-identity-id': user.id, + 'x-woody-meta-user-identity-realm': 'internal', + 'x-woody-meta-user-identity-username': user.username, + 'x-woody-parent-id': undefined, +}); + +export const createRequestWachterHeaders = () => { + const traceId = generateId(); + return { + 'x-woody-span-id': traceId, + 'x-woody-trace-id': traceId, + }; +}; diff --git a/apps/control-center/src/app/shared/services/keycloak-token-info/index.ts b/apps/control-center/src/app/shared/services/keycloak-token-info/index.ts index c96213bec..74b36a310 100644 --- a/apps/control-center/src/app/shared/services/keycloak-token-info/index.ts +++ b/apps/control-center/src/app/shared/services/keycloak-token-info/index.ts @@ -1,4 +1 @@ -export * from './keycloak-token-info.module'; -export * from './keycloak-token-info.service'; -export * from './types'; -export * from './to-wachter-headers'; +export * from './create-wachter-headers'; diff --git a/apps/control-center/src/app/shared/services/keycloak-token-info/keycloak-token-info.module.ts b/apps/control-center/src/app/shared/services/keycloak-token-info/keycloak-token-info.module.ts deleted file mode 100644 index a6b96d5bb..000000000 --- a/apps/control-center/src/app/shared/services/keycloak-token-info/keycloak-token-info.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { KeycloakTokenInfoService } from './keycloak-token-info.service'; - -@NgModule({ - providers: [KeycloakTokenInfoService], -}) -export class KeycloakTokenInfoModule {} diff --git a/apps/control-center/src/app/shared/services/keycloak-token-info/keycloak-token-info.service.ts b/apps/control-center/src/app/shared/services/keycloak-token-info/keycloak-token-info.service.ts deleted file mode 100644 index bb805b871..000000000 --- a/apps/control-center/src/app/shared/services/keycloak-token-info/keycloak-token-info.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import jwtDecode from 'jwt-decode'; -import { KeycloakService } from 'keycloak-angular'; -import { Observable, defer, of, switchMap } from 'rxjs'; -import { map, shareReplay } from 'rxjs/operators'; - -import { KeycloakToken } from './types/keycloak-token'; - -@Injectable({ providedIn: 'root' }) -export class KeycloakTokenInfoService { - private keycloakService = inject(KeycloakService); - info$: Observable = defer(() => this.token$).pipe( - map((token) => ({ - ...jwtDecode(token), - token, - })), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - - private token$ = defer(() => - this.keycloakService.isTokenExpired() ? this.keycloakService.updateToken() : of(null), - ).pipe(switchMap(() => this.keycloakService.getToken())); -} diff --git a/apps/control-center/src/app/shared/services/keycloak-token-info/to-wachter-headers.ts b/apps/control-center/src/app/shared/services/keycloak-token-info/to-wachter-headers.ts deleted file mode 100644 index eac25156e..000000000 --- a/apps/control-center/src/app/shared/services/keycloak-token-info/to-wachter-headers.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { generateId } from '@vality/domain-proto'; - -import { KeycloakToken } from './types'; - -export const toWachterHeaders = - (service: string) => - ({ email, sub, preferred_username, token }: KeycloakToken) => { - const id = generateId(); - return { - service, - authorization: `Bearer ${token}`, - 'x-woody-meta-user-identity-email': email, - 'x-woody-meta-user-identity-id': sub, - 'x-woody-meta-user-identity-realm': 'internal', - 'x-woody-meta-user-identity-username': preferred_username, - 'x-woody-parent-id': undefined, - 'x-woody-span-id': id, - 'x-woody-trace-id': id, - }; - }; diff --git a/apps/control-center/src/app/shared/services/keycloak-token-info/types/index.ts b/apps/control-center/src/app/shared/services/keycloak-token-info/types/index.ts deleted file mode 100644 index 41670c914..000000000 --- a/apps/control-center/src/app/shared/services/keycloak-token-info/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './keycloak-token'; diff --git a/apps/control-center/src/app/shared/services/keycloak-token-info/types/keycloak-token.ts b/apps/control-center/src/app/shared/services/keycloak-token-info/types/keycloak-token.ts deleted file mode 100644 index b8727c33e..000000000 --- a/apps/control-center/src/app/shared/services/keycloak-token-info/types/keycloak-token.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface KeycloakToken { - acr: string; - 'allowed-origins': string[]; - aud: string; - auth_time: number; - azp: string; - email: string; - exp: number; - family_name: string; - given_name: string; - iat: number; - iss: string; - jti: string; - name: string; - nbf: number; - nonce: string; - preferred_username: string; - realm_access: unknown; - resource_access: unknown; - scope: string; - session_state: string; - sub: string; - typ: string; - token: string; -} diff --git a/apps/control-center/src/app/shared/services/keycloak-user.service.ts b/apps/control-center/src/app/shared/services/keycloak-user.service.ts deleted file mode 100644 index 621059e81..000000000 --- a/apps/control-center/src/app/shared/services/keycloak-user.service.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Injectable, inject } from '@angular/core'; -import { rxResource } from '@angular/core/rxjs-interop'; -import { KeycloakService } from 'keycloak-angular'; -import { map } from 'rxjs'; - -import { KeycloakTokenInfoService } from './keycloak-token-info'; - -export interface KeycloakUser { - email: string; - username: string; -} - -@Injectable({ - providedIn: 'root', -}) -export class KeycloakUserService { - private keycloakTokenInfoService = inject(KeycloakTokenInfoService); - private keycloakService = inject(KeycloakService); - user = rxResource({ - stream: () => - this.keycloakTokenInfoService.info$.pipe( - map( - ({ email, preferred_username }): KeycloakUser => ({ - email, - username: preferred_username, - }), - ), - ), - }); - - logout() { - return this.keycloakService.logout(); - } -} diff --git a/apps/control-center/src/app/shared/services/navigate/models/route.ts b/apps/control-center/src/app/shared/services/navigate/models/route.ts index 4357a5d85..ea08fc53d 100644 --- a/apps/control-center/src/app/shared/services/navigate/models/route.ts +++ b/apps/control-center/src/app/shared/services/navigate/models/route.ts @@ -1,8 +1,8 @@ -import { LoadChildren, Route as NgRoute } from '@angular/router'; +import { Route as NgRoute } from '@angular/router'; import { Overwrite } from 'utility-types'; import { ZodObject, ZodRawShape } from 'zod'; -import { AppAuthGuardService, RoutingConfig, Services } from '../../app-auth-guard'; +import { RoutingConfig, Services, canActivateAuthRole } from '../../app-auth-guard'; export class Route< const TPath extends string = string, @@ -13,16 +13,17 @@ export class Route< public readonly config: { services?: Services[]; queryParams?: TQpSchema; - loadChildren?: LoadChildren; - } = {}, + } & NgRoute = {}, ) {} getRoute(): Overwrite { + const { services, ...routeParams } = this.config; + return { + ...routeParams, path: this.path, - loadChildren: this.config.loadChildren, - canActivate: [AppAuthGuardService], - data: { services: this.config.services }, + canActivate: [canActivateAuthRole], + data: { services }, }; } } diff --git a/apps/control-center/src/app/shared/services/user-info-based-id-generator/user-info-based-id-generator.service.ts b/apps/control-center/src/app/shared/services/user-info-based-id-generator/user-info-based-id-generator.service.ts index 21c3da807..194e86aba 100644 --- a/apps/control-center/src/app/shared/services/user-info-based-id-generator/user-info-based-id-generator.service.ts +++ b/apps/control-center/src/app/shared/services/user-info-based-id-generator/user-info-based-id-generator.service.ts @@ -1,17 +1,16 @@ import { Injectable, inject } from '@angular/core'; -import { KeycloakService } from 'keycloak-angular'; +import Keycloak from 'keycloak-js'; import short from 'short-uuid'; @Injectable() export class UserInfoBasedIdGeneratorService { - private keycloakService = inject(KeycloakService); + private keycloakService = inject(Keycloak); getUsernameBasedId(): string { - // TODO: replace it by id-generator after fix return `${this.getUsernameForId()}-${short().new()}`; } private getUsernameForId(): string { - return this.keycloakService.getUsername().substr(0, 10); + return this.keycloakService.profile.username.substring(0, 10); } } diff --git a/apps/control-center/src/app/shared/utils/table/create-contract-column.ts b/apps/control-center/src/app/shared/utils/table/create-contract-column.ts deleted file mode 100644 index 7352513ac..000000000 --- a/apps/control-center/src/app/shared/utils/table/create-contract-column.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { inject } from '@angular/core'; -import { createColumn } from '@vality/matez'; - -import { ContractCardComponent } from '../../components/contract-card/contract-card.component'; -import { SidenavInfoService } from '../../components/sidenav-info'; - -export const createContractColumn = createColumn( - ({ id, partyId }: { id: string; partyId: string }) => { - const sidenavInfoService = inject(SidenavInfoService); - return { - value: id, - click: () => { - sidenavInfoService.toggle(ContractCardComponent, { id, partyId }); - }, - }; - }, - { header: 'Contract' }, -); diff --git a/apps/control-center/src/app/shared/utils/table/create-currency-column.ts b/apps/control-center/src/app/shared/utils/table/create-currency-column.ts index 9d0ab6575..ef07944c4 100644 --- a/apps/control-center/src/app/shared/utils/table/create-currency-column.ts +++ b/apps/control-center/src/app/shared/utils/table/create-currency-column.ts @@ -1,6 +1,5 @@ import { getCurrencySymbol } from '@angular/common'; import { LOCALE_ID, inject } from '@angular/core'; -import { toObservable } from '@angular/core/rxjs-interop'; import { createColumn, formatCurrency } from '@vality/matez'; import { groupBy, uniq } from 'lodash-es'; import { combineLatest, of } from 'rxjs'; @@ -60,7 +59,7 @@ export const createCurrencyColumn = createColumn( const currenciesStoreService = inject(CurrenciesStoreService); return combineLatest([ combineLatest(currencyValuesByCodeList.map((g) => formatCurrencyValues(g))), - toObservable(currenciesStoreService.isLoading), + currenciesStoreService.isLoading$, ]).pipe( map(([currencyValueStrings, inProgress]) => ({ value: currencyValueStrings[0], diff --git a/apps/control-center/src/app/shared/utils/table/create-domain-object-column.ts b/apps/control-center/src/app/shared/utils/table/create-domain-object-column.ts index 0c97a1a50..cbc55fd9e 100644 --- a/apps/control-center/src/app/shared/utils/table/create-domain-object-column.ts +++ b/apps/control-center/src/app/shared/utils/table/create-domain-object-column.ts @@ -1,15 +1,13 @@ import { inject } from '@angular/core'; -import { Reference } from '@vality/domain-proto/internal/domain'; +import { Reference } from '@vality/domain-proto/domain'; import { createColumn } from '@vality/matez'; import { getUnionKey, getUnionValue } from '@vality/ng-thrift'; import { map, startWith } from 'rxjs/operators'; -import { DomainStoreService } from '../../../api/domain-config'; +import { DomainObjectsStoreService } from '../../../api/domain-config'; import { SidenavInfoService } from '../../components/sidenav-info'; -import { - DomainObjectCardComponent, - getDomainObjectDetails, -} from '../../components/thrift-api-crud'; +import { getDomainObjectDetails, getReferenceId } from '../../components/thrift-api-crud'; +import { DomainObjectCardComponent } from '../../components/thrift-api-crud/domain2'; export const createDomainObjectColumn = createColumn(({ ref }: { ref: Reference }) => { const sourceObj = { @@ -19,12 +17,12 @@ export const createDomainObjectColumn = createColumn(({ ref }: { ref: Reference const click = () => { sidenavInfoService.toggle(DomainObjectCardComponent, { ref }); }; - return inject(DomainStoreService) - .getObject(ref) - .pipe( + return inject(DomainObjectsStoreService) + .getLimitedObject(ref) + .value$.pipe( map((obj) => ({ - value: getDomainObjectDetails(obj).label || '', - description: getDomainObjectDetails(obj).id || '', + value: obj.name || '', + description: getReferenceId(obj.ref) || '', click, })), startWith({ diff --git a/apps/control-center/src/app/shared/utils/table/create-party-column.ts b/apps/control-center/src/app/shared/utils/table/create-party-column.ts index 599c59de2..aa8806316 100644 --- a/apps/control-center/src/app/shared/utils/table/create-party-column.ts +++ b/apps/control-center/src/app/shared/utils/table/create-party-column.ts @@ -11,11 +11,17 @@ export const createPartyColumn = createColumn( 'partyName' in params ? of(params.partyName) : inject(PartiesStoreService) - .get(id) - .pipe(map((party) => party.contact_info.registration_email)); + .getParty(id) + .value$.pipe( + map( + (party) => + party?.data?.party_name ?? + party?.data?.contact_info?.registration_email, + ), + ); const partyCell = { description: id, - link: () => `/party/${id}`, + link: () => `/parties/${id}`, }; return partyName$.pipe( map((partyName) => ({ diff --git a/apps/control-center/src/app/shared/utils/table/create-shop-column.ts b/apps/control-center/src/app/shared/utils/table/create-shop-column.ts index 6c3295182..44851e3ab 100644 --- a/apps/control-center/src/app/shared/utils/table/create-shop-column.ts +++ b/apps/control-center/src/app/shared/utils/table/create-shop-column.ts @@ -4,22 +4,24 @@ import { of } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; import { PartiesStoreService } from '../../../api/payment-processing'; -import { ShopCardComponent } from '../../components/shop-card/shop-card.component'; import { SidenavInfoService } from '../../components/sidenav-info'; +import { DomainObjectCardComponent } from '../../components/thrift-api-crud/domain2'; export const createShopColumn = createColumn( - ({ shopId, partyId, ...params }: { shopId: string; partyId: string; shopName?: string }) => { + ({ shopId, ...params }: { shopId: string; shopName?: string }) => { const name$ = 'shopName' in params ? of(params.shopName) : inject(PartiesStoreService) - .get(partyId) - .pipe(map((party) => party.shops.get(shopId).details.name)); + .getShop(shopId) + .value$.pipe(map((shop) => shop.data.details.name)); const sidenavInfoService = inject(SidenavInfoService); const shopCell = { description: shopId, click: () => { - sidenavInfoService.toggle(ShopCardComponent, { id: shopId, partyId }); + sidenavInfoService.toggle(DomainObjectCardComponent, { + ref: { shop_config: { id: shopId } }, + }); }, }; return name$.pipe( diff --git a/apps/control-center/src/app/shared/utils/table/create-wallet-column.ts b/apps/control-center/src/app/shared/utils/table/create-wallet-column.ts index f8d81a9d5..26bfd2df4 100644 --- a/apps/control-center/src/app/shared/utils/table/create-wallet-column.ts +++ b/apps/control-center/src/app/shared/utils/table/create-wallet-column.ts @@ -6,13 +6,13 @@ import { map, startWith } from 'rxjs/operators'; import { PartiesStoreService } from '../../../api/payment-processing'; export const createWalletColumn = createColumn( - ({ id, partyId, ...params }: { id: string; partyId: string; name?: string }) => { + ({ id, ...params }: { id: string; name?: string }) => { const name$ = 'name' in params ? of(params.name) : inject(PartiesStoreService) - .getWallet(id, partyId) - .pipe(map((wallet) => wallet?.name)); + .getWallet(id) + .value$.pipe(map((wallet) => wallet?.data?.name)); const cell = { description: id }; return name$.pipe( map((name) => ({ diff --git a/apps/control-center/src/app/shared/utils/table/format-cash-volume.ts b/apps/control-center/src/app/shared/utils/table/format-cash-volume.ts index 88d22e87d..49614933e 100644 --- a/apps/control-center/src/app/shared/utils/table/format-cash-volume.ts +++ b/apps/control-center/src/app/shared/utils/table/format-cash-volume.ts @@ -1,4 +1,4 @@ -import { CashVolume } from '@vality/domain-proto/internal/domain'; +import { CashVolume } from '@vality/domain-proto/domain'; import { formatCurrency } from '@vality/matez'; import { getUnionKey, getUnionValue } from '@vality/ng-thrift'; diff --git a/apps/control-center/src/app/shared/utils/table/format-rational.ts b/apps/control-center/src/app/shared/utils/table/format-rational.ts index aca373bac..fbafc568e 100644 --- a/apps/control-center/src/app/shared/utils/table/format-rational.ts +++ b/apps/control-center/src/app/shared/utils/table/format-rational.ts @@ -1,6 +1,6 @@ -import { Rational } from '@vality/domain-proto/internal/base'; +import { type base } from '@vality/domain-proto/domain'; import round from 'lodash-es/round'; -export function formatRational(value: Rational) { +export function formatRational(value: base.Rational) { return `${round((value.p / value.q) * 100, 4)}%`; } diff --git a/apps/control-center/src/app/shared/utils/table/index.ts b/apps/control-center/src/app/shared/utils/table/index.ts index 58b9617a8..5fd911963 100644 --- a/apps/control-center/src/app/shared/utils/table/index.ts +++ b/apps/control-center/src/app/shared/utils/table/index.ts @@ -3,7 +3,6 @@ export * from './format-cash-volume'; export * from './format-rational'; export * from './format-predicate'; export * from './get-cash-volume-parts'; -export * from './create-contract-column'; export * from './create-currency-column'; export * from './create-domain-object-column'; export * from './create-predicate-column'; diff --git a/apps/control-center/src/app/styles/_theme-colors.scss b/apps/control-center/src/app/styles/_theme-colors.scss index 58c216f4b..bf5763b53 100644 --- a/apps/control-center/src/app/styles/_theme-colors.scss +++ b/apps/control-center/src/app/styles/_theme-colors.scss @@ -4,32 +4,13 @@ @use 'sass:map'; @use '@angular/material' as mat; -$primary-palette: ( - 0: #000000, - 10: #00105c, - 20: #08218a, - 25: #1b2f95, - 30: #293ca0, - 35: #3649ac, - 40: #4355b9, - 50: #5d6fd4, - 60: #7789f0, - 70: #97a5ff, - 80: #bac3ff, - 90: #dee0ff, - 95: #f0efff, - 98: #fbf8ff, - 99: #fefbff, - 100: #ffffff, -); - -// Note: Color palettes are generated from primary: #3f51b5 +// Note: Color palettes are generated from primary: #4355b9 $_palettes: ( primary: ( 0: #000000, 10: #00105c, 20: #08218a, - 25: #1b2f95, + 25: #1b2f94, 30: #293ca0, 35: #3649ac, 40: #4355b9, @@ -65,15 +46,15 @@ $_palettes: ( 0: #000000, 10: #37003c, 20: #58055f, - 25: #65176b, - 30: #722578, - 35: #803284, + 25: #65166b, + 30: #732477, + 35: #803184, 40: #8e3e91, - 50: #aa58ac, + 50: #aa57ac, 60: #c771c8, - 70: #e48be4, - 80: #ffa9fd, - 90: #ffd6fa, + 70: #e58be4, + 80: #ffa9fc, + 90: #ffd6f9, 95: #ffebf9, 98: #fff7fa, 99: #fffbff, diff --git a/apps/control-center/src/app/styles/styles.scss b/apps/control-center/src/app/styles/styles.scss index 350b1dbc6..1cf8268f5 100644 --- a/apps/control-center/src/app/styles/styles.scss +++ b/apps/control-center/src/app/styles/styles.scss @@ -1,50 +1,59 @@ -@use 'sass:map'; @use '@angular/material' as mat; -@use '../../../../../libs/matez/index' as v; -@use './all-component-themes'; -@use './theme-colors' as palette; @use '@ng-matero/extensions' as mtx; +@use 'tailwindcss/theme.css'; +@use 'tailwindcss/utilities.css'; +@use './theme-colors'; -// Google Fonts Settings -.material-symbols-outlined { - font-variation-settings: - 'FILL' 0, - 'wght' 300, - 'GRAD' 0, - 'opsz' 24; -} +@use './all-component-themes'; +@use '../../../../../libs/matez/index' as v; $config: ( - color: ( - theme-type: light, - primary: palette.$primary-palette, - ), - density: ( - scale: 0, - ), + color: theme-colors.$primary-palette, + typography: Roboto, + density: 0, ); -$theme: mat.private-deep-merge-all(mat.define-theme($config), mtx.define-theme($config)); +html, +body { + height: 100%; +} + +html { + color-scheme: light; + @include mat.theme($config); +} + +body { + margin: 0; + background: var(--mat-sys-surface); + color: var(--mat-sys-on-surface); +} + +body.dark-mode { + color-scheme: dark; +} +// Deprecated :root { - html, - body { - height: 100%; - } - body { - margin: 0; - font-family: Roboto, 'Helvetica Neue', sans-serif; - } - - @include mat.elevation-classes(); - @include mat.app-background(); - @include mat.all-component-themes($theme); - @include mat.typography-hierarchy($theme); - @include v.typography-hierarchy($theme); - @include v.v-tokens($theme); - @include all-component-themes.all-component-themes($theme); - @include v.all-component-themes($theme); - @include v.mat-component-variants($theme); + $oldTheme: mat.define-theme( + ( + color: ( + theme-type: light, + primary: theme-colors.$primary-palette, + ), + density: ( + scale: 0, + ), + ) + ); + + @include mat.all-component-themes($oldTheme); + @include mat.typography-hierarchy($oldTheme); + @include v.typography-hierarchy($oldTheme); + @include v.v-tokens($oldTheme); + @include all-component-themes.all-component-themes($oldTheme); + @include v.all-component-themes($oldTheme); + @include v.mat-component-variants($oldTheme); @include mat.divider-overrides( ( diff --git a/apps/control-center/src/assets/_appConfig.json b/apps/control-center/src/assets/_appConfig.json index 0339c9813..4286015b5 100644 --- a/apps/control-center/src/assets/_appConfig.json +++ b/apps/control-center/src/assets/_appConfig.json @@ -1,5 +1,4 @@ { - "fileStorageEndpoint": "https://files.sample.vality.dev", "api": { "wachter": { "hostname": "control-center.vality.dev", diff --git a/apps/control-center/src/components/cash-field/cash-field.component.ts b/apps/control-center/src/components/cash-field/cash-field.component.ts index ca86d624c..f0ab4ca64 100644 --- a/apps/control-center/src/components/cash-field/cash-field.component.ts +++ b/apps/control-center/src/components/cash-field/cash-field.component.ts @@ -28,7 +28,7 @@ import isNil from 'lodash-es/isNil'; import { combineLatest } from 'rxjs'; import { distinctUntilChanged, map, shareReplay, startWith, take } from 'rxjs/operators'; -import { DomainStoreService } from '../../app/api/domain-config'; +import { CurrenciesStoreService } from '../../app/api/domain-config'; export interface Cash { amount: number; @@ -55,14 +55,15 @@ const RADIX_POINT = '.'; export class CashFieldComponent extends FormComponentSuperclass implements Validator, OnInit { private _locale = inject(LOCALE_ID); private destroyRef = inject(DestroyRef); - private domainStoreService = inject(DomainStoreService); + private currenciesStoreService = inject(CurrenciesStoreService); + @Input() label?: string; @Input({ transform: booleanAttribute }) required: boolean = false; amountControl = new FormControl(null); currencyControl = new FormControl(null); - options$ = this.domainStoreService.getObjects('currency').pipe( + options$ = this.currenciesStoreService.currencies$.pipe( startWith([] as CurrencyObject[]), map((objs): Option[] => objs diff --git a/apps/control-center/src/components/source-cash-field/source-cash-field.component.ts b/apps/control-center/src/components/source-cash-field/source-cash-field.component.ts index cc99d0697..894311484 100644 --- a/apps/control-center/src/components/source-cash-field/source-cash-field.component.ts +++ b/apps/control-center/src/components/source-cash-field/source-cash-field.component.ts @@ -27,7 +27,7 @@ import isNil from 'lodash-es/isNil'; import { combineLatest, of, switchMap } from 'rxjs'; import { distinctUntilChanged, map, shareReplay, startWith, take } from 'rxjs/operators'; -import { DomainStoreService } from '../../app/api/domain-config'; +import { CurrenciesStoreService } from '../../app/api/domain-config'; import { FetchSourcesService } from '../../app/sections/sources'; export interface SourceCash { @@ -59,7 +59,8 @@ export class SourceCashFieldComponent private _locale = inject(LOCALE_ID); private destroyRef = inject(DestroyRef); private fetchSourcesService = inject(FetchSourcesService); - private domainStoreService = inject(DomainStoreService); + private currenciesStoreService = inject(CurrenciesStoreService); + @Input() label?: string; @Input({ transform: booleanAttribute }) required: boolean = false; @@ -163,15 +164,13 @@ export class SourceCashFieldComponent } private getCurrencyExponent(symbolicCode: string) { - return this.domainStoreService - .getObjects('currency') - .pipe( - map( - (currencies) => - currencies.find((c) => c.data.symbolic_code === symbolicCode)?.data - ?.exponent ?? DEFAULT_EXPONENT, - ), - ); + return this.currenciesStoreService.currencies$.pipe( + map( + (currencies) => + currencies.find((c) => c.symbolic_code === symbolicCode)?.exponent ?? + DEFAULT_EXPONENT, + ), + ); } private setValues(amount: number, source: StatSource, exponent: number = DEFAULT_EXPONENT) { diff --git a/apps/control-center/src/environments/environment.dev.ts b/apps/control-center/src/environments/environment.dev.ts index 97da67d1d..d90e91b70 100644 --- a/apps/control-center/src/environments/environment.dev.ts +++ b/apps/control-center/src/environments/environment.dev.ts @@ -8,5 +8,4 @@ export const environment: Environment = { requests: true, }, ignoreRoles: true, - domain2: true, }; diff --git a/apps/control-center/src/environments/types/environment.ts b/apps/control-center/src/environments/types/environment.ts index 71bd8e4aa..d0a551dac 100644 --- a/apps/control-center/src/environments/types/environment.ts +++ b/apps/control-center/src/environments/types/environment.ts @@ -5,7 +5,6 @@ export interface Environment { logging: { requests?: boolean; }; - domain2?: boolean; // not available in production ignoreRoles?: boolean; } diff --git a/apps/control-center/src/index.html b/apps/control-center/src/index.html index d16fa6910..f76024c1d 100644 --- a/apps/control-center/src/index.html +++ b/apps/control-center/src/index.html @@ -18,7 +18,7 @@ rel="stylesheet" /> - + diff --git a/apps/control-center/src/app/core/config.service.ts b/apps/control-center/src/services/config/config.service.ts similarity index 51% rename from apps/control-center/src/app/core/config.service.ts rename to apps/control-center/src/services/config/config.service.ts index b35492d28..65b819b78 100644 --- a/apps/control-center/src/app/core/config.service.ts +++ b/apps/control-center/src/services/config/config.service.ts @@ -1,5 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; +import { observableResource } from '@vality/matez'; import { environment } from '../../environments/environment'; @@ -8,14 +9,8 @@ import { AppConfig } from './types/app-config'; @Injectable() export class ConfigService { private http = inject(HttpClient); - config: AppConfig; - load(): Promise { - return new Promise((resolve) => { - this.http.get(environment.appConfigPath).subscribe((config) => { - this.config = config; - resolve(undefined); - }); - }); - } + config = observableResource({ + loader: () => this.http.get(environment.appConfigPath), + }); } diff --git a/apps/control-center/src/services/config/index.ts b/apps/control-center/src/services/config/index.ts new file mode 100644 index 000000000..90392254a --- /dev/null +++ b/apps/control-center/src/services/config/index.ts @@ -0,0 +1 @@ +export * from './config.service'; diff --git a/apps/control-center/src/app/core/types/app-config.ts b/apps/control-center/src/services/config/types/app-config.ts similarity index 100% rename from apps/control-center/src/app/core/types/app-config.ts rename to apps/control-center/src/services/config/types/app-config.ts diff --git a/apps/control-center/src/services/index.ts b/apps/control-center/src/services/index.ts new file mode 100644 index 000000000..f03c2281a --- /dev/null +++ b/apps/control-center/src/services/index.ts @@ -0,0 +1 @@ +export * from './config'; diff --git a/apps/control-center/src/utils/thrift/provide-thrift-services.ts b/apps/control-center/src/utils/thrift/provide-thrift-services.ts index 989934b44..9bd316d15 100644 --- a/apps/control-center/src/utils/thrift/provide-thrift-services.ts +++ b/apps/control-center/src/utils/thrift/provide-thrift-services.ts @@ -3,12 +3,112 @@ import { FactoryProvider, Type, inject, + isDevMode, makeEnvironmentProviders, } from '@angular/core'; -import { map } from 'rxjs'; +import { ConnectOptions } from '@vality/domain-proto'; +import { toJson } from '@vality/ng-thrift'; +import Keycloak from 'keycloak-js'; +import { isObject } from 'lodash-es'; +import { combineLatest, map } from 'rxjs'; -import { ConfigService } from '../../app/core/config.service'; -import { KeycloakTokenInfoService, toWachterHeaders } from '../../app/shared/services'; +import { + KeycloakUserService, + createRequestWachterHeaders, + createWachterHeaders, +} from '../../app/shared/services'; +import { ConfigService } from '../../services'; + +export function parseThriftError(error: unknown) { + switch (error?.['name']) { + case 'ThriftServiceError': + return { + type: 'ThriftServiceError', + name: String(error?.['error']?.name), + message: String(error?.['error']?.message), + details: Object.fromEntries( + Object.entries(error?.['error'] || {}).filter( + ([k, v]) => k !== 'name' && k !== 'message' && v, + ), + ) as T, + error: error?.['error'], + wrapper: error, + } as const; + case 'ThriftServiceNotFoundError': + return { + type: 'ThriftServiceNotFoundError', + name: String(error?.['name']), + message: String(error?.['message']), + error, + } as const; + case 'ThriftServiceTimeoutError': + return { + type: 'ThriftServiceTimeoutError', + name: String(error?.['name']), + message: String(error?.['message']), + error, + } as const; + default: { + if (isObject(error)) + return { + type: 'UnknownError', + name: String(error?.['name']), + message: String(error?.['message']), + error, + } as const; + return { + type: 'UnknownError', + name: String(error), + message: String(error), + error, + } as const; + } + } +} + +const logger: ConnectOptions['loggingFn'] = (params) => { + const info = `${params.name} (${params.namespace} ${params.serviceName})`; + + switch (params.type) { + case 'error': { + const parsedError = parseThriftError(params.error); + console.groupCollapsed( + `🔴\u00A0${info}`, + `\n⚠️\u00A0${parsedError.message || parsedError.name || 'Unknown error'}`, + `\n🆔\u00A0Trace:\u00A0${params.headers['x-woody-trace-id']}`, + ); + console.error(parsedError.error); + if (isDevMode()) { + console.log('Arguments'); + console.log(JSON.stringify(toJson(params.args), null, 2)); + + console.groupCollapsed('Headers'); + console.table(params.headers); + console.groupEnd(); + } + console.groupEnd(); + return; + } + case 'success': { + if (isDevMode()) { + console.groupCollapsed(`🟢\u00A0${info}`); + console.log('Arguments'); + console.log(JSON.stringify(toJson(params.args), null, 2)); + console.log('Response'); + console.log(JSON.stringify(toJson(params.response), null, 2)); + + console.groupCollapsed('Headers'); + console.table(params.headers); + console.groupEnd(); + console.groupEnd(); + } + return; + } + case 'call': { + return; + } + } +}; function provideThriftService>( service: T, @@ -17,15 +117,29 @@ function provideThriftService>( return { provide: service, useFactory: () => { - const keycloakTokenInfoService = inject(KeycloakTokenInfoService); const configService = inject(ConfigService); + const keycloak = inject(Keycloak); + const keycloakUserService = inject(KeycloakUserService); + return new service( - keycloakTokenInfoService.info$.pipe( - map((kcInfo) => ({ - headers: toWachterHeaders(serviceName)(kcInfo), - logging: true, - ...configService.config.api.wachter, - })), + combineLatest([keycloakUserService.user.value$, configService.config.value$]).pipe( + map( + ([user, config]): ConnectOptions => ({ + headers: createWachterHeaders(serviceName, { + id: user.id, + email: user.email, + username: user.username, + token: keycloak.token ?? '', + }), + logging: true, + loggingFn: logger, + createCallOptions: () => ({ + headers: createRequestWachterHeaders(), + }), + timeout: isDevMode() ? 10_000 : 60_000, + ...config.api.wachter, + }), + ), ), ); }, diff --git a/apps/control-center/tsconfig.app.json b/apps/control-center/tsconfig.app.json index 236ff7f36..5f57e7e76 100644 --- a/apps/control-center/tsconfig.app.json +++ b/apps/control-center/tsconfig.app.json @@ -2,8 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", - "types": [] - // "moduleResolution": "bundler" + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/control-center/tsconfig.editor.json b/apps/control-center/tsconfig.editor.json index ef9568706..66c05b2c9 100644 --- a/apps/control-center/tsconfig.editor.json +++ b/apps/control-center/tsconfig.editor.json @@ -1,10 +1,6 @@ { "extends": "./tsconfig.json", - "include": [ - "src/**/*.ts", - "../../libs/ng-thrift/src/lib/components/thrift-editor/thrift-editor.component.ts", - "../../libs/ng-thrift/src/lib/components/thrift-editor/thrift-editor.module.ts" - ], + "include": ["src/**/*.ts"], "compilerOptions": {}, "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] } diff --git a/cspell.json b/cspell.json index def8e4b45..5806534bc 100644 --- a/cspell.json +++ b/cspell.json @@ -35,7 +35,6 @@ "papaparse", "unparse", "Wachter", - "Deanonimus", "vality", "valitydev", "Magista", @@ -61,6 +60,8 @@ "buildable", "matv", "autorenew", - "refless" + "refless", + + "cmdk" ] } diff --git a/libs/matez/_index.scss b/libs/matez/_index.scss index b7fe82c4c..ea3825282 100644 --- a/libs/matez/_index.scss +++ b/libs/matez/_index.scss @@ -1,9 +1,4 @@ -@forward './src/lib/styles/utils/create-palette' show create-palette; - @forward './src/lib/styles/typography-hierarchy' show typography-hierarchy; @forward './src/lib/styles/all-component-themes' show all-component-themes; -@forward './src/lib/styles/select-theme' show select-theme; -@forward './src/lib/styles/app' show app; -@forward './src/lib/styles/core' show core; @forward './src/lib/styles/mat-component-variants' show mat-component-variants; @forward './src/lib/styles/v-tokens' show v-tokens; diff --git a/libs/matez/src/lib/components/cmdk/cmdk.component.html b/libs/matez/src/lib/components/cmdk/cmdk.component.html new file mode 100644 index 000000000..7207c5d40 --- /dev/null +++ b/libs/matez/src/lib/components/cmdk/cmdk.component.html @@ -0,0 +1,49 @@ +
+
+ + Search + + search + +
+ + + @let options = options$ | async; + + @if (options?.length) { + @for (option of options; track $index) { + + {{ option.icon || 'article' }} + + + + } + } @else if (searchControl.value) { + @if (inProgress$ | async) { + + + + } @else { + + list + No results found + + } + } @else { + + help + Start typing to search + + } + +
diff --git a/libs/matez/src/lib/components/cmdk/cmdk.component.scss b/libs/matez/src/lib/components/cmdk/cmdk.component.scss new file mode 100644 index 000000000..c44d2c97a --- /dev/null +++ b/libs/matez/src/lib/components/cmdk/cmdk.component.scss @@ -0,0 +1,12 @@ +::ng-deep .cmdk-backdrop { + backdrop-filter: blur(6px) saturate(80%) brightness(80%); +} + +::ng-deep .cmdk-panel { + display: block; + border-radius: 12px; + max-height: calc(80vh - 32px); + box-shadow: + 0 20px 25px -5px rgba(0, 0, 0, 0.1), + 0 10px 10px -5px rgba(0, 0, 0, 0.04); +} diff --git a/libs/matez/src/lib/components/cmdk/cmdk.component.ts b/libs/matez/src/lib/components/cmdk/cmdk.component.ts new file mode 100644 index 000000000..1c4e9bcec --- /dev/null +++ b/libs/matez/src/lib/components/cmdk/cmdk.component.ts @@ -0,0 +1,64 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, DestroyRef, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatListModule } from '@angular/material/list'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { Router, RouterModule } from '@angular/router'; + +import { HighlightDirective } from '../../directives'; +import { getValueChanges } from '../../utils'; + +import { CmdkService } from './cmdk.service'; +import { CmdkOption } from './types/cmdk-option'; + +@Component({ + selector: 'v-cmdk', + imports: [ + MatDialogModule, + MatButtonModule, + MatInputModule, + MatListModule, + MatIconModule, + CommonModule, + RouterModule, + ReactiveFormsModule, + MatProgressBarModule, + HighlightDirective, + MatTooltipModule, + ], + templateUrl: './cmdk.component.html', + styleUrl: './cmdk.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class CmdkComponent { + private cmdkService = inject(CmdkService); + private dr = inject(DestroyRef); + private router = inject(Router); + + searchControl = new FormControl(''); + options$ = this.cmdkService.options$; + inProgress$ = this.cmdkService.inProgress$; + + constructor() { + getValueChanges(this.searchControl) + .pipe(takeUntilDestroyed(this.dr)) + .subscribe((searchStr) => { + this.cmdkService.search$.next(searchStr || ''); + }); + } + + action(option: CmdkOption) { + this.cmdkService.close(); + if (option.action) { + option.action(); + return; + } + this.router.navigate([option.url]); + } +} diff --git a/libs/matez/src/lib/components/cmdk/cmdk.service.ts b/libs/matez/src/lib/components/cmdk/cmdk.service.ts new file mode 100644 index 000000000..0dc79fbb7 --- /dev/null +++ b/libs/matez/src/lib/components/cmdk/cmdk.service.ts @@ -0,0 +1,85 @@ +import { Injectable, OnDestroy, inject } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { + BehaviorSubject, + Subject, + debounceTime, + distinctUntilChanged, + finalize, + shareReplay, + switchMap, + tap, +} from 'rxjs'; + +import { getPossiblyAsyncObservable } from '../../utils'; + +import { CmdkComponent } from './cmdk.component'; +import { CmdkOptions } from './types/cmdk-options'; + +@Injectable({ + providedIn: 'root', +}) +export class CmdkService implements OnDestroy { + private dialog = inject(MatDialog); + private options: CmdkOptions = { + search: () => [], + }; + search$ = new Subject(); + + inProgress$ = new BehaviorSubject(false); + options$ = this.search$.pipe( + distinctUntilChanged(), + tap(() => this.inProgress$.next(true)), + debounceTime(300), + switchMap((searchStr) => + getPossiblyAsyncObservable(this.options.search(searchStr)).pipe( + finalize(() => this.inProgress$.next(false)), + ), + ), + shareReplay(1), + ); + + constructor() { + document.addEventListener('keydown', this.listener); + } + + ngOnDestroy() { + document.removeEventListener('keydown', this.listener); + } + + open() { + if (this.dialog.openDialogs.length) return; + this.dialog.open(CmdkComponent, { + width: 'calc(max(400px, 100vw - 32px))', + maxWidth: '600px', + position: { top: '20vh' }, + hasBackdrop: true, + backdropClass: 'cmdk-backdrop', + panelClass: 'cmdk-panel', + }); + } + + close() { + this.search$.next(''); + this.dialog.closeAll(); + } + + toggle() { + if (this.dialog.openDialogs.length) { + this.close(); + } else { + this.open(); + } + } + + init(options: CmdkOptions) { + this.options = options; + } + + private listener = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault(); + this.toggle(); + } + }; +} diff --git a/libs/matez/src/lib/components/cmdk/index.ts b/libs/matez/src/lib/components/cmdk/index.ts new file mode 100644 index 000000000..1eb9155c8 --- /dev/null +++ b/libs/matez/src/lib/components/cmdk/index.ts @@ -0,0 +1,3 @@ +export * from './cmdk.service'; +export * from './types/cmdk-option'; +export * from './types/cmdk-options'; diff --git a/libs/matez/src/lib/components/cmdk/types/cmdk-option.ts b/libs/matez/src/lib/components/cmdk/types/cmdk-option.ts new file mode 100644 index 000000000..ec7e4625d --- /dev/null +++ b/libs/matez/src/lib/components/cmdk/types/cmdk-option.ts @@ -0,0 +1,8 @@ +export interface CmdkOption { + label: string; + description?: string; + tooltip?: string; + icon?: string; + url?: string; + action?: () => void; +} diff --git a/libs/matez/src/lib/components/cmdk/types/cmdk-options.ts b/libs/matez/src/lib/components/cmdk/types/cmdk-options.ts new file mode 100644 index 000000000..cb26a6562 --- /dev/null +++ b/libs/matez/src/lib/components/cmdk/types/cmdk-options.ts @@ -0,0 +1,7 @@ +import { PossiblyAsync } from '../../../utils'; + +import { CmdkOption } from './cmdk-option'; + +export interface CmdkOptions { + search: (searchStr: string) => PossiblyAsync; +} diff --git a/libs/matez/src/lib/components/index.ts b/libs/matez/src/lib/components/index.ts index fa75ecc33..3fb602b95 100644 --- a/libs/matez/src/lib/components/index.ts +++ b/libs/matez/src/lib/components/index.ts @@ -16,3 +16,4 @@ export * from './switch-button'; export * from './value'; export * from './nav'; export * from './content-loading'; +export * from './cmdk'; diff --git a/libs/matez/src/lib/components/input-field/input-field.component.scss b/libs/matez/src/lib/components/input-field/input-field.component.scss index 0537df3b9..d2016eafa 100644 --- a/libs/matez/src/lib/components/input-field/input-field.component.scss +++ b/libs/matez/src/lib/components/input-field/input-field.component.scss @@ -1,4 +1,6 @@ .v-input-field { + width: 100%; + .postfix { white-space: nowrap; } diff --git a/libs/matez/src/lib/components/input-field/input-field.component.ts b/libs/matez/src/lib/components/input-field/input-field.component.ts index fec435657..fe307c61b 100644 --- a/libs/matez/src/lib/components/input-field/input-field.component.ts +++ b/libs/matez/src/lib/components/input-field/input-field.component.ts @@ -14,7 +14,7 @@ export class InputFieldComponent extends FormControlSuperclass { @Input() label?: string; @Input() placeholder: string = ''; @Input() type: 'string' | 'number' = 'string'; - @Input() appearance: MatFormFieldAppearance = 'fill'; + @Input() appearance!: MatFormFieldAppearance; @Input() size?: 'small' | ''; cleanButton = input(false, { transform: booleanAttribute }); icon = input(); diff --git a/libs/matez/src/lib/components/nav/nav.component.html b/libs/matez/src/lib/components/nav/nav.component.html index 1990f1710..bb441c150 100644 --- a/libs/matez/src/lib/components/nav/nav.component.html +++ b/libs/matez/src/lib/components/nav/nav.component.html @@ -1,17 +1,58 @@ -
- @for (linksGroup of links(); track linksGroup; let index = $index) { - @if (index !== 0) { +
+ @let activeLinks = (activeLinks$ | async) || []; + + @for (linkGroup of viewedLinks$ | async; track linkGroup; let index = $index) { + @if (index > 0) { } - @for (link of linksGroup; track link) { -
- {{ link.label }} -
- } +
+ @for (link of linkGroup.children; track link) { + @let isActive = activeLinks.includes(link); + + @if (link.children) { + + } @else { + + } + @if (isActive && link.children?.length) { +
+ @for (childLink of link.children; track childLink) { + + } +
+ } + } +
}
diff --git a/libs/matez/src/lib/components/nav/nav.component.ts b/libs/matez/src/lib/components/nav/nav.component.ts index 379f453f4..05ad3a8b5 100644 --- a/libs/matez/src/lib/components/nav/nav.component.ts +++ b/libs/matez/src/lib/components/nav/nav.component.ts @@ -1,52 +1,107 @@ -import { AsyncPipe, NgClass } from '@angular/common'; -import { Component, ViewEncapsulation, booleanAttribute, inject, input } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { + Component, + Injector, + ViewEncapsulation, + inject, + input, + runInInjectionContext, +} from '@angular/core'; import { toObservable } from '@angular/core/rxjs-interop'; -import { MatDivider } from '@angular/material/divider'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatIconModule } from '@angular/material/icon'; import { RouterLink } from '@angular/router'; -import { isArray } from 'lodash-es'; -import { combineLatest } from 'rxjs'; -import { map, shareReplay } from 'rxjs/operators'; +import { isNil } from 'lodash-es'; +import { + Observable, + combineLatest, + combineLatestWith, + map, + of, + shareReplay, + switchMap, +} from 'rxjs'; +import { Overwrite } from 'utility-types'; import { UrlService } from '../../services'; +import { PossiblyAsyncValue, getPossiblyAsyncValue } from '../../utils'; -export interface Link { - label: string; - url: string; +export interface BaseLink { + label?: string; + url?: string; + checkUrl?: string; + icon?: string; + children?: BaseLink[]; + isHidden?: boolean; +} + +export type Link = PossiblyAsyncValue, [url: string]>; + +function isActiveLink(link: BaseLink, url: string): boolean { + return ( + !isNil(url) && + (url.startsWith(link.url || '') || (!!link.checkUrl && url.startsWith(link.checkUrl || ''))) + ); +} + +function getActiveLinks(links: BaseLink[], url: string): BaseLink[] { + return links.reduce((acc, link) => { + if (isActiveLink(link, url)) { + acc.push(link); + } + return acc.concat(getActiveLinks(link.children || [], url)); + }, [] as BaseLink[]); } @Component({ selector: 'v-nav', styleUrls: ['./nav.component.scss'], templateUrl: './nav.component.html', - imports: [RouterLink, NgClass, AsyncPipe, MatDivider], + imports: [RouterLink, MatButtonModule, MatDividerModule, CommonModule, MatIconModule], encapsulation: ViewEncapsulation.None, }) export class NavComponent { private urlService = inject(UrlService); - type = input<'secondary' | undefined>(); - links = input([], { - transform: (v) => (isArray(v[0]) ? (v as never) : [v as never]), - }); - exact = input(false, { transform: booleanAttribute }); - - activeLink$ = combineLatest([ - this.urlService.url$, - toObservable(this.links), - toObservable(this.exact), - ]).pipe( - map( - ([url, links, exact]) => - links.flat().reduce( - (res, link) => { - const index = url.indexOf(link.url); - if (index !== -1 && (exact ? index === 0 : index < res.index)) { - return { index, link }; - } - return res; - }, - { index: Infinity, link: null as never as Link }, - )?.link, - ), + private injector = inject(Injector); + + links = input([]); + + viewedLinks$ = combineLatest([toObservable(this.links), this.urlService.url$]).pipe( + switchMap(([links, url]) => this.getViewedLinks(links, url)), shareReplay({ refCount: true, bufferSize: 1 }), ); + activeLinks$ = this.viewedLinks$.pipe( + combineLatestWith(this.urlService.url$), + map(([links, url]) => getActiveLinks(links, url)), + shareReplay({ refCount: true, bufferSize: 1 }), + ); + + getViewedLinks(links: Link[], url: string): Observable { + if (!links?.length) { + return of([]); + } + return combineLatest( + links.map((link) => + runInInjectionContext(this.injector, () => getPossiblyAsyncValue(link, [url])), + ), + ).pipe( + switchMap((links) => + combineLatest( + links.map((link) => + link.children ? this.getViewedLinks(link.children, url) : of(undefined), + ), + ).pipe( + map((children) => + links + .map((link, index) => ({ + ...link, + children: children[index], + })) + .filter((link) => !link.isHidden), + ), + ), + ), + ); + } } diff --git a/libs/matez/src/lib/components/select-autocomplete/select-field/select-field.component.ts b/libs/matez/src/lib/components/select-autocomplete/select-field/select-field.component.ts index e3942453c..85c72dd50 100644 --- a/libs/matez/src/lib/components/select-autocomplete/select-field/select-field.component.ts +++ b/libs/matez/src/lib/components/select-autocomplete/select-field/select-field.component.ts @@ -17,7 +17,7 @@ export class SelectFieldComponent extends FormControlSuperclass[] = []; @Output() searchChange = new EventEmitter(); - @Input() appearance: MatFormFieldAppearance = 'fill'; + @Input() appearance!: MatFormFieldAppearance; @Input() label?: string; @Input() hint?: string; diff --git a/libs/matez/src/lib/services/log/log-error.ts b/libs/matez/src/lib/services/log/log-error.ts deleted file mode 100644 index 40af8f7a8..000000000 --- a/libs/matez/src/lib/services/log/log-error.ts +++ /dev/null @@ -1,44 +0,0 @@ -import isObject from 'lodash-es/isObject'; - -export const DEFAULT_ERROR_NAME = 'Unknown error'; - -export class LogError { - source!: Record; - - get name() { - return String(this.source?.['name'] || DEFAULT_ERROR_NAME); - } - - get message() { - return String(this.source?.['message'] || this.name); - } - - get cause() { - return this.source?.['cause']; - } - - get details() { - return Object.fromEntries( - Object.entries(this.source).filter(([k, v]) => k !== 'name' && k !== 'message' && v), - ); - } - - constructor(error: unknown) { - this.source = (isObject(error) - ? error - : new Error( - error ? String(error) : DEFAULT_ERROR_NAME, - )) as unknown as LogError['source']; - } - - getLogMessage(message?: string) { - return [ - message && `Caught error: ${message}`, - this.name && `Name: ${this.name}`, - this.message && `Message: ${this.message}`, - Object.keys(this.details).length && JSON.stringify(this.details, null, 2), - ] - .filter(Boolean) - .join('\n'); - } -} diff --git a/libs/matez/src/lib/services/log/notify-log.service.ts b/libs/matez/src/lib/services/log/notify-log.service.ts index bb6a05743..9ea369bdd 100644 --- a/libs/matez/src/lib/services/log/notify-log.service.ts +++ b/libs/matez/src/lib/services/log/notify-log.service.ts @@ -1,32 +1,55 @@ -import { Injectable, inject } from '@angular/core'; +import { Injectable, InjectionToken, inject } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { capitalize } from 'lodash-es'; +import { capitalize, isObject } from 'lodash-es'; import { Observer, first, timeout } from 'rxjs'; import { PossiblyAsync, getPossiblyAsyncObservable } from '../../utils'; -import { DEFAULT_ERROR_NAME, LogError } from './log-error'; import { Operation } from './types/operation'; +export interface LogError { + name?: string; + message?: string; + details?: object; + error?: unknown; + noConsole?: boolean; +} + +type ErrorParser = (error: unknown) => LogError; + +export const ERROR_PARSER = new InjectionToken('ErrorParser'); + const DEFAULT_DURATION_MS = 3_000; const DEFAULT_ERROR_DURATION_MS = 10_000; const DEFAULT_TIMEOUT_MS = 10_000; +const DEFAULT_ERROR_PARSER: ErrorParser = (error: unknown) => + ({ ...(isObject(error) ? error : new Error(String(error))), error }) as LogError; @Injectable({ providedIn: 'root' }) export class NotifyLogService { private snackBar = inject(MatSnackBar); + private parseError = inject(ERROR_PARSER, { optional: true }) || DEFAULT_ERROR_PARSER; + success = (message: PossiblyAsync = 'Completed successfully'): void => { this.notify(message); }; - error = (errors: unknown | unknown[], message?: PossiblyAsync): void => { - const logErrors = (Array.isArray(errors) ? errors : [errors]).map((e) => new LogError(e)); - message = message || (logErrors.length === 1 ? logErrors[0].message : DEFAULT_ERROR_NAME); - this.subscribeWithTimeout(message, (msg) => { - console.warn( - [`Caught error: ${msg}.`, ...logErrors.map((e) => e.getLogMessage())].join('\n'), - ); - }); + error = (error: unknown, message?: PossiblyAsync): void => { + const parsedError = this.parseError(error); + message = message || parsedError.message || parsedError.name || 'An error occurred'; + if (!parsedError.noConsole) { + this.subscribeWithTimeout(message, (msg) => { + console.warn( + [ + `Caught error: ${msg}.`, + parsedError.name, + parsedError.message, + parsedError.details, + ].join('\n'), + parsedError.error, + ); + }); + } this.notify(message, DEFAULT_ERROR_DURATION_MS); }; diff --git a/libs/matez/src/lib/services/url.service.ts b/libs/matez/src/lib/services/url.service.ts index 4fc087ffd..f1bd7ffc1 100644 --- a/libs/matez/src/lib/services/url.service.ts +++ b/libs/matez/src/lib/services/url.service.ts @@ -1,31 +1,32 @@ -import { Injectable, inject } from '@angular/core'; +import { Injectable, computed, inject } from '@angular/core'; +import { toObservable, toSignal } from '@angular/core/rxjs-interop'; import { Router } from '@angular/router'; import { distinctUntilChanged, filter, map, shareReplay, startWith } from 'rxjs/operators'; +export function getUrlPath(url: string): string[] { + return url?.split('/')?.slice(1) ?? []; +} + @Injectable({ providedIn: 'root', }) export class UrlService { private router = inject(Router); + url$ = this.router.events.pipe( startWith(null), - map(() => this.url), + map(() => this.getUrl()), filter((url) => this.router.navigated || (!!url && url !== '/')), distinctUntilChanged(), shareReplay({ refCount: true, bufferSize: 1 }), ); - path$ = this.url$.pipe( - map(() => this.path), - shareReplay({ refCount: true, bufferSize: 1 }), - ); + url = toSignal(this.url$, { initialValue: this.getUrl() }); + path = computed(() => getUrlPath(this.url())); + path$ = toObservable(this.path); - get url() { + private getUrl() { return this.router.url && this.router.url !== '/' ? this.router.url.split('?', 1)[0].split('#', 1)[0] : window.location.pathname; } - - get path() { - return this.url?.split('/')?.slice(1) ?? []; - } } diff --git a/libs/matez/src/lib/styles/_all-component-themes.scss b/libs/matez/src/lib/styles/_all-component-themes.scss index 424672e5c..d42a783e7 100644 --- a/libs/matez/src/lib/styles/_all-component-themes.scss +++ b/libs/matez/src/lib/styles/_all-component-themes.scss @@ -1,5 +1,4 @@ @use '@angular/material' as mat; -@use '@ng-matero/extensions' as mtx; @use '../components/table/table-theme'; @use '../components/value/value-theme'; @@ -12,8 +11,6 @@ // Alternatively, you can import and @include the theme mixins for each component // that you are using. @mixin all-component-themes($theme) { - @include mtx.select-theme($theme); - @include table-theme.theme($theme); @include value-theme.theme($theme); @include dialog-theme.theme($theme); diff --git a/libs/matez/src/lib/styles/_app.scss b/libs/matez/src/lib/styles/_app.scss deleted file mode 100644 index 8a4529bb8..000000000 --- a/libs/matez/src/lib/styles/_app.scss +++ /dev/null @@ -1,10 +0,0 @@ -@mixin app($theme) { - html, - body { - height: 100%; - } - body { - margin: 0; - font-family: Roboto, 'Helvetica Neue', sans-serif; - } -} diff --git a/libs/matez/src/lib/styles/_core.scss b/libs/matez/src/lib/styles/_core.scss deleted file mode 100644 index 6c189960d..000000000 --- a/libs/matez/src/lib/styles/_core.scss +++ /dev/null @@ -1,8 +0,0 @@ -@use '@angular/material' as mat; - -@mixin core() { - // Include the common styles for Angular Material. We include this here so that you only - // have to load a single css file for Angular Material in your app. - // Be sure that you only ever include this mixin once! - @include mat.core(); -} diff --git a/libs/matez/src/lib/styles/_select-theme.scss b/libs/matez/src/lib/styles/_select-theme.scss deleted file mode 100644 index 5e92de26a..000000000 --- a/libs/matez/src/lib/styles/_select-theme.scss +++ /dev/null @@ -1,6 +0,0 @@ -@use '@angular/material' as mat; -@use '@ng-matero/extensions' as mtx; - -@mixin select-theme($theme) { - @include mtx.select-theme($theme); -} diff --git a/libs/matez/src/lib/styles/utils/create-palette.scss b/libs/matez/src/lib/styles/utils/create-palette.scss deleted file mode 100644 index 81d7cecdf..000000000 --- a/libs/matez/src/lib/styles/utils/create-palette.scss +++ /dev/null @@ -1,84 +0,0 @@ -@use 'sass:color'; -@use 'sass:math'; -@use 'sass:map'; - -$values: (50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400, A700); - -@function create-palette($color) { - $white: #fff; - $black: #000; - $baseDark: multiply($color, $color); - - $palette: ( - 50: color.mix($color, $white, 12%), - 100: color.mix($color, $white, 30%), - 200: color.mix($color, $white, 50%), - 300: color.mix($color, $white, 70%), - 400: color.mix($color, $white, 85%), - 500: color.mix($color, $white, 100%), - 600: color.mix($color, $baseDark, 87%), - 700: color.mix($color, $baseDark, 70%), - 800: color.mix($color, $baseDark, 54%), - 900: color.mix($color, $baseDark, 25%), - A100: color.adjust( - color.adjust(color.mix($black, $baseDark, 15%), $saturation: 80%), - $lightness: 65% - ), - A200: color.adjust( - color.adjust(color.mix($black, $baseDark, 15%), $saturation: 80%), - $lightness: 55% - ), - A400: color.adjust( - color.adjust(color.mix($black, $baseDark, 15%), $saturation: 100%), - $lightness: 45% - ), - A700: color.adjust( - color.adjust(color.mix($black, $baseDark, 15%), $saturation: 100%), - $lightness: 40% - ), - ); - - $contrast: (); - @each $v in $values { - $contrast: map.merge( - $contrast, - ( - $v: getContrast(map.get($palette, $v)), - ) - ); - } - - $palette: map.merge( - $palette, - ( - contrast: $contrast, - ) - ); - - @return $palette; -} - -@function multiply($rgb1, $rgb2) { - $r: math.floor(calc(color.red($rgb1) * color.red($rgb2) / 255)); - $g: math.floor(calc(color.green($rgb1) * color.green($rgb2) / 255)); - $b: math.floor(calc(color.blue($rgb1) * color.blue($rgb2) / 255)); - @return rgb($r, $g, $b); -} - -@function getBrightness($color) { - @return calc( - (color.red($color) * 299 + color.green($color) * 587 + color.blue($color) * 114) / 1000 - ); -} - -@function isLight($color) { - @return getBrightness($color) >= 128; -} - -@function getContrast($color) { - @if isLight($color) { - @return #000; - } @else { - @return #fff; - } -} diff --git a/libs/matez/src/lib/utils/async/from-async.ts b/libs/matez/src/lib/utils/async/from-async.ts new file mode 100644 index 000000000..13d5ccc65 --- /dev/null +++ b/libs/matez/src/lib/utils/async/from-async.ts @@ -0,0 +1,9 @@ +import { isSignal } from '@angular/core'; +import { toObservable } from '@angular/core/rxjs-interop'; +import { Observable, from } from 'rxjs'; + +import { Async } from './is-async'; + +export function fromAsync(asyncValue: Async): Observable { + return isSignal(asyncValue) ? toObservable(asyncValue) : from(asyncValue); +} diff --git a/libs/matez/src/lib/utils/async/get-possibly-async-observable.ts b/libs/matez/src/lib/utils/async/get-possibly-async-observable.ts index d9d6ebc16..3b13df1d8 100644 --- a/libs/matez/src/lib/utils/async/get-possibly-async-observable.ts +++ b/libs/matez/src/lib/utils/async/get-possibly-async-observable.ts @@ -1,7 +1,8 @@ -import { Observable, from, of } from 'rxjs'; +import { Observable, of } from 'rxjs'; +import { fromAsync } from './from-async'; import { PossiblyAsync, isAsync } from './is-async'; export function getPossiblyAsyncObservable(possiblyAsync: PossiblyAsync): Observable { - return isAsync(possiblyAsync) ? from(possiblyAsync) : of(possiblyAsync); + return isAsync(possiblyAsync) ? fromAsync(possiblyAsync) : of(possiblyAsync); } diff --git a/libs/matez/src/lib/utils/async/get-possibly-async-value.ts b/libs/matez/src/lib/utils/async/get-possibly-async-value.ts new file mode 100644 index 000000000..da49aa49a --- /dev/null +++ b/libs/matez/src/lib/utils/async/get-possibly-async-value.ts @@ -0,0 +1,21 @@ +import { Observable, of } from 'rxjs'; + +import { getPossiblyAsyncObservable } from './get-possibly-async-observable'; +import { PossiblyAsyncFn, PossiblyAsyncValue } from './types/possibly-async-value'; + +export function getPossiblyAsyncValue( + getValue: PossiblyAsyncValue | D, + args: P = [] as never as P, + defaultValue: T = undefined as T, + isDefaultValue: (v: T | D) => boolean = ((v: T | D) => v === undefined) as ( + v: T | D, + ) => boolean, +): Observable { + if (isDefaultValue(getValue as D)) { + return of(defaultValue as T); + } + if (typeof getValue === 'function') { + return getPossiblyAsyncObservable((getValue as PossiblyAsyncFn)(...args)); + } + return getPossiblyAsyncObservable(getValue) as never as Observable; +} diff --git a/libs/matez/src/lib/utils/async/index.ts b/libs/matez/src/lib/utils/async/index.ts index 8099d0daa..9f21ac601 100644 --- a/libs/matez/src/lib/utils/async/index.ts +++ b/libs/matez/src/lib/utils/async/index.ts @@ -1,3 +1,6 @@ export * from './async-transform'; export * from './is-async'; +export * from './from-async'; export * from './get-possibly-async-observable'; +export * from './get-possibly-async-value'; +export * from './types/possibly-async-value'; diff --git a/libs/matez/src/lib/utils/async/is-async.ts b/libs/matez/src/lib/utils/async/is-async.ts index 2f6f1f70d..d627690e9 100644 --- a/libs/matez/src/lib/utils/async/is-async.ts +++ b/libs/matez/src/lib/utils/async/is-async.ts @@ -1,9 +1,10 @@ +import { Signal, isSignal } from '@angular/core'; import { Observable, isObservable } from 'rxjs'; import { isPromise } from 'rxjs/internal/util/isPromise'; -export type Async = Observable | Promise; +export type Async = Observable | Promise | Signal; export type PossiblyAsync = Async | T; export function isAsync(value: PossiblyAsync): value is Async { - return isObservable(value) || isPromise(value); + return isObservable(value) || isPromise(value) || isSignal(value); } diff --git a/libs/matez/src/lib/utils/async/types/possibly-async-value.ts b/libs/matez/src/lib/utils/async/types/possibly-async-value.ts new file mode 100644 index 000000000..33792c8b0 --- /dev/null +++ b/libs/matez/src/lib/utils/async/types/possibly-async-value.ts @@ -0,0 +1,7 @@ +import { PossiblyAsync } from '../is-async'; + +export type PossiblyAsyncFn = (...args: P) => PossiblyAsync; + +export type PossiblyAsyncValue = + | PossiblyAsync + | PossiblyAsyncFn; diff --git a/libs/matez/src/lib/utils/fetch-all.ts b/libs/matez/src/lib/utils/fetch-all.ts new file mode 100644 index 000000000..cd2106f03 --- /dev/null +++ b/libs/matez/src/lib/utils/fetch-all.ts @@ -0,0 +1,19 @@ +import { Observable, map, of, switchMap } from 'rxjs'; + +export function fetchAll( + loadFn: ( + continuationToken?: string, + ) => Observable<{ result: T[]; continuation_token?: string }>, + continuationToken?: string, +): Observable { + return loadFn(continuationToken).pipe( + switchMap((resp) => { + if (resp.continuation_token) { + return fetchAll(loadFn, resp.continuation_token).pipe( + map((nextData) => [...resp.result, ...nextData]), + ); + } + return of(resp.result); + }), + ); +} diff --git a/libs/matez/src/lib/utils/index.ts b/libs/matez/src/lib/utils/index.ts index 3882849dc..7ff72c2ba 100644 --- a/libs/matez/src/lib/utils/index.ts +++ b/libs/matez/src/lib/utils/index.ts @@ -18,3 +18,5 @@ export * from './transform-attribute'; export * from './decorators'; export * from './create-storage-value'; export * from './subscribe-return'; +export * from './observable-resource'; +export * from './fetch-all'; diff --git a/libs/matez/src/lib/utils/observable-resource/index.ts b/libs/matez/src/lib/utils/observable-resource/index.ts new file mode 100644 index 000000000..55cefc4ca --- /dev/null +++ b/libs/matez/src/lib/utils/observable-resource/index.ts @@ -0,0 +1,2 @@ +export * from './observable-resource'; +export * from './paged-observable-resource'; diff --git a/libs/matez/src/lib/utils/observable-resource/observable-resource.ts b/libs/matez/src/lib/utils/observable-resource/observable-resource.ts new file mode 100644 index 000000000..ff721b1ad --- /dev/null +++ b/libs/matez/src/lib/utils/observable-resource/observable-resource.ts @@ -0,0 +1,117 @@ +import { DestroyRef, Injector, inject } from '@angular/core'; +import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; +import { BehaviorSubject, Observable, Subject, combineLatest, merge } from 'rxjs'; +import { map, mergeScan, mergeWith, shareReplay, skipWhile, switchMap, take } from 'rxjs/operators'; + +import { PossiblyAsync, getPossiblyAsyncObservable } from '../async'; +import { filterOperator, progressTo } from '../operators'; + +export class ObservableResourceAction { + constructor(public readonly type: string) {} +} + +const INIT_ACTION = new ObservableResourceAction('init'); + +export interface ObservableResourceOptions { + loader: (params: TParams, acc: TAccResult) => Observable; + map?: (value: TAccResult) => PossiblyAsync; + seed?: TAccResult; + params?: PossiblyAsync; +} + +export class ObservableResource { + protected dr = inject(DestroyRef); + protected injector = inject(Injector); + + protected progress$ = new BehaviorSubject(0); + isLoading$ = this.progress$.pipe(map(Boolean)); + isLoading = toSignal(this.isLoading$, { initialValue: false }); + + protected action$ = new BehaviorSubject(INIT_ACTION); + protected mergedParams$ = new Subject(); + params$ = this.createParams().pipe(takeUntilDestroyed(this.dr), shareReplay(1)); + params = toSignal(this.params$); + + protected accValue$ = this.createAccValue().pipe(takeUntilDestroyed(this.dr), shareReplay(1)); + protected mergedValue$ = new Subject(); + value$ = this.createValue().pipe(takeUntilDestroyed(this.dr), shareReplay(1)); + value = toSignal(this.value$); + + constructor(protected options: ObservableResourceOptions) {} + + protected createParams() { + return merge( + getPossiblyAsyncObservable(this.options.params as never), + this.mergedParams$, + ); + } + + protected createAccValue() { + return combineLatest([this.params$, this.action$]).pipe( + mergeScan( + (acc, [params]) => + this.options.loader(params as TParams, acc).pipe(progressTo(this.progress$)), + this.options.seed as TAccResult, + 1, + ), + ); + } + + protected createValue() { + const mapFn = this.options.map; + return this.accValue$.pipe( + ...filterOperator( + mapFn && switchMap((v: TAccResult) => getPossiblyAsyncObservable(mapFn(v))), + ), + mergeWith(this.mergedValue$), + ); + } + + set(value: TResult) { + this.mergedValue$.next(value); + } + + getFirstValue(): Observable { + return combineLatest([this.value$, this.isLoading$]).pipe( + skipWhile(([_, isLoading]) => isLoading), + map(([value]) => value), + take(1), + ); + } + + setParams(paramsOrParamsFn: TParams | ((prevParams: TParams) => TParams)) { + const newParams = + typeof paramsOrParamsFn === 'function' + ? (paramsOrParamsFn as (prevParams: TParams) => TParams)(this.params() as TParams) + : (paramsOrParamsFn as TParams); + this.mergedParams$.next(newParams); + } + + reload() { + this.setParams((p) => p); + } + + map( + mapFn: (value: TResult) => PossiblyAsync, + sourceValue$ = this.value$, + ): ObservableResource { + const value$ = sourceValue$.pipe( + switchMap((value) => getPossiblyAsyncObservable(mapFn(value))), + shareReplay({ refCount: true, bufferSize: 1 }), + ); + + return { + ...this, + value$, + value: toSignal(value$, { injector: this.injector }), + map: (fn: (value: TResult) => PossiblyAsync) => + this.map(fn, value$ as never), + } as never; + } +} + +export function observableResource( + options: ObservableResourceOptions, +) { + return new ObservableResource(options); +} diff --git a/libs/matez/src/lib/utils/observable-resource/paged-observable-resource.ts b/libs/matez/src/lib/utils/observable-resource/paged-observable-resource.ts new file mode 100644 index 000000000..330293b39 --- /dev/null +++ b/libs/matez/src/lib/utils/observable-resource/paged-observable-resource.ts @@ -0,0 +1,63 @@ +import { toSignal } from '@angular/core/rxjs-interop'; +import { Observable, map } from 'rxjs'; + +import { + ObservableResource, + ObservableResourceAction, + ObservableResourceOptions, +} from './observable-resource'; + +const MORE_ACTION = new ObservableResourceAction('more'); + +class ObservableResourceSetOptionsAction extends ObservableResourceAction { + constructor(public readonly options: PagedObservableResourceLoaderOptions) { + super('options'); + } +} + +export interface PagedObservableResourceAccResult { + result: TItem[]; + continuationToken?: string; +} + +export interface PagedObservableResourceLoaderOptions { + size: number; + continuationToken?: string; +} + +export interface PagedObservableResourceOptions + extends ObservableResourceOptions, TParams, TItem[]>, + Partial { + partialLoader: ( + params: TParams, + options: PagedObservableResourceLoaderOptions, + ) => Observable>; +} + +export class PagedObservableResource extends ObservableResource< + PagedObservableResourceAccResult, + TParams, + TItem[] +> { + hasMore$ = this.accValue$.pipe(map((value) => !!value.continuationToken)); + hasMore = toSignal(this.hasMore$, { initialValue: false }); + + more() { + this.action$.next(MORE_ACTION); + } + + setOptions(options: PagedObservableResourceLoaderOptions) { + this.action$.next(new ObservableResourceSetOptionsAction(options)); + } +} + +export function pagedObservableResource( + options: PagedObservableResourceOptions, +) { + return new PagedObservableResource(options); +} + +export abstract class PagedObservableResourceSuperClass< + TItem, + TParams = void, +> extends PagedObservableResource {} diff --git a/libs/matez/src/lib/utils/operators/filter-operator.ts b/libs/matez/src/lib/utils/operators/filter-operator.ts new file mode 100644 index 000000000..1a08e564b --- /dev/null +++ b/libs/matez/src/lib/utils/operators/filter-operator.ts @@ -0,0 +1,3 @@ +export function filterOperator(...operators: [T | undefined]) { + return operators.filter(Boolean) as [T]; +} diff --git a/libs/matez/src/lib/utils/operators/index.ts b/libs/matez/src/lib/utils/operators/index.ts index c34085595..3e95fc58a 100644 --- a/libs/matez/src/lib/utils/operators/index.ts +++ b/libs/matez/src/lib/utils/operators/index.ts @@ -6,3 +6,5 @@ export * from './switch-combine-with'; export * from './debounce-time-with-first'; export * from './handle-error'; export * from './async-start-with'; +export * from './tap-log'; +export * from './filter-operator'; diff --git a/libs/matez/src/lib/utils/operators/tap-log.ts b/libs/matez/src/lib/utils/operators/tap-log.ts new file mode 100644 index 000000000..bc05a4517 --- /dev/null +++ b/libs/matez/src/lib/utils/operators/tap-log.ts @@ -0,0 +1,7 @@ +import { MonoTypeOperatorFunction, tap } from 'rxjs'; + +export function tapLog(message: string = `Log`): MonoTypeOperatorFunction { + return (source) => { + return source.pipe(tap((value) => console.log(`${message}:`, value))); + }; +} diff --git a/libs/ng-thrift/src/lib/components/thrift-editor/components/complex-form/complex-form.component.ts b/libs/ng-thrift/src/lib/components/thrift-editor/components/complex-form/complex-form.component.ts index 45ed766da..55e2868d2 100644 --- a/libs/ng-thrift/src/lib/components/thrift-editor/components/complex-form/complex-form.component.ts +++ b/libs/ng-thrift/src/lib/components/thrift-editor/components/complex-form/complex-form.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { Component, DestroyRef, Input, OnInit, forwardRef, inject } from '@angular/core'; +import { AfterViewInit, Component, DestroyRef, Input, forwardRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { AbstractControl, @@ -17,6 +17,7 @@ import { PipesModule, createControlProviders, getErrorsTree, + getValueChanges, } from '@vality/matez'; import { ListType, MapType, SetType } from '@vality/thrift-ts'; import { merge } from 'rxjs'; @@ -56,7 +57,7 @@ type ComplexType = T[] | Map | Set; }) export class ComplexFormComponent extends FormComponentSuperclass> - implements OnInit, Validator + implements AfterViewInit, Validator { private destroyRef = inject(DestroyRef); @Input() data!: ThriftData; @@ -80,8 +81,8 @@ export class ComplexFormComponent return undefined; } - override ngOnInit() { - merge(this.valueControls.valueChanges, this.keyControls.valueChanges) + ngAfterViewInit() { + merge(getValueChanges(this.valueControls), getValueChanges(this.keyControls)) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { const values = this.valueControls.value; diff --git a/libs/ng-thrift/src/lib/components/thrift-editor/components/extension-field/extension-field.component.html b/libs/ng-thrift/src/lib/components/thrift-editor/components/extension-field/extension-field.component.html index 1b518fbd9..dfbe85d3f 100644 --- a/libs/ng-thrift/src/lib/components/thrift-editor/components/extension-field/extension-field.component.html +++ b/libs/ng-thrift/src/lib/components/thrift-editor/components/extension-field/extension-field.component.html @@ -13,6 +13,7 @@ [label]=" (extensionResult$ | async)?.label ?? (data.type | fieldLabel: data.field) " + [required]="data.isRequired" style="flex: 1" > @if (!data.isRequired && control.value) { diff --git a/libs/ng-thrift/src/lib/components/thrift-editor/components/struct-form/struct-form.component.ts b/libs/ng-thrift/src/lib/components/thrift-editor/components/struct-form/struct-form.component.ts index 40a7d68a7..cee112959 100644 --- a/libs/ng-thrift/src/lib/components/thrift-editor/components/struct-form/struct-form.component.ts +++ b/libs/ng-thrift/src/lib/components/thrift-editor/components/struct-form/struct-form.component.ts @@ -7,7 +7,7 @@ import { FormGroup, ReactiveFormsModule, ValidationErrors, - Validators, + ValidatorFn, } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { @@ -29,6 +29,10 @@ import { FieldLabelPipe } from '../../pipes/field-label.pipe'; import { ThriftFormExtension } from '../../types/thrift-form-extension'; import { ThriftFormComponent } from '../thrift-form/thrift-form.component'; +const nilValidator: ValidatorFn = (control) => { + return isNil(control.value) ? { required: true } : null; +}; + @Component({ selector: 'v-struct-form', templateUrl: './struct-form.component.html', @@ -89,7 +93,7 @@ export class StructFormComponent validators: isRequiredField( (this.data.ast || []).find((f: Field) => f.name === name), ) - ? [Validators.required] + ? [nilValidator] : [], }) as never, ), diff --git a/libs/ng-thrift/src/lib/components/thrift-editor/components/thrift-form/thrift-form.component.ts b/libs/ng-thrift/src/lib/components/thrift-editor/components/thrift-form/thrift-form.component.ts index 7db904ec5..ff58207db 100644 --- a/libs/ng-thrift/src/lib/components/thrift-editor/components/thrift-form/thrift-form.component.ts +++ b/libs/ng-thrift/src/lib/components/thrift-editor/components/thrift-form/thrift-form.component.ts @@ -6,6 +6,7 @@ import { Input, OnChanges, OnInit, + SimpleChanges, inject, } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -70,7 +71,6 @@ export class ThriftFormComponent private updated$ = new BehaviorSubject(undefined); override ngOnInit() { - super.ngOnInit(); this.extensionResult$ .pipe( map((res) => !!res?.hidden), @@ -80,9 +80,10 @@ export class ThriftFormComponent .subscribe((hidden) => { this.hidden = hidden; }); + return super.ngOnInit(); } - override ngOnChanges() { + override ngOnChanges(changes: SimpleChanges) { if (this.metadata && this.namespace && this.type) { try { this.data = new ThriftData( @@ -98,5 +99,6 @@ export class ThriftFormComponent console.warn(err); } } + return super.ngOnChanges(changes); } } diff --git a/libs/ng-thrift/src/lib/components/thrift-viewer/utils/thrift-view-extension.ts b/libs/ng-thrift/src/lib/components/thrift-viewer/utils/thrift-view-extension.ts index bfeec5973..5bb68a74e 100644 --- a/libs/ng-thrift/src/lib/components/thrift-viewer/utils/thrift-view-extension.ts +++ b/libs/ng-thrift/src/lib/components/thrift-viewer/utils/thrift-view-extension.ts @@ -4,11 +4,11 @@ import { ThriftData } from '../../../models'; import { ThriftViewExtensionResult } from './thrift-view-extension-result'; -export type ThriftViewExtension = { +export type ThriftViewExtension = { determinant: (data: ThriftData, value: unknown) => Observable; extension: ( data: ThriftData, - value: unknown, + value: V, viewValue: unknown, ) => Observable; }; diff --git a/libs/ng-thrift/src/lib/utils/field/get-thrift-object-field-type.ts b/libs/ng-thrift/src/lib/utils/field/get-thrift-object-field-type.ts new file mode 100644 index 000000000..c8fb5370b --- /dev/null +++ b/libs/ng-thrift/src/lib/utils/field/get-thrift-object-field-type.ts @@ -0,0 +1,15 @@ +import { Field, JsonAST, ValueType } from '@vality/thrift-ts'; + +import { ThriftAstMetadata } from '../../types'; + +export function getThriftObjectFieldType( + metadata: ThriftAstMetadata[], + ns: string, + objectType: string, + fieldName: string, +): T { + const nsAst = metadata.find(({ name }) => name === ns)?.ast as JsonAST; + return ( + Array.from(Object.values(nsAst)).find((v) => v[objectType])?.[objectType] as Field[] + ).find((f) => f.name === fieldName)?.type as T; +} diff --git a/libs/ng-thrift/src/lib/utils/field/index.ts b/libs/ng-thrift/src/lib/utils/field/index.ts index 576ee8a7c..c3dfebbf6 100644 --- a/libs/ng-thrift/src/lib/utils/field/index.ts +++ b/libs/ng-thrift/src/lib/utils/field/index.ts @@ -1,2 +1,3 @@ export * from './get-field-label'; export * from './is-required-field'; +export * from './get-thrift-object-field-type'; diff --git a/libs/ng-thrift/src/lib/utils/thrift-type/is-equal-thrift.ts b/libs/ng-thrift/src/lib/utils/thrift-type/is-equal-thrift.ts index a07d029f0..87ae8c28e 100644 --- a/libs/ng-thrift/src/lib/utils/thrift-type/is-equal-thrift.ts +++ b/libs/ng-thrift/src/lib/utils/thrift-type/is-equal-thrift.ts @@ -1,5 +1,5 @@ import { toJson } from '.'; -export function isEqualThrift(a: unknown, b: unknown) { +export function isEqualThrift(a: T, b: T): boolean { return JSON.stringify(toJson(a)) === JSON.stringify(toJson(b)); } diff --git a/libs/ng-thrift/tsconfig.lib.json b/libs/ng-thrift/tsconfig.lib.json index d2605d79d..d1068d521 100644 --- a/libs/ng-thrift/tsconfig.lib.json +++ b/libs/ng-thrift/tsconfig.lib.json @@ -5,8 +5,8 @@ "declaration": true, "declarationMap": true, "inlineSources": true, - "types": [] - // "moduleResolution": "bundler" + "types": [], + "moduleResolution": "bundler" }, "exclude": ["src/**/*.spec.ts", "src/test-setup.ts", "jest.config.ts", "src/**/*.test.ts"], "include": ["src/**/*.ts"] diff --git a/libs/ng-thrift/tsconfig.lib.prod.json b/libs/ng-thrift/tsconfig.lib.prod.json index dcca82db7..7f136dfe9 100644 --- a/libs/ng-thrift/tsconfig.lib.prod.json +++ b/libs/ng-thrift/tsconfig.lib.prod.json @@ -1,8 +1,8 @@ { "extends": "./tsconfig.lib.json", "compilerOptions": { - "declarationMap": false - // "moduleResolution": "bundler" + "declarationMap": false, + "moduleResolution": "bundler" }, "angularCompilerOptions": { "compilationMode": "partial" diff --git a/package-lock.json b/package-lock.json index 7e54691c9..0296c1910 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,23 +24,24 @@ "@angular/router": "20.0.5", "@ngneat/input-mask": "6.0.0", "@s-libs/ng-core": "^20.0.0", - "@vality/deanonimus-proto": "2.0.1-03613cc.0", - "@vality/domain-proto": "2.0.1-35a8abf.0", - "@vality/dominator-proto": "1.0.1-feba5ac.0", - "@vality/fistful-proto": "2.0.1-88e69a5.0", - "@vality/machinegun-proto": "1.0.1-631e6ed.0", - "@vality/magista-proto": "2.0.2-bbdee33.0", - "@vality/repairer-proto": "2.0.2-636fb38.0", - "@vality/scrooge-proto": "0.1.1-d9a7996.0", + "@tailwindcss/postcss": "^4.1.11", + "@vality/domain-proto": "2.0.1-559aadc.0", + "@vality/dominator-proto": "1.0.1-67ea35c.0", + "@vality/fistful-proto": "2.0.1-fe195c5.0", + "@vality/machinegun-proto": "1.0.1-cc2c27c.0", + "@vality/magista-proto": "2.0.2-0e0b068.0", + "@vality/repairer-proto": "2.0.2-1a48729.0", + "@vality/scrooge-proto": "0.1.1-42aba67.0", "@vality/thrift-ts": "2.5.1-2b658f2.0", "@vality/woody": "0.1.3", "date-fns": "^3.6.0", "keycloak-angular": "^20.0.0", - "keycloak-js": "^20.0.5", + "keycloak-js": "^26.2.0", "lodash-es": "^4.17.21", "monaco-editor": "^0.52.2", "rxjs": "7.8.1", "short-uuid": "4.2.2", + "tailwindcss": "^4.1.11", "tslib": "2.3.1", "utility-types": "3.10.0", "zod": "^3.24.4", @@ -65,7 +66,6 @@ "@swc/helpers": "~0.5.11", "@types/jasmine": "4.0.3", "@types/jest": "^29.5.12", - "@types/jwt-decode": "2.2.1", "@types/lodash-es": "^4.17.12", "@types/node": "^22.0.0", "@typescript-eslint/utils": "^8.33.1", @@ -84,10 +84,9 @@ "jest-environment-jsdom": "^29.7.0", "jest-preset-angular": "14.6.0", "jsonc-eslint-parser": "^2.4.0", - "jwt-decode": "2.2.0", "ng-packagr": "20.0.1", "nx": "21.2.1", - "postcss": "^8.4.5", + "postcss": "^8.5.6", "postcss-url": "~10.1.3", "prettier": "^3.5.3", "prettier-plugin-organize-attributes": "^1.0.0", @@ -156,11 +155,22 @@ "dev": true, "license": "MIT" }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -4528,7 +4538,7 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@emnapi/wasi-threads": "1.0.2", @@ -4539,14 +4549,14 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, + "devOptional": true, "license": "0BSD" }, "node_modules/@emnapi/runtime": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "tslib": "^2.4.0" @@ -4556,14 +4566,14 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, + "devOptional": true, "license": "0BSD" }, "node_modules/@emnapi/wasi-threads": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "tslib": "^2.4.0" @@ -4573,7 +4583,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, + "devOptional": true, "license": "0BSD" }, "node_modules/@esbuild/aix-ppc64": { @@ -5731,7 +5741,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.4" @@ -6155,7 +6164,6 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -6170,7 +6178,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -6180,7 +6187,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -6201,14 +6207,12 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -8099,7 +8103,6 @@ "version": "0.2.11", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -11177,6 +11180,335 @@ "@swc/counter": "^0.1.3" } }, + "node_modules/@tailwindcss/node": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.11" + } + }, + "node_modules/@tailwindcss/node/node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-x64": "4.1.11", + "@tailwindcss/oxide-freebsd-x64": "4.1.11", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.11", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz", + "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", + "postcss": "^8.4.41", + "tailwindcss": "4.1.11" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -11269,7 +11601,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "tslib": "^2.4.0" @@ -11279,7 +11611,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, + "devOptional": true, "license": "0BSD" }, "node_modules/@types/babel__core": { @@ -11522,13 +11854,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/jwt-decode": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@types/jwt-decode/-/jwt-decode-2.2.1.tgz", - "integrity": "sha512-aWw2YTtAdT7CskFyxEX2K21/zSDStuf/ikI3yBqmwpwJF0pS+/IX5DWv+1UFffZIbruP6cnT9/LAJV1gFwAT1A==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/lodash": { "version": "4.17.17", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz", @@ -11960,40 +12285,34 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@vality/deanonimus-proto": { - "version": "2.0.1-03613cc.0", - "resolved": "https://registry.npmjs.org/@vality/deanonimus-proto/-/deanonimus-proto-2.0.1-03613cc.0.tgz", - "integrity": "sha512-F+GCYaMkMuBQqVwXxByXD7yw2L78BtdcADGEsOQvXRaBvRo0vJjoZgr6/PHcHJ7yixiKjlD+83JYdytbsh8ENQ==", - "license": "Apache-2.0" - }, "node_modules/@vality/domain-proto": { - "version": "2.0.1-35a8abf.0", - "resolved": "https://registry.npmjs.org/@vality/domain-proto/-/domain-proto-2.0.1-35a8abf.0.tgz", - "integrity": "sha512-5zRd5bgclVt02HXRjOk8LhBQpEKAPAVG4MvNYKxYf/K0wQeQGAewqNub/5VKG+ovZkpHRLiAjto4XsimCrQL6w==", + "version": "2.0.1-559aadc.0", + "resolved": "https://registry.npmjs.org/@vality/domain-proto/-/domain-proto-2.0.1-559aadc.0.tgz", + "integrity": "sha512-/K8XjPacIJyxaD4n+v4SXDckIJoc1L9pu1YMQDHViBbHBwyuv+P2liwBsKcKnW5Ubf7DewAw4uHRBy12UFuVFA==", "license": "Apache-2.0" }, "node_modules/@vality/dominator-proto": { - "version": "1.0.1-feba5ac.0", - "resolved": "https://registry.npmjs.org/@vality/dominator-proto/-/dominator-proto-1.0.1-feba5ac.0.tgz", - "integrity": "sha512-6JpGbVw4s66icAYpbezLUhvmG5rHep2mOwwnpGBKtWE8CIGE9vmq25V3UxRSXlA9/ao1+zpRYYj5/7HpiZE2QA==", + "version": "1.0.1-67ea35c.0", + "resolved": "https://registry.npmjs.org/@vality/dominator-proto/-/dominator-proto-1.0.1-67ea35c.0.tgz", + "integrity": "sha512-xxFbBHnaPriJ8LJUZ5LtRjVNdAHaKr9Ig8UrRXBmW9hkEstYVRhWm5PH97c2U0cyNxd4Htbwv2dJotrevBFFAA==", "license": "Apache-2.0" }, "node_modules/@vality/fistful-proto": { - "version": "2.0.1-88e69a5.0", - "resolved": "https://registry.npmjs.org/@vality/fistful-proto/-/fistful-proto-2.0.1-88e69a5.0.tgz", - "integrity": "sha512-fsMqJbOZa2GesbP2OEmPlyPfV236VN+JRxke7HO79ujC4i8TwHeejV55DTfUbrFO7Q/d2Wwj8BtMcQF/c4KYGA==", + "version": "2.0.1-fe195c5.0", + "resolved": "https://registry.npmjs.org/@vality/fistful-proto/-/fistful-proto-2.0.1-fe195c5.0.tgz", + "integrity": "sha512-tLkVRZMfmkAnCbiTW20blnXUm9RGpNdXobz3MKPfrCdkuJxue+hd/dgR4FNZrIs+EPnPl337dda+yDmR/m3J+Q==", "license": "Apache-2.0" }, "node_modules/@vality/machinegun-proto": { - "version": "1.0.1-631e6ed.0", - "resolved": "https://registry.npmjs.org/@vality/machinegun-proto/-/machinegun-proto-1.0.1-631e6ed.0.tgz", - "integrity": "sha512-dnpDLlZ3NpWqJ7qXGAOkKKHAI905YsnBa9N13KnyYL5weQrvylficTmo6Vhc9N/4dM6f9Evm1oFuw8R5BmXUgA==", + "version": "1.0.1-cc2c27c.0", + "resolved": "https://registry.npmjs.org/@vality/machinegun-proto/-/machinegun-proto-1.0.1-cc2c27c.0.tgz", + "integrity": "sha512-EP7F4O8XEsziDvgfMHp9pbOVek6tjs8SPZNR+BYsoxjwLvRKWpDA6N0FTzC6WtNXGUC8cC5D70AURtKjRTuzAw==", "license": "Apache-2.0" }, "node_modules/@vality/magista-proto": { - "version": "2.0.2-bbdee33.0", - "resolved": "https://registry.npmjs.org/@vality/magista-proto/-/magista-proto-2.0.2-bbdee33.0.tgz", - "integrity": "sha512-fpzESaJLvIdLMtuwBQ8D7bOS6JiZur204XB3M9ux7CbuPyd0eNJ51O8d8xPPsJbCQ+FmYM5rWWJcUgrqaT645Q==", + "version": "2.0.2-0e0b068.0", + "resolved": "https://registry.npmjs.org/@vality/magista-proto/-/magista-proto-2.0.2-0e0b068.0.tgz", + "integrity": "sha512-pRn/rUMx8URnSfqbi8GBQ+EbuoRHp+5/F2d/hG/rcs6xmiXmFt9OCHFwruT45Vvu555YclBkDIvuajYry3USqA==", "license": "Apache-2.0" }, "node_modules/@vality/matez": { @@ -12005,15 +12324,15 @@ "link": true }, "node_modules/@vality/repairer-proto": { - "version": "2.0.2-636fb38.0", - "resolved": "https://registry.npmjs.org/@vality/repairer-proto/-/repairer-proto-2.0.2-636fb38.0.tgz", - "integrity": "sha512-yAOQopZzKvL3Q5fCx2n+DcUyDXs5PuSOWyQ9PfUvw/s2sgpBa97hlzkmQATBoGVCeUC/85MCRd7xJuhUNY8xOA==", + "version": "2.0.2-1a48729.0", + "resolved": "https://registry.npmjs.org/@vality/repairer-proto/-/repairer-proto-2.0.2-1a48729.0.tgz", + "integrity": "sha512-70rIcVJ9Wh4pm9mLwsRuVdHa7HA/O/4PmnlUzwLZcB/p1X1a+eI9GRtwKFeqJ70LsIuSZEBjMDBOaLTfeV5Qrg==", "license": "Apache-2.0" }, "node_modules/@vality/scrooge-proto": { - "version": "0.1.1-d9a7996.0", - "resolved": "https://registry.npmjs.org/@vality/scrooge-proto/-/scrooge-proto-0.1.1-d9a7996.0.tgz", - "integrity": "sha512-gL/OdRYGw8O8t50Nv2Z4KKQxjvIjHhkYr7emlbqqRgSHRjJeVzAq4GEEqjG8ULlOSOmGKyE1QL9AaZoqHg4Rmw==", + "version": "0.1.1-42aba67.0", + "resolved": "https://registry.npmjs.org/@vality/scrooge-proto/-/scrooge-proto-0.1.1-42aba67.0.tgz", + "integrity": "sha512-oHrj2ARgY3P8sLRzDzWmDEfkJYbJ0EKy5ZydHJm+TLe043UUbK/Obm9hAqzoT5RBdmJlcU/8+GPK5hKo5vCMNg==", "license": "Apache-2.0" }, "node_modules/@vality/thrift-ts": { @@ -17105,7 +17424,6 @@ "version": "5.18.2", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", - "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -19519,7 +19837,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -22162,12 +22479,6 @@ "jiti": "bin/jiti.js" } }, - "node_modules/js-sha256": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", - "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==", - "license": "MIT" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -22476,13 +22787,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/jwt-decode": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", - "integrity": "sha512-86GgN2vzfUu7m9Wcj63iUkuDzFNYFVmjeDm2GzWpUk+opB0pEpMsw6ePCMrhYkumz2C1ihqtZzOMAg7FiXcNoQ==", - "dev": true, - "license": "MIT" - }, "node_modules/karma-source-map-support": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", @@ -22510,14 +22814,10 @@ } }, "node_modules/keycloak-js": { - "version": "20.0.5", - "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-20.0.5.tgz", - "integrity": "sha512-7+M5Uni4oNlAmbjM/lDJzFHu2+PGqU6/bvmTBuQssE1fJ7ZyNeCRHgFoaVfFpIU3m6aAFwPUko4lVcn4kPXP5Q==", - "license": "Apache-2.0", - "dependencies": { - "base64-js": "^1.5.1", - "js-sha256": "^0.9.0" - } + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-26.2.0.tgz", + "integrity": "sha512-CrFcXTN+d6J0V/1v3Zpioys6qHNWE6yUzVVIsCUAmFn9H14GZ0vuYod+lt+SSpMgWGPuneDZBSGBAeLBFuqjsw==", + "license": "Apache-2.0" }, "node_modules/keygrip": { "version": "1.1.0", @@ -22856,6 +23156,243 @@ } } }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -23384,7 +23921,6 @@ "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -23708,7 +24244,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -23848,7 +24383,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.1.2" @@ -23955,7 +24489,6 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -25747,7 +26280,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -25942,10 +26474,9 @@ } }, "node_modules/postcss": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz", - "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==", - "dev": true, + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -28733,7 +29264,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -29500,11 +30030,16 @@ "node": ">=16.0.0" } }, + "node_modules/tailwindcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "license": "MIT" + }, "node_modules/tapable": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" diff --git a/package.json b/package.json index 7a6e47da4..dfcac4247 100644 --- a/package.json +++ b/package.json @@ -32,23 +32,24 @@ "@angular/router": "20.0.5", "@ngneat/input-mask": "6.0.0", "@s-libs/ng-core": "^20.0.0", - "@vality/deanonimus-proto": "2.0.1-03613cc.0", - "@vality/domain-proto": "2.0.1-35a8abf.0", - "@vality/dominator-proto": "1.0.1-feba5ac.0", - "@vality/fistful-proto": "2.0.1-88e69a5.0", - "@vality/machinegun-proto": "1.0.1-631e6ed.0", - "@vality/magista-proto": "2.0.2-bbdee33.0", - "@vality/repairer-proto": "2.0.2-636fb38.0", - "@vality/scrooge-proto": "0.1.1-d9a7996.0", + "@tailwindcss/postcss": "^4.1.11", + "@vality/domain-proto": "2.0.1-559aadc.0", + "@vality/dominator-proto": "1.0.1-67ea35c.0", + "@vality/fistful-proto": "2.0.1-fe195c5.0", + "@vality/machinegun-proto": "1.0.1-cc2c27c.0", + "@vality/magista-proto": "2.0.2-0e0b068.0", + "@vality/repairer-proto": "2.0.2-1a48729.0", + "@vality/scrooge-proto": "0.1.1-42aba67.0", "@vality/thrift-ts": "2.5.1-2b658f2.0", "@vality/woody": "0.1.3", "date-fns": "^3.6.0", "keycloak-angular": "^20.0.0", - "keycloak-js": "^20.0.5", + "keycloak-js": "^26.2.0", "lodash-es": "^4.17.21", "monaco-editor": "^0.52.2", "rxjs": "7.8.1", "short-uuid": "4.2.2", + "tailwindcss": "^4.1.11", "tslib": "2.3.1", "utility-types": "3.10.0", "zod": "^3.24.4", @@ -73,7 +74,6 @@ "@swc/helpers": "~0.5.11", "@types/jasmine": "4.0.3", "@types/jest": "^29.5.12", - "@types/jwt-decode": "2.2.1", "@types/lodash-es": "^4.17.12", "@types/node": "^22.0.0", "@typescript-eslint/utils": "^8.33.1", @@ -92,10 +92,9 @@ "jest-environment-jsdom": "^29.7.0", "jest-preset-angular": "14.6.0", "jsonc-eslint-parser": "^2.4.0", - "jwt-decode": "2.2.0", "ng-packagr": "20.0.1", "nx": "21.2.1", - "postcss": "^8.4.5", + "postcss": "^8.5.6", "postcss-url": "~10.1.3", "prettier": "^3.5.3", "prettier-plugin-organize-attributes": "^1.0.0", diff --git a/tools/generators/.gitkeep b/tools/generators/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/tsconfig.base.json b/tsconfig.base.json index d61048a9d..e4e607f17 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -4,7 +4,7 @@ "rootDir": ".", "sourceMap": true, "declaration": false, - "moduleResolution": "node", + "moduleResolution": "bundler", "emitDecoratorMetadata": true, "experimentalDecorators": true, "importHelpers": true,