diff --git a/src/app/api/claims/claims.service.ts b/src/app/api/claims/claims.service.ts index 6432bff0..0c5d4243 100644 --- a/src/app/api/claims/claims.service.ts +++ b/src/app/api/claims/claims.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { IdGeneratorService } from '@rbkmoney/id-generator'; import { Observable } from 'rxjs'; -import { first, switchMap } from 'rxjs/operators'; import { Claim, @@ -11,105 +10,71 @@ import { Reason, StatusModificationUnit, } from '@dsh/api-codegen/claim-management'; -import { ContextService } from '@dsh/app/shared/services/context'; import { mapResult, noContinuationToken } from '@dsh/operators'; export const CLAIM_STATUS = StatusModificationUnit.StatusEnum; @Injectable() export class ClaimsService { - constructor( - private claimsService: APIClaimsService, - private idGenerator: IdGeneratorService, - private contextService: ContextService - ) {} + constructor(private claimsService: APIClaimsService, private idGenerator: IdGeneratorService) {} searchClaims( + partyId: string, limit: number, claimStatuses?: StatusModificationUnit.StatusEnum[], claimID?: number, continuationToken?: string ): Observable { - return this.contextService.organization$.pipe( - first(), - switchMap((organization) => - this.claimsService.searchClaims( - this.idGenerator.shortUuid(), - organization.id, - limit, - undefined, - continuationToken, - claimID, - claimStatuses || Object.values(StatusModificationUnit.StatusEnum) - ) - ) + return this.claimsService.searchClaims( + this.idGenerator.shortUuid(), + partyId, + limit, + undefined, + continuationToken, + claimID, + claimStatuses || Object.values(StatusModificationUnit.StatusEnum) ); } - search1000Claims(claimStatuses?: StatusModificationUnit.StatusEnum[]): Observable { - return this.searchClaims(1000, claimStatuses).pipe(noContinuationToken, mapResult); + search1000Claims(partyId: string, claimStatuses?: StatusModificationUnit.StatusEnum[]): Observable { + return this.searchClaims(partyId, 1000, claimStatuses).pipe(noContinuationToken, mapResult); } - getClaimByID(claimID: number): Observable { - return this.contextService.organization$.pipe( - first(), - switchMap((organization) => - this.claimsService.getClaimByID(this.idGenerator.shortUuid(), organization.id, claimID) - ) + getClaimByID(partyId: string, claimID: number): Observable { + return this.claimsService.getClaimByID(this.idGenerator.shortUuid(), partyId, claimID); + } + + createClaim(partyId: string, changeset: Modification[]): Observable { + return this.claimsService.createClaim(this.idGenerator.shortUuid(), partyId, changeset); + } + + updateClaimByID( + partyId: string, + claimID: number, + claimRevision: number, + changeset: Modification[] + ): Observable { + return this.claimsService.updateClaimByID( + this.idGenerator.shortUuid(), + partyId, + claimID, + claimRevision, + changeset ); } - createClaim(changeset: Modification[]): Observable { - return this.contextService.organization$.pipe( - first(), - switchMap((organization) => - this.claimsService.createClaim(this.idGenerator.shortUuid(), organization.id, changeset) - ) + revokeClaimByID(partyId: string, claimID: number, claimRevision: number, reason: Reason): Observable { + return this.claimsService.revokeClaimByID( + this.idGenerator.shortUuid(), + partyId, + claimID, + claimRevision, + undefined, + reason ); } - updateClaimByID(claimID: number, claimRevision: number, changeset: Modification[]): Observable { - return this.contextService.organization$.pipe( - first(), - switchMap((organization) => - this.claimsService.updateClaimByID( - this.idGenerator.shortUuid(), - organization.id, - claimID, - claimRevision, - changeset - ) - ) - ); - } - - revokeClaimByID(claimID: number, claimRevision: number, reason: Reason): Observable { - return this.contextService.organization$.pipe( - first(), - switchMap((organization) => - this.claimsService.revokeClaimByID( - this.idGenerator.shortUuid(), - organization.id, - claimID, - claimRevision, - undefined, - reason - ) - ) - ); - } - - requestReviewClaimByID(claimID: number, claimRevision: number): Observable { - return this.contextService.organization$.pipe( - first(), - switchMap((organization) => - this.claimsService.requestReviewClaimByID( - this.idGenerator.shortUuid(), - organization.id, - claimID, - claimRevision - ) - ) - ); + requestReviewClaimByID(partyId: string, claimID: number, claimRevision: number): Observable { + return this.claimsService.requestReviewClaimByID(this.idGenerator.shortUuid(), partyId, claimID, claimRevision); } } diff --git a/src/app/app.component.html b/src/app/app.component.html index f017d491..734ad2aa 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,5 +1,2 @@ - - - - + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3639fc87..4f2ba0bf 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,31 +1,12 @@ -import { Component, Inject, OnInit } from '@angular/core'; -import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import * as Sentry from '@sentry/angular'; -import { first } from 'rxjs/operators'; +import { Component, Inject, ChangeDetectionStrategy } from '@angular/core'; import { ENV, Env } from '../environments'; -import { BootstrapService } from './bootstrap.service'; -import { KeycloakTokenInfoService } from './shared'; -@UntilDestroy() @Component({ selector: 'dsh-root', templateUrl: 'app.component.html', - providers: [BootstrapService], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AppComponent implements OnInit { - bootstrapped$ = this.bootstrapService.bootstrapped$; - - constructor( - private bootstrapService: BootstrapService, - @Inject(ENV) public env: Env, - private keycloakTokenInfoService: KeycloakTokenInfoService - ) {} - - ngOnInit(): void { - this.bootstrapService.bootstrap(); - this.keycloakTokenInfoService.partyID$ - .pipe(first(), untilDestroyed(this)) - .subscribe((partyID) => Sentry.setUser({ id: partyID })); - } +export class AppComponent { + constructor(@Inject(ENV) public env: Env) {} } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9d144002..adfc1cf0 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -16,7 +16,6 @@ import { TRANSLOCO_CONFIG, TRANSLOCO_LOADER, TranslocoConfig, TranslocoModule } import * as Sentry from '@sentry/angular'; import { ErrorModule, KeycloakTokenInfoModule } from '@dsh/app/shared/services'; -import { ContextModule } from '@dsh/app/shared/services/context'; import { QUERY_PARAMS_SERIALIZERS } from '@dsh/app/shared/services/query-params/utils/query-params-serializers'; import { createDateRangeWithPresetSerializer } from '@dsh/components/filters/date-range-filter'; import { SELECT_SEARCH_FIELD_OPTIONS } from '@dsh/components/form-controls/select-search-field'; @@ -41,7 +40,6 @@ import { TranslocoHttpLoaderService } from './transloco-http-loader.service'; import { YandexMetrikaConfigService, YandexMetrikaModule } from './yandex-metrika'; @NgModule({ - declarations: [AppComponent], imports: [ CommonModule, BrowserModule, @@ -63,7 +61,6 @@ import { YandexMetrikaConfigService, YandexMetrikaModule } from './yandex-metrik IconsModule, KeycloakTokenInfoModule, FlexLayoutModule, - ContextModule, ], providers: [ LanguageService, @@ -133,6 +130,7 @@ import { YandexMetrikaConfigService, YandexMetrikaModule } from './yandex-metrik useValue: [createDateRangeWithPresetSerializer()], }, ], + declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule {} diff --git a/src/app/bootstrap.service.ts b/src/app/bootstrap.service.ts index 78d97ae0..fdc03bcf 100644 --- a/src/app/bootstrap.service.ts +++ b/src/app/bootstrap.service.ts @@ -81,16 +81,21 @@ export class BootstrapService { } private createTestShop(): Observable { - return this.claimsService - .createClaim( - createTestShopClaimChangeset( - this.idGenerator.uuid(), - this.idGenerator.uuid(), - this.idGenerator.uuid(), - this.idGenerator.uuid() + return this.contextService.organization$.pipe( + first(), + switchMap((org) => + this.claimsService.createClaim( + org.id, + createTestShopClaimChangeset( + this.idGenerator.uuid(), + this.idGenerator.uuid(), + this.idGenerator.uuid(), + this.idGenerator.uuid() + ) ) - ) - .pipe(mapTo(true)); + ), + mapTo(true) + ); } private initContext(): Observable { diff --git a/src/app/home/actionbar/components/select-active-organization-dialog/select-active-organization-dialog.component.html b/src/app/home/actionbar/components/select-active-organization-dialog/select-active-organization-dialog.component.html index edf95231..77333cde 100644 --- a/src/app/home/actionbar/components/select-active-organization-dialog/select-active-organization-dialog.component.html +++ b/src/app/home/actionbar/components/select-active-organization-dialog/select-active-organization-dialog.component.html @@ -18,7 +18,7 @@ dsh-button color="accent" (click)="confirm()" - [disabled]="!selectedOrganization || selectedOrganization.id === (contextOrganization$ | async)?.id" + [disabled]="!selectedOrganization || selectedOrganization.id === organization.id" > {{ t('confirm') }} diff --git a/src/app/home/actionbar/components/select-active-organization-dialog/select-active-organization-dialog.component.ts b/src/app/home/actionbar/components/select-active-organization-dialog/select-active-organization-dialog.component.ts index 69850cc2..c2ac2907 100644 --- a/src/app/home/actionbar/components/select-active-organization-dialog/select-active-organization-dialog.component.ts +++ b/src/app/home/actionbar/components/select-active-organization-dialog/select-active-organization-dialog.component.ts @@ -1,14 +1,11 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { MatDialogRef } from '@angular/material/dialog'; -import { Router } from '@angular/router'; +import { ChangeDetectionStrategy, Component, OnInit, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy'; -import { combineLatest } from 'rxjs'; import { first, map } from 'rxjs/operators'; import { Organization } from '@dsh/api-codegen/organizations'; import { SEARCH_LIMIT } from '@dsh/app/sections/tokens'; import { BaseDialogResponseStatus } from '@dsh/app/shared/components/dialog/base-dialog'; -import { ContextService } from '@dsh/app/shared/services/context'; import { FetchOrganizationsService } from '@dsh/app/shared/services/fetch-organizations'; const DISPLAYED_COUNT = 5; @@ -26,24 +23,22 @@ export class SelectActiveOrganizationDialogComponent implements OnInit { displayedCount = DISPLAYED_COUNT; selectedOrganization: Organization; isLoading$ = this.fetchOrganizationsService.doAction$; - contextOrganization$ = this.contextService.organization$; constructor( private dialogRef: MatDialogRef< SelectActiveOrganizationDialogComponent, BaseDialogResponseStatus | Organization >, - private fetchOrganizationsService: FetchOrganizationsService, - private router: Router, - private contextService: ContextService + @Inject(MAT_DIALOG_DATA) public organization: Organization, + private fetchOrganizationsService: FetchOrganizationsService ) {} ngOnInit(): void { this.fetchOrganizationsService.search(); - combineLatest([this.organizations$, this.contextService.organization$]) + this.organizations$ .pipe( first(), - map(([orgs, activeOrg]) => orgs.find((org) => org.id === activeOrg.id)), + map((organizations) => organizations.find((org) => org.id === this.organization.id)), untilDestroyed(this) ) .subscribe((organization) => (this.selectedOrganization = organization)); @@ -51,7 +46,6 @@ export class SelectActiveOrganizationDialogComponent implements OnInit { confirm(): void { this.dialogRef.close(this.selectedOrganization); - void this.router.navigate(['/']); } close(): void { diff --git a/src/app/home/actionbar/components/user/user.component.ts b/src/app/home/actionbar/components/user/user.component.ts index 44ad5f67..ac8b1066 100644 --- a/src/app/home/actionbar/components/user/user.component.ts +++ b/src/app/home/actionbar/components/user/user.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, Output, EventEmitter, Inject } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; -import { filter } from 'rxjs/operators'; +import { filter, first, switchMap } from 'rxjs/operators'; import { Organization } from '@dsh/api-codegen/organizations'; import { DIALOG_CONFIG, DialogConfig } from '@dsh/app/sections/tokens'; @@ -57,13 +57,20 @@ export class UserComponent { selectActiveOrg(): void { this.selected.emit(); - this.dialog - .open( - SelectActiveOrganizationDialogComponent, - this.dialogConfig.medium + this.contextService.organization$ + .pipe( + first(), + switchMap((organization) => + this.dialog + .open< + SelectActiveOrganizationDialogComponent, + Organization, + BaseDialogResponseStatus | Organization + >(SelectActiveOrganizationDialogComponent, { ...this.dialogConfig.medium, data: organization }) + .afterClosed() + ), + filter((res) => !Object.values(BaseDialogResponseStatus).includes(res as BaseDialogResponseStatus)) ) - .afterClosed() - .pipe(filter((res) => !Object.values(BaseDialogResponseStatus).includes(res as BaseDialogResponseStatus))) .subscribe((org: Organization) => { this.contextService.switchOrganization(org.id); }); diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index aa13d2fe..3fa77720 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -1,20 +1,18 @@ -
- - -
+ + - + - + diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index e4e9ea91..01a10745 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -1,9 +1,10 @@ import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; -import { Component, OnInit } from '@angular/core'; -import { NavigationEnd, Router, RouterEvent } from '@angular/router'; -import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { Observable } from 'rxjs'; -import { filter, map, pluck, take } from 'rxjs/operators'; +import { Component } from '@angular/core'; +import { Router, ActivatedRoute, NavigationEnd } from '@angular/router'; +import { UntilDestroy } from '@ngneat/until-destroy'; +import { pluck, filter, map, startWith, distinctUntilChanged } from 'rxjs/operators'; + +import { shareReplayRefCount } from '@dsh/operators'; import { ThemeManager } from '../theme-manager'; @@ -12,34 +13,25 @@ import { ThemeManager } from '../theme-manager'; selector: 'dsh-home', templateUrl: 'home.component.html', }) -export class HomeComponent implements OnInit { - routerNavigationEnd$: Observable; - isXSmallSmall$: Observable; +export class HomeComponent { + isXSmallSmall$ = this.breakpointObserver.observe([Breakpoints.XSmall, Breakpoints.Small]).pipe(pluck('matches')); + hasBackground$ = this.router.events.pipe( + filter((event) => event instanceof NavigationEnd), + startWith(null), + map(() => /^\/organization\/[\w-]+$/.test(this.router.url) && this.themeManager.isMainBackgroundImages), + distinctUntilChanged(), + shareReplayRefCount() + ); constructor( private router: Router, + private route: ActivatedRoute, // need to create class when home component was init private themeManager: ThemeManager, private breakpointObserver: BreakpointObserver ) {} - get hasBackground(): boolean { - return this.router.url === '/' && this.themeManager.isMainBackgroundImages; - } - get logoName(): string { return this.themeManager.logoName; } - - ngOnInit(): void { - this.routerNavigationEnd$ = this.router.events.pipe( - filter((event: RouterEvent) => event instanceof NavigationEnd), - map(() => true), - take(1), - untilDestroyed(this) - ); - this.isXSmallSmall$ = this.breakpointObserver - .observe([Breakpoints.XSmall, Breakpoints.Small]) - .pipe(pluck('matches')); - } } diff --git a/src/app/home/toolbar/toolbar.component.html b/src/app/home/toolbar/toolbar.component.html index df92fc31..ecf45dd0 100644 --- a/src/app/home/toolbar/toolbar.component.html +++ b/src/app/home/toolbar/toolbar.component.html @@ -11,7 +11,7 @@ this.routeParamClaimService.claim$), - switchMap(({ id, revision }) => - this.claimsApiService.requestReviewClaimByID(id, revision).pipe( + withLatestFrom(this.contextService.organization$), + switchMap(([{ id, revision }, org]) => + this.claimsApiService.requestReviewClaimByID(org.id, id, revision).pipe( catchError((ex) => { this.progress$.next(false); console.error(ex); diff --git a/src/app/sections/claim-section/claim/revoke-claim-dialog/revoke-claim-dialog.service.ts b/src/app/sections/claim-section/claim/revoke-claim-dialog/revoke-claim-dialog.service.ts index 8d256a16..d56f76d3 100644 --- a/src/app/sections/claim-section/claim/revoke-claim-dialog/revoke-claim-dialog.service.ts +++ b/src/app/sections/claim-section/claim/revoke-claim-dialog/revoke-claim-dialog.service.ts @@ -4,9 +4,10 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { progress } from '@rbkmoney/utils'; import get from 'lodash-es/get'; import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; -import { catchError, filter, pluck, switchMap, tap } from 'rxjs/operators'; +import { catchError, filter, pluck, switchMap, tap, withLatestFrom } from 'rxjs/operators'; import { ClaimsService } from '@dsh/api/claims'; +import { ContextService } from '@dsh/app/shared/services/context'; import { UiError } from '../../../ui-error'; import { RevokeClaimDialogComponent } from './revoke-claim-dialog.component'; @@ -27,7 +28,8 @@ export class RevokeClaimDialogService { private dialogRef: MatDialogRef, private claimsApiService: ClaimsService, private fb: FormBuilder, - @Inject(MAT_DIALOG_DATA) private data: { claimId: number; revision: number } + @Inject(MAT_DIALOG_DATA) private data: { claimId: number; revision: number }, + private contextService: ContextService ) { this.form = this.fb.group({ reason: ['', [Validators.required, Validators.maxLength(1000)]], @@ -35,8 +37,9 @@ export class RevokeClaimDialogService { this.revoke$ .pipe( tap(() => this.error$.next({ hasError: false })), - switchMap((reason) => - this.claimsApiService.revokeClaimByID(this.data.claimId, this.data.revision, reason).pipe( + withLatestFrom(this.contextService.organization$), + switchMap(([reason, org]) => + this.claimsApiService.revokeClaimByID(org.id, this.data.claimId, this.data.revision, reason).pipe( catchError((ex) => { console.error(ex); const error = { hasError: true, code: 'revokeClaimByIDFailed' }; diff --git a/src/app/sections/claim-section/claim/route-param-claim.service.ts b/src/app/sections/claim-section/claim/route-param-claim.service.ts index 13ed88f6..dc42f979 100644 --- a/src/app/sections/claim-section/claim/route-param-claim.service.ts +++ b/src/app/sections/claim-section/claim/route-param-claim.service.ts @@ -1,15 +1,20 @@ import { Injectable } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; +import { combineLatest } from 'rxjs'; import { pluck, switchMap } from 'rxjs/operators'; import { ClaimsService } from '@dsh/api/claims'; +import { ContextService } from '@dsh/app/shared/services/context'; @Injectable() export class RouteParamClaimService { - claim$ = this.route.params.pipe( - pluck('claimId'), - switchMap((id) => this.claimsService.getClaimByID(id)) + claim$ = combineLatest([this.contextService.organization$, this.route.params.pipe(pluck('claimId'))]).pipe( + switchMap(([org, id]) => this.claimsService.getClaimByID(org.id, id)) ); - constructor(private route: ActivatedRoute, private claimsService: ClaimsService) {} + constructor( + private route: ActivatedRoute, + private claimsService: ClaimsService, + private contextService: ContextService + ) {} } diff --git a/src/app/sections/claim-section/claim/update-claim/update-claim.service.ts b/src/app/sections/claim-section/claim/update-claim/update-claim.service.ts index bd4843ff..776b157b 100644 --- a/src/app/sections/claim-section/claim/update-claim/update-claim.service.ts +++ b/src/app/sections/claim-section/claim/update-claim/update-claim.service.ts @@ -1,12 +1,13 @@ import { Injectable } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { TranslocoService } from '@ngneat/transloco'; -import { BehaviorSubject, combineLatest, Observable, of, Subject } from 'rxjs'; -import { catchError, filter, pluck, share, switchMap, tap } from 'rxjs/operators'; +import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; +import { catchError, filter, pluck, share, switchMap, tap, withLatestFrom } from 'rxjs/operators'; import { FileModification } from '@dsh/api-codegen/claim-management'; import { Conversation } from '@dsh/api-codegen/messages'; import { ClaimsService } from '@dsh/api/claims'; +import { ContextService } from '@dsh/app/shared/services/context'; import { progress } from '../../../../custom-operators'; import { UiError } from '../../../ui-error'; @@ -33,14 +34,15 @@ export class UpdateClaimService { private routeParamClaimService: RouteParamClaimService, private claimApiService: ClaimsService, private snackBar: MatSnackBar, - private transloco: TranslocoService + private transloco: TranslocoService, + private contextService: ContextService ) { const updated$ = this.updateBy$.pipe( tap(() => this.error$.next({ hasError: false })), toChangeset, - switchMap((changeset) => combineLatest([of(changeset), this.routeParamClaimService.claim$])), - switchMap(([changeset, { id, revision }]) => - this.claimApiService.updateClaimByID(id, revision, changeset).pipe( + withLatestFrom(this.routeParamClaimService.claim$, this.contextService.organization$), + switchMap(([changeset, { id, revision }, org]) => + this.claimApiService.updateClaimByID(org.id, id, revision, changeset).pipe( catchError((ex) => { console.error(ex); const error = { hasError: true, code: 'updateClaimByIDFailed' }; diff --git a/src/app/sections/claim-section/claims/services/fetch-claims/fetch-claims.service.ts b/src/app/sections/claim-section/claims/services/fetch-claims/fetch-claims.service.ts index ef833c2b..078a3c97 100644 --- a/src/app/sections/claim-section/claims/services/fetch-claims/fetch-claims.service.ts +++ b/src/app/sections/claim-section/claims/services/fetch-claims/fetch-claims.service.ts @@ -3,10 +3,11 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { TranslocoService } from '@ngneat/transloco'; import { FetchResult, PartialFetcher } from '@rbkmoney/partial-fetcher'; import { Observable } from 'rxjs'; -import { shareReplay } from 'rxjs/operators'; +import { shareReplay, first, switchMap } from 'rxjs/operators'; import { Claim } from '@dsh/api-codegen/claim-management/swagger-codegen'; import { ClaimsService } from '@dsh/api/claims'; +import { ContextService } from '@dsh/app/shared/services/context'; import { booleanDebounceTime, mapToTimestamp } from '@dsh/operators'; import { ClaimsSearchFiltersSearchParams } from '../../claims-search-filters/claims-search-filters-search-params'; @@ -21,7 +22,8 @@ export class FetchClaimsService extends PartialFetcher { @@ -34,11 +36,17 @@ export class FetchClaimsService extends PartialFetcher> { - return this.claimsService.searchClaims( - this.searchLimit, - params.claimStatuses, - params.claimID, - continuationToken + return this.contextService.organization$.pipe( + first(), + switchMap(({ id }) => + this.claimsService.searchClaims( + id, + this.searchLimit, + params.claimStatuses, + params.claimID, + continuationToken + ) + ) ); } } diff --git a/src/app/sections/claim-section/onboarding/company-search/company-search.service.ts b/src/app/sections/claim-section/onboarding/company-search/company-search.service.ts index bee6b2f9..ce7b221b 100644 --- a/src/app/sections/claim-section/onboarding/company-search/company-search.service.ts +++ b/src/app/sections/claim-section/onboarding/company-search/company-search.service.ts @@ -7,8 +7,8 @@ import { TranslocoService } from '@ngneat/transloco'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { IdGeneratorService } from '@rbkmoney/id-generator'; import isNil from 'lodash-es/isNil'; -import { combineLatest, Observable, of, Subject, throwError } from 'rxjs'; -import { catchError, filter, map, mapTo, pluck, switchMap } from 'rxjs/operators'; +import { combineLatest, Observable, of, Subject, throwError, EMPTY } from 'rxjs'; +import { catchError, filter, map, mapTo, pluck, switchMap, withLatestFrom } from 'rxjs/operators'; import { OrgType, PartyContent, ReqResponse } from '@dsh/api-codegen/aggr-proxy'; import { Claim } from '@dsh/api-codegen/claim-management'; @@ -21,6 +21,7 @@ import { } from '@dsh/api/claims'; import { KonturFocusService } from '@dsh/api/kontur-focus'; import { QuestionaryService } from '@dsh/api/questionary'; +import { ContextService } from '@dsh/app/shared/services/context'; import { ConfirmActionDialogComponent } from '@dsh/components/popups'; import { shareReplayRefCount } from '@dsh/operators'; @@ -39,11 +40,11 @@ export class CompanySearchService { switchMap(({ claimID }) => of(isNil(claimID) ? null : Number(claimID))), shareReplayRefCount() ); - private claim$ = this.claimID$.pipe( - switchMap((claimID) => + private claim$ = combineLatest([this.contextService.organization$, this.claimID$]).pipe( + switchMap(([org, claimID]) => isNil(claimID) ? of(null) - : this.claimsService.getClaimByID(claimID).pipe(catchError(() => of(null))) + : this.claimsService.getClaimByID(org.id, claimID).pipe(catchError(() => of(null))) ), shareReplayRefCount() ); @@ -59,7 +60,8 @@ export class CompanySearchService { private konturFocusService: KonturFocusService, private keycloakService: KeycloakService, private idGenerator: IdGeneratorService, - private route: ActivatedRoute + private route: ActivatedRoute, + private contextService: ContextService ) { this.leaveOnboarding$ .pipe( @@ -69,19 +71,23 @@ export class CompanySearchService { ) .subscribe(() => void this.router.navigate(['/'])); combineLatest(this.claim$, this.claimID$) - .pipe(untilDestroyed(this)) - .subscribe(([claim, claimID]) => { - if ( - (claimID && !claim) || - (claim && - !claim.changeset.every( - (c) => - isClaimModification(c.modification) && - isExternalInfoModificationUnit(c.modification.claimModificationType) - )) - ) - void this.router.navigate(['./onboarding']); - }); + .pipe( + switchMap(([claim, claimID]) => { + if ( + (claimID && !claim) || + (claim && + !claim.changeset.every( + (c) => + isClaimModification(c.modification) && + isExternalInfoModificationUnit(c.modification.claimModificationType) + )) + ) + return this.contextService.navigate(['onboarding']); + return EMPTY; + }), + untilDestroyed(this) + ) + .subscribe(); } isKnownOrgType({ orgType }: PartyContent): boolean { @@ -94,11 +100,14 @@ export class CompanySearchService { const defaultEmail = this.keycloakService.getUsername(); const questionaryData: QuestionaryData = { ...data, contactInfo: { email: defaultEmail, ...data.contactInfo } }; return this.claim$.pipe( - switchMap((claim) => + withLatestFrom(this.contextService.organization$), + switchMap(([claim, org]) => claim - ? this.claimsService.updateClaimByID(claim.id, claim.revision, changeset).pipe(mapTo(claim.id)) + ? this.claimsService + .updateClaimByID(org.id, claim.id, claim.revision, changeset) + .pipe(mapTo(claim.id)) : this.questionaryService.saveQuestionary(documentID, questionaryData).pipe( - switchMap(() => this.claimsService.createClaim(changeset)), + switchMap(() => this.claimsService.createClaim(org.id, changeset)), pluck('id') ) ), diff --git a/src/app/sections/claim-section/onboarding/data-flow/claim/claim.service.ts b/src/app/sections/claim-section/onboarding/data-flow/claim/claim.service.ts index 69c854cc..6607c25f 100644 --- a/src/app/sections/claim-section/onboarding/data-flow/claim/claim.service.ts +++ b/src/app/sections/claim-section/onboarding/data-flow/claim/claim.service.ts @@ -4,17 +4,22 @@ import { BehaviorSubject, combineLatest, defer } from 'rxjs'; import { shareReplay, switchMap } from 'rxjs/operators'; import { ClaimsService } from '@dsh/api/claims'; +import { ContextService } from '@dsh/app/shared/services/context'; @Injectable() export class ClaimService { - claim$ = combineLatest([this.route.params, defer(() => this.loadClaim$)]).pipe( - switchMap(([{ claimID }]) => this.claimsService.getClaimByID(claimID)), + claim$ = combineLatest([this.route.params, this.contextService.organization$, defer(() => this.loadClaim$)]).pipe( + switchMap(([{ claimID }, org]) => this.claimsService.getClaimByID(org.id, claimID)), shareReplay(1) ); private loadClaim$ = new BehaviorSubject(undefined); - constructor(private route: ActivatedRoute, private claimsService: ClaimsService) {} + constructor( + private route: ActivatedRoute, + private claimsService: ClaimsService, + private contextService: ContextService + ) {} reloadClaim(): void { this.loadClaim$.next(); diff --git a/src/app/sections/claim-section/onboarding/data-flow/forms/upload-documents/upload-documents.service.ts b/src/app/sections/claim-section/onboarding/data-flow/forms/upload-documents/upload-documents.service.ts index f7c3b824..b3934a78 100644 --- a/src/app/sections/claim-section/onboarding/data-flow/forms/upload-documents/upload-documents.service.ts +++ b/src/app/sections/claim-section/onboarding/data-flow/forms/upload-documents/upload-documents.service.ts @@ -9,6 +9,7 @@ import { map, pluck, share, switchMap, withLatestFrom } from 'rxjs/operators'; import { FileModification, FileModificationUnit } from '@dsh/api-codegen/claim-management'; import { QuestionaryData } from '@dsh/api-codegen/questionary'; import { ClaimsService, createFileModificationUnit, takeFileModificationUnits } from '@dsh/api/claims'; +import { ContextService } from '@dsh/app/shared/services/context'; import { filterError, filterPayload, replaceError } from '@dsh/operators'; import { ClaimService } from '../../claim'; @@ -36,14 +37,15 @@ export class UploadDocumentsService extends QuestionaryFormService { private claimService: ClaimService, private claimsService: ClaimsService, private snackBar: MatSnackBar, - private transloco: TranslocoService + private transloco: TranslocoService, + private contextService: ContextService ) { super(questionaryStateService, validityService, validationCheckService); const uploadedFilesWithError$ = this.filesUploaded$.pipe( map((fileIds) => fileIds.map((id) => createFileModificationUnit(id))), - withLatestFrom(this.claimService.claim$), - switchMap(([changeset, { id, revision }]) => - this.claimsService.updateClaimByID(id, revision, changeset).pipe(replaceError) + withLatestFrom(this.claimService.claim$, this.contextService.organization$), + switchMap(([changeset, { id, revision }, org]) => + this.claimsService.updateClaimByID(org.id, id, revision, changeset).pipe(replaceError) ), share() ); @@ -51,9 +53,9 @@ export class UploadDocumentsService extends QuestionaryFormService { map((unit) => [ createFileModificationUnit(unit.fileId, FileModification.FileModificationTypeEnum.FileDeleted), ]), - withLatestFrom(this.claimService.claim$), - switchMap(([changeset, { id, revision }]) => - this.claimsService.updateClaimByID(id, revision, changeset).pipe(replaceError) + withLatestFrom(this.claimService.claim$, this.contextService.organization$), + switchMap(([changeset, { id, revision }, org]) => + this.claimsService.updateClaimByID(org.id, id, revision, changeset).pipe(replaceError) ), share() ); diff --git a/src/app/sections/claim-section/onboarding/data-flow/step-card/step-card.service.ts b/src/app/sections/claim-section/onboarding/data-flow/step-card/step-card.service.ts index 578330bd..b2b31c6d 100644 --- a/src/app/sections/claim-section/onboarding/data-flow/step-card/step-card.service.ts +++ b/src/app/sections/claim-section/onboarding/data-flow/step-card/step-card.service.ts @@ -3,9 +3,10 @@ import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { combineLatest, Observable, Subject } from 'rxjs'; -import { filter, map, pluck, shareReplay, switchMap, switchMapTo } from 'rxjs/operators'; +import { filter, map, pluck, shareReplay, switchMap, switchMapTo, withLatestFrom } from 'rxjs/operators'; import { ClaimsService } from '@dsh/api/claims'; +import { ContextService } from '@dsh/app/shared/services/context'; import { ConfirmActionDialogComponent } from '@dsh/components/popups'; import { QuestionaryStateService } from '../questionary-state.service'; @@ -32,7 +33,8 @@ export class StepCardService { private claimsService: ClaimsService, private dialog: MatDialog, private route: ActivatedRoute, - private validityService: ValidityService + private validityService: ValidityService, + private contextService: ContextService ) { const claimID$ = this.route.params.pipe(pluck('claimID')); combineLatest([this.stepFlowService.stepFlow$, this.selectStepFlowIndex$]) @@ -49,8 +51,13 @@ export class StepCardService { switchMap(() => this.dialog.open(ConfirmActionDialogComponent).afterClosed()), filter((r) => r === 'confirm'), switchMapTo(claimID$), - switchMap((claimID) => this.claimsService.getClaimByID(claimID)), - switchMap(({ id, revision }) => this.claimsService.requestReviewClaimByID(id, revision)), + withLatestFrom(this.contextService.organization$), + switchMap(([claimID, org]) => + this.claimsService.getClaimByID(org.id, claimID).pipe(map((claim) => [claim, org.id] as const)) + ), + switchMap(([{ id, revision }, orgId]) => + this.claimsService.requestReviewClaimByID(orgId, id, revision) + ), switchMapTo(claimID$), untilDestroyed(this) ) diff --git a/src/app/sections/index.ts b/src/app/sections/index.ts index 780d5d73..a5209947 100644 --- a/src/app/sections/index.ts +++ b/src/app/sections/index.ts @@ -1,2 +1 @@ export * from './sections.module'; -export * from './sections.component'; diff --git a/src/app/sections/landing/parts/payments/payments.service.ts b/src/app/sections/landing/parts/payments/payments.service.ts index 11ee95ef..37431276 100644 --- a/src/app/sections/landing/parts/payments/payments.service.ts +++ b/src/app/sections/landing/parts/payments/payments.service.ts @@ -2,10 +2,11 @@ import { Injectable } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { TranslocoService } from '@ngneat/transloco'; import { combineLatest, Observable } from 'rxjs'; -import { pluck, shareReplay } from 'rxjs/operators'; +import { pluck, shareReplay, switchMap } from 'rxjs/operators'; import { ClaimsService, CLAIM_STATUS } from '@dsh/api/claims'; import { ApiShopsService } from '@dsh/api/shop'; +import { ContextService } from '@dsh/app/shared/services/context'; import { booleanDelay, takeError } from '@dsh/operators'; import { ActionBtnContent, TestEnvBtnContent } from './content-config'; @@ -22,11 +23,19 @@ export class PaymentsService { private shopService: ApiShopsService, private claimService: ClaimsService, private snackBar: MatSnackBar, - private transloco: TranslocoService + private transloco: TranslocoService, + private contextService: ContextService ) { - const claims = this.claimService - .search1000Claims([CLAIM_STATUS.Pending, CLAIM_STATUS.PendingAcceptance, CLAIM_STATUS.Review]) - .pipe(shareReplay(1)); + const claims = this.contextService.organization$.pipe( + switchMap(({ id }) => + this.claimService.search1000Claims(id, [ + CLAIM_STATUS.Pending, + CLAIM_STATUS.PendingAcceptance, + CLAIM_STATUS.Review, + ]) + ), + shareReplay(1) + ); const contentConfig = toContentConf(this.shopService.shops$, claims); this.actionBtnContent$ = contentConfig.pipe(pluck('actionBtnContent')); this.testEnvBtnContent$ = contentConfig.pipe(pluck('testEnvBtnContent')); diff --git a/src/app/sections/sections-routing.module.ts b/src/app/sections/sections-routing.module.ts index 7dc72fbc..2b3b29e1 100644 --- a/src/app/sections/sections-routing.module.ts +++ b/src/app/sections/sections-routing.module.ts @@ -1,26 +1,44 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { SectionsComponent } from './sections.component'; + const ROUTES: Routes = [ + { + path: 'organization', + pathMatch: 'full', + redirectTo: 'organization/', + }, { path: '', - loadChildren: () => import('./landing').then((m) => m.LandingModule), + pathMatch: 'full', + redirectTo: 'organization/', }, { - path: 'claim-section', - loadChildren: () => import('./claim-section').then((m) => m.ClaimSectionModule), - }, - { - path: 'payment-section', - loadChildren: () => import('./payment-section').then((m) => m.PaymentSectionModule), - }, - { - path: 'wallet-section', - loadChildren: () => import('./wallet-section').then((m) => m.WalletSectionModule), - }, - { - path: 'organization-section', - loadChildren: () => import('./organization-section').then((m) => m.OrginizationSectionModule), + path: 'organization/:organizationId', + component: SectionsComponent, + children: [ + { + path: '', + loadChildren: () => import('./landing').then((m) => m.LandingModule), + }, + { + path: 'claim-section', + loadChildren: () => import('./claim-section').then((m) => m.ClaimSectionModule), + }, + { + path: 'payment-section', + loadChildren: () => import('./payment-section').then((m) => m.PaymentSectionModule), + }, + { + path: 'wallet-section', + loadChildren: () => import('./wallet-section').then((m) => m.WalletSectionModule), + }, + { + path: 'organization-section', + loadChildren: () => import('./organization-section').then((m) => m.OrginizationSectionModule), + }, + ], }, { path: '**', diff --git a/src/app/sections/sections.component.html b/src/app/sections/sections.component.html new file mode 100644 index 00000000..78038e3c --- /dev/null +++ b/src/app/sections/sections.component.html @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/sections/sections.component.ts b/src/app/sections/sections.component.ts index a7c8a200..4af1fb9a 100644 --- a/src/app/sections/sections.component.ts +++ b/src/app/sections/sections.component.ts @@ -1,7 +1,32 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy'; +import * as Sentry from '@sentry/angular'; +import { first } from 'rxjs/operators'; +import { KeycloakTokenInfoService } from '@dsh/app/shared'; +import { ContextService } from '@dsh/app/shared/services/context'; + +import { BootstrapService } from '../bootstrap.service'; + +@UntilDestroy() @Component({ selector: 'dsh-sections', - template: ``, + templateUrl: 'sections.component.html', + providers: [ContextService, BootstrapService], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class SectionsComponent {} +export class SectionsComponent implements OnInit { + bootstrapped$ = this.bootstrapService.bootstrapped$; + + constructor( + private bootstrapService: BootstrapService, + private keycloakTokenInfoService: KeycloakTokenInfoService + ) {} + + ngOnInit(): void { + this.bootstrapService.bootstrap(); + this.keycloakTokenInfoService.partyID$ + .pipe(first(), untilDestroyed(this)) + .subscribe((partyID) => Sentry.setUser({ id: partyID })); + } +} diff --git a/src/app/sections/sections.module.ts b/src/app/sections/sections.module.ts index e92b70af..210d8911 100644 --- a/src/app/sections/sections.module.ts +++ b/src/app/sections/sections.module.ts @@ -1,10 +1,14 @@ +import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { ExtendedModule } from '@angular/flex-layout'; import { MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material/dialog'; import { DEBOUNCE_FETCHER_ACTION_TIME, DEFAULT_FETCHER_DEBOUNCE_ACTION_TIME } from '@rbkmoney/partial-fetcher'; import { ShopModule } from '@dsh/api/shop'; import { WalletModule } from '@dsh/api/wallet'; +import { FeedbackModule } from '../feedback'; +import { HomeModule } from '../home'; import { CHARTS_THEME } from './payment-section/analytics/charts-theme'; import { SectionsRoutingModule } from './sections-routing.module'; import { SectionsComponent } from './sections.component'; @@ -17,9 +21,15 @@ import { } from './tokens'; @NgModule({ - imports: [SectionsRoutingModule, ShopModule, WalletModule], - declarations: [SectionsComponent], - exports: [SectionsComponent], + imports: [ + CommonModule, + ShopModule, + WalletModule, + SectionsRoutingModule, + FeedbackModule, + HomeModule, + ExtendedModule, + ], providers: [ { provide: SEARCH_LIMIT, useValue: DEFAULT_SEARCH_LIMIT }, { provide: MAT_DIALOG_DEFAULT_OPTIONS, useValue: DEFAULT_DIALOG_CONFIG.medium }, @@ -27,5 +37,7 @@ import { { provide: DEBOUNCE_FETCHER_ACTION_TIME, useValue: DEFAULT_FETCHER_DEBOUNCE_ACTION_TIME }, { provide: CHARTS_THEME, useValue: DEFAULT_CHARTS_THEME }, ], + declarations: [SectionsComponent], + exports: [SectionsRoutingModule], }) export class SectionsModule {} diff --git a/src/app/shared/components/shop-creation/create-international-shop-entity/services/create-international-shop-entity/create-international-shop-entity.service.ts b/src/app/shared/components/shop-creation/create-international-shop-entity/services/create-international-shop-entity/create-international-shop-entity.service.ts index c259e97f..c59f5739 100644 --- a/src/app/shared/components/shop-creation/create-international-shop-entity/services/create-international-shop-entity/create-international-shop-entity.service.ts +++ b/src/app/shared/components/shop-creation/create-international-shop-entity/services/create-international-shop-entity/create-international-shop-entity.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { IdGeneratorService } from '@rbkmoney/id-generator'; import { Observable } from 'rxjs'; -import { mapTo, switchMap } from 'rxjs/operators'; +import { mapTo, switchMap, first, map } from 'rxjs/operators'; import { Claim, Modification } from '@dsh/api-codegen/claim-management'; import { ClaimsService } from '@dsh/api/claims'; @@ -13,6 +13,7 @@ import { } from '@dsh/api/claims/claim-party-modification'; import { createInternationalContractPayoutToolModification } from '@dsh/api/claims/claim-party-modification/claim-contract-modification/create-international-contract-payout-tool-modification'; +import { ContextService } from '../../../../../services/context'; import { InternationalShopEntityFormValue } from '../../types/international-shop-entity-form-value'; import { payoutToolDetailsInternationalBankAccountToInternationalBankAccount, @@ -21,16 +22,24 @@ import { @Injectable() export class CreateInternationalShopEntityService { - constructor(private claimsService: ClaimsService, private idGenerator: IdGeneratorService) {} + constructor( + private claimsService: ClaimsService, + private idGenerator: IdGeneratorService, + private contextService: ContextService + ) {} createShop(creationData: InternationalShopEntityFormValue): Observable { - return this.claimsService - .createClaim(this.createClaimsModifications(creationData)) - .pipe( - switchMap((claim) => - this.claimsService.requestReviewClaimByID(claim.id, claim.revision).pipe(mapTo(claim)) - ) - ); + return this.contextService.organization$.pipe( + first(), + switchMap((org) => + this.claimsService + .createClaim(org.id, this.createClaimsModifications(creationData)) + .pipe(map((claim) => [claim, org.id] as const)) + ), + switchMap(([claim, orgId]) => + this.claimsService.requestReviewClaimByID(orgId, claim.id, claim.revision).pipe(mapTo(claim)) + ) + ); } private createClaimsModifications({ diff --git a/src/app/shared/components/shop-creation/create-russian-shop-entity/services/create-russian-shop-entity/create-russian-shop-entity.service.ts b/src/app/shared/components/shop-creation/create-russian-shop-entity/services/create-russian-shop-entity/create-russian-shop-entity.service.ts index d1e95e89..fab0565e 100644 --- a/src/app/shared/components/shop-creation/create-russian-shop-entity/services/create-russian-shop-entity/create-russian-shop-entity.service.ts +++ b/src/app/shared/components/shop-creation/create-russian-shop-entity/services/create-russian-shop-entity/create-russian-shop-entity.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { IdGeneratorService } from '@rbkmoney/id-generator'; -import { forkJoin, Observable, of } from 'rxjs'; -import { pluck, switchMap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { switchMap, first, map, mapTo } from 'rxjs/operators'; import { Claim, PartyModification } from '@dsh/api-codegen/claim-management'; import { ClaimsService } from '@dsh/api/claims'; @@ -14,18 +14,28 @@ import { makeShopLocation, } from '@dsh/api/claims/claim-party-modification'; +import { ContextService } from '../../../../../services/context'; import { RussianShopForm } from '../../types/russian-shop-entity'; @Injectable() export class CreateRussianShopEntityService { - constructor(private claimsService: ClaimsService, private idGenerator: IdGeneratorService) {} + constructor( + private claimsService: ClaimsService, + private idGenerator: IdGeneratorService, + private contextService: ContextService + ) {} createShop(creationData: RussianShopForm): Observable { - return this.claimsService.createClaim(this.createShopCreationModifications(creationData)).pipe( - switchMap((claim) => { - return forkJoin([of(claim), this.claimsService.requestReviewClaimByID(claim.id, claim.revision)]); - }), - pluck(0) + return this.contextService.organization$.pipe( + first(), + switchMap((org) => + this.claimsService + .createClaim(org.id, this.createShopCreationModifications(creationData)) + .pipe(map((claim) => [claim, org.id] as const)) + ), + switchMap(([claim, orgId]) => + this.claimsService.requestReviewClaimByID(orgId, claim.id, claim.revision).pipe(mapTo(claim)) + ) ); } diff --git a/src/app/shared/services/context/context.module.ts b/src/app/shared/services/context/context.module.ts deleted file mode 100644 index 9800e2aa..00000000 --- a/src/app/shared/services/context/context.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { ContextService } from './context.service'; - -@NgModule({ - imports: [], - declarations: [], - exports: [], - providers: [ContextService], -}) -export class ContextModule {} diff --git a/src/app/shared/services/context/context.service.ts b/src/app/shared/services/context/context.service.ts index f040065a..1c169895 100644 --- a/src/app/shared/services/context/context.service.ts +++ b/src/app/shared/services/context/context.service.ts @@ -1,44 +1,76 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, defer, concat, EMPTY, ReplaySubject } from 'rxjs'; -import { switchMap, pluck, tap, mapTo, catchError, shareReplay, distinctUntilChanged } from 'rxjs/operators'; +import { ActivatedRoute, Router, NavigationExtras } from '@angular/router'; +import { Observable, EMPTY, ReplaySubject, merge } from 'rxjs'; +import { switchMap, pluck, tap, catchError, startWith, distinctUntilChanged, shareReplay, first } from 'rxjs/operators'; import { OrganizationsService } from '@dsh/api'; import { Organization } from '@dsh/api-codegen/organizations'; +const ORGANIZATION_REG_EXP = /^(\/organization\/)([\w-]+)(.*)/; + @Injectable() export class ContextService { - organization$: Observable = concat( - this.organizationsService.getContext().pipe( - pluck('organizationId'), - catchError((err) => { - if (err instanceof HttpErrorResponse && err.status === 404) - return this.initOrganization().pipe(switchMap(() => EMPTY)); - console.error(err); - return EMPTY; - }) + organization$: Observable = this.route.params.pipe( + startWith(this.route.snapshot.params), + pluck('organizationId'), + distinctUntilChanged(), + switchMap((id) => + this.organizationsService.getOrg(id).pipe( + catchError((err) => { + console.error(err); + return this.getContextOrganization(); + }) + ) ), - defer(() => this.switchOrganization$).pipe( - distinctUntilChanged(), - switchMap((id) => this.organizationsService.switchContext(id).pipe(mapTo(id))) - ) - ).pipe( - switchMap((id) => this.organizationsService.getOrg(id)), shareReplay(1) ); private switchOrganization$ = new ReplaySubject(1); - constructor(private organizationsService: OrganizationsService) {} + constructor( + private organizationsService: OrganizationsService, + private route: ActivatedRoute, + private router: Router + ) { + merge(this.switchOrganization$, this.organization$.pipe(pluck('id'))).subscribe( + (id) => + void this.router.navigateByUrl( + this.router.url.replace(ORGANIZATION_REG_EXP, (match, start, oldId, end) => + [start, id, end].join('') + ) + ) + ); + } switchOrganization(organizationId: string): void { this.switchOrganization$.next(organizationId); } - private initOrganization() { - return this.organizationsService.listOrgMembership(1).pipe( - pluck('result', 0, 'id'), - tap((id) => this.switchOrganization(id)) + navigate(commands: (string | number)[], extras?: NavigationExtras): Observable { + return this.organization$.pipe( + first(), + switchMap(({ id }) => this.router.navigate(['organization', id, ...commands], extras)) + ); + } + + private getInitOrganization() { + return this.organizationsService.listOrgMembership(1).pipe(pluck('result', 0)); + } + + private getContextOrganization() { + return this.organizationsService.getContext().pipe( + pluck('organizationId'), + catchError((err) => { + if (err instanceof HttpErrorResponse && err.status === 404) + return this.getInitOrganization().pipe( + pluck('id'), + tap((id) => this.switchOrganization(id)) + ); + console.error(err); + return EMPTY; + }), + switchMap((id) => this.organizationsService.getOrg(id)) ); } } diff --git a/src/app/shared/services/context/index.ts b/src/app/shared/services/context/index.ts index dca9a55b..db863310 100644 --- a/src/app/shared/services/context/index.ts +++ b/src/app/shared/services/context/index.ts @@ -1,2 +1 @@ export * from './context.service'; -export * from './context.module';