mirror of
https://github.com/valitydev/dashboard.git
synced 2024-11-06 02:25:23 +00:00
OPS-355: Fix roles accesses (#148)
This commit is contained in:
parent
adc9a1a167
commit
452cc3b66d
@ -61,7 +61,9 @@ export function createApi<
|
|||||||
|
|
||||||
private call(name: keyof T, params: Record<PropertyKey, unknown>) {
|
private call(name: keyof T, params: Record<PropertyKey, unknown>) {
|
||||||
return this.createExtendedParams().pipe(
|
return this.createExtendedParams().pipe(
|
||||||
switchMap((p) => this.api[name](Object.assign({}, params, ...p))),
|
switchMap((extendParams) =>
|
||||||
|
this.api[name](Object.assign({}, ...extendParams, params)),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
*ngFor="let roleId of roleIds"
|
*ngFor="let roleId of roleIds"
|
||||||
class="dsh-body-2"
|
class="dsh-body-2"
|
||||||
fxLayoutAlign="center center"
|
fxLayoutAlign="center center"
|
||||||
>{{ (roleIdDict$ | async)?.[roleId] }}</dsh-nested-table-col
|
><button dsh-button (click)="show(roleId)">
|
||||||
|
{{ (roleIdDict$ | async)?.[roleId] }}
|
||||||
|
</button></dsh-nested-table-col
|
||||||
>
|
>
|
||||||
<dsh-nested-table-col *ngIf="isAllowAdd">
|
<dsh-nested-table-col *ngIf="isAllowAdd">
|
||||||
<button color="accent" dsh-button (click)="add()">{{ t('add') }}</button>
|
<button color="accent" dsh-button (click)="add()">{{ t('add') }}</button>
|
||||||
|
@ -6,18 +6,20 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
|
OnChanges,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
|
||||||
import { MemberRole, ResourceScopeId, RoleId } from '@vality/swag-organizations';
|
import { ComponentChanges } from '@vality/ng-core';
|
||||||
|
import { MemberRole, ResourceScopeId, RoleId, Organization } from '@vality/swag-organizations';
|
||||||
import { coerceBoolean } from 'coerce-property';
|
import { coerceBoolean } from 'coerce-property';
|
||||||
import isNil from 'lodash-es/isNil';
|
import isNil from 'lodash-es/isNil';
|
||||||
import { BehaviorSubject, combineLatest, EMPTY, Observable, of } from 'rxjs';
|
import { BehaviorSubject, combineLatest, EMPTY, Observable, of, ReplaySubject } from 'rxjs';
|
||||||
import { first, map, switchMap, tap } from 'rxjs/operators';
|
import { first, map, switchMap, tap, shareReplay } from 'rxjs/operators';
|
||||||
|
|
||||||
import { OrganizationsDictionaryService } from '@dsh/app/api/organizations';
|
import { OrganizationsDictionaryService } from '@dsh/app/api/organizations';
|
||||||
|
import { ShopsService } from '@dsh/app/api/payments';
|
||||||
import { DialogConfig, DIALOG_CONFIG } from '@dsh/app/sections/tokens';
|
import { DialogConfig, DIALOG_CONFIG } from '@dsh/app/sections/tokens';
|
||||||
import { ShopsDataService } from '@dsh/app/shared';
|
|
||||||
import { sortRoleIds } from '@dsh/app/shared/components/organization-roles/utils/sort-role-ids';
|
import { sortRoleIds } from '@dsh/app/shared/components/organization-roles/utils/sort-role-ids';
|
||||||
import { PartialReadonly } from '@dsh/type-utils';
|
import { PartialReadonly } from '@dsh/type-utils';
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ import { SelectRoleDialogData } from './components/select-role-dialog/types/sele
|
|||||||
templateUrl: 'change-roles-table.component.html',
|
templateUrl: 'change-roles-table.component.html',
|
||||||
styleUrls: ['change-roles-table.component.scss'],
|
styleUrls: ['change-roles-table.component.scss'],
|
||||||
})
|
})
|
||||||
export class ChangeRolesTableComponent implements OnInit {
|
export class ChangeRolesTableComponent implements OnInit, OnChanges {
|
||||||
@Input() set roles(roles: PartialReadonly<MemberRole>[]) {
|
@Input() set roles(roles: PartialReadonly<MemberRole>[]) {
|
||||||
if (!isNil(roles)) {
|
if (!isNil(roles)) {
|
||||||
this.roles$.next(roles);
|
this.roles$.next(roles);
|
||||||
@ -44,6 +46,7 @@ export class ChangeRolesTableComponent implements OnInit {
|
|||||||
get roles(): PartialReadonly<MemberRole>[] {
|
get roles(): PartialReadonly<MemberRole>[] {
|
||||||
return this.roles$.value;
|
return this.roles$.value;
|
||||||
}
|
}
|
||||||
|
@Input() organization: Organization;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit mode:
|
* Edit mode:
|
||||||
@ -57,8 +60,14 @@ export class ChangeRolesTableComponent implements OnInit {
|
|||||||
@Output() addedRoles = new EventEmitter<PartialReadonly<MemberRole>[]>();
|
@Output() addedRoles = new EventEmitter<PartialReadonly<MemberRole>[]>();
|
||||||
@Output() removedRoles = new EventEmitter<PartialReadonly<MemberRole>[]>();
|
@Output() removedRoles = new EventEmitter<PartialReadonly<MemberRole>[]>();
|
||||||
|
|
||||||
|
organization$ = new ReplaySubject<Organization>(1);
|
||||||
roleIds: RoleId[] = [];
|
roleIds: RoleId[] = [];
|
||||||
shops$ = this.shopsDataService.shops$;
|
shops$ = this.organization$.pipe(
|
||||||
|
switchMap((organization) =>
|
||||||
|
this.shopsService.getShopsForParty({ partyID: organization.party }),
|
||||||
|
),
|
||||||
|
shareReplay({ bufferSize: 1, refCount: true }),
|
||||||
|
);
|
||||||
roleIdDict$ = this.organizationsDictionaryService.roleId$;
|
roleIdDict$ = this.organizationsDictionaryService.roleId$;
|
||||||
|
|
||||||
get availableRoles(): RoleId[] {
|
get availableRoles(): RoleId[] {
|
||||||
@ -71,20 +80,31 @@ export class ChangeRolesTableComponent implements OnInit {
|
|||||||
|
|
||||||
roles$ = new BehaviorSubject<PartialReadonly<MemberRole>[]>([]);
|
roles$ = new BehaviorSubject<PartialReadonly<MemberRole>[]>([]);
|
||||||
|
|
||||||
isAllowRemoves$ = this.roles$.pipe(map((r) => r.length > 1));
|
isAllowRemoves$ = this.roles$.pipe(
|
||||||
|
map(
|
||||||
|
(roles) =>
|
||||||
|
!this.editMode || roles.some((r) => roles.some((b) => b.roleId !== r.roleId)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
private get hasAdminRole() {
|
private get hasAdminRole() {
|
||||||
return !!this.roles.find((r) => r.id === RoleId.Administrator);
|
return !!this.roles.find((r) => r.id === RoleId.Administrator);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private shopsDataService: ShopsDataService,
|
private shopsService: ShopsService,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
@Inject(DIALOG_CONFIG) private dialogConfig: DialogConfig,
|
@Inject(DIALOG_CONFIG) private dialogConfig: DialogConfig,
|
||||||
private cdr: ChangeDetectorRef,
|
private cdr: ChangeDetectorRef,
|
||||||
private organizationsDictionaryService: OrganizationsDictionaryService,
|
private organizationsDictionaryService: OrganizationsDictionaryService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
ngOnChanges({ organization }: ComponentChanges<ChangeRolesTableComponent>) {
|
||||||
|
if (organization) {
|
||||||
|
this.organization$.next(organization.currentValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.roles$.pipe(untilDestroyed(this)).subscribe((roles) => this.selectedRoles.emit(roles));
|
this.roles$.pipe(untilDestroyed(this)).subscribe((roles) => this.selectedRoles.emit(roles));
|
||||||
}
|
}
|
||||||
@ -116,6 +136,25 @@ export class ChangeRolesTableComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show(roleId: RoleId) {
|
||||||
|
const removeDialogsClass = addDialogsClass(this.dialog.openDialogs, 'dsh-hidden');
|
||||||
|
this.dialog
|
||||||
|
.open<SelectRoleDialogComponent, SelectRoleDialogData, SelectRoleDialogResult>(
|
||||||
|
SelectRoleDialogComponent,
|
||||||
|
{
|
||||||
|
...this.dialogConfig.large,
|
||||||
|
data: { availableRoles: [roleId], isShow: true },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.afterClosed()
|
||||||
|
.pipe(untilDestroyed(this))
|
||||||
|
.subscribe({
|
||||||
|
complete: () => {
|
||||||
|
removeDialogsClass();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
remove(roleId: RoleId): void {
|
remove(roleId: RoleId): void {
|
||||||
this.removeRoleIds([roleId]);
|
this.removeRoleIds([roleId]);
|
||||||
this.removeRoles(this.roles.filter((r) => r.roleId === roleId));
|
this.removeRoles(this.roles.filter((r) => r.roleId === roleId));
|
||||||
|
@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FlexModule } from '@angular/flex-layout';
|
import { FlexModule } from '@angular/flex-layout';
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { MatRadioModule } from '@angular/material/radio';
|
import { MatRadioModule } from '@angular/material/radio';
|
||||||
import { TranslocoModule } from '@ngneat/transloco';
|
import { TranslocoModule } from '@ngneat/transloco';
|
||||||
@ -26,6 +27,7 @@ import { SelectRoleDialogComponent } from './components/select-role-dialog/selec
|
|||||||
MatRadioModule,
|
MatRadioModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
SelectionModule,
|
SelectionModule,
|
||||||
|
MatButtonModule,
|
||||||
],
|
],
|
||||||
declarations: [ChangeRolesTableComponent, SelectRoleDialogComponent],
|
declarations: [ChangeRolesTableComponent, SelectRoleDialogComponent],
|
||||||
exports: [ChangeRolesTableComponent],
|
exports: [ChangeRolesTableComponent],
|
||||||
|
@ -5,13 +5,13 @@
|
|||||||
>
|
>
|
||||||
<mat-radio-group [formControl]="roleControl">
|
<mat-radio-group [formControl]="roleControl">
|
||||||
<dsh-nested-table [rowsGridTemplateColumns]="rowsGridTemplateColumns">
|
<dsh-nested-table [rowsGridTemplateColumns]="rowsGridTemplateColumns">
|
||||||
<dsh-nested-table-row>
|
<dsh-nested-table-row *ngIf="!data.isShow">
|
||||||
<dsh-nested-table-col></dsh-nested-table-col>
|
<dsh-nested-table-col></dsh-nested-table-col>
|
||||||
<dsh-nested-table-col *ngFor="let role of roles" fxLayoutAlign="center center">
|
<dsh-nested-table-col *ngFor="let role of roles" fxLayoutAlign="center center">
|
||||||
<span class="dsh-body-2 header">{{ (roleIdDict$ | async)?.[role] }}</span>
|
<span class="dsh-body-2 header">{{ (roleIdDict$ | async)?.[role] }}</span>
|
||||||
</dsh-nested-table-col>
|
</dsh-nested-table-col>
|
||||||
</dsh-nested-table-row>
|
</dsh-nested-table-row>
|
||||||
<dsh-nested-table-row>
|
<dsh-nested-table-row *ngIf="!data.isShow">
|
||||||
<dsh-nested-table-col></dsh-nested-table-col>
|
<dsh-nested-table-col></dsh-nested-table-col>
|
||||||
<dsh-nested-table-col *ngFor="let role of roles">
|
<dsh-nested-table-col *ngFor="let role of roles">
|
||||||
<mat-radio-button [value]="role"></mat-radio-button>
|
<mat-radio-button [value]="role"></mat-radio-button>
|
||||||
@ -33,7 +33,7 @@
|
|||||||
</dsh-nested-table-row>
|
</dsh-nested-table-row>
|
||||||
</dsh-nested-table>
|
</dsh-nested-table>
|
||||||
</mat-radio-group>
|
</mat-radio-group>
|
||||||
<ng-container dshBaseDialogActions>
|
<ng-container *ngIf="!data.isShow" dshBaseDialogActions>
|
||||||
<button [disabled]="roleControl.invalid" color="accent" dsh-button (click)="select()">
|
<button [disabled]="roleControl.invalid" color="accent" dsh-button (click)="select()">
|
||||||
{{ t('select') }}
|
{{ t('select') }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2,4 +2,5 @@ import { RoleId } from '@vality/swag-organizations';
|
|||||||
|
|
||||||
export interface SelectRoleDialogData {
|
export interface SelectRoleDialogData {
|
||||||
availableRoles: RoleId[];
|
availableRoles: RoleId[];
|
||||||
|
isShow?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,10 @@
|
|||||||
<div fxLayout="column" fxLayoutGap="24px">
|
<div fxLayout="column" fxLayoutGap="24px">
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<h2 class="dsh-title">{{ t('roles') }}</h2>
|
<h2 class="dsh-title">{{ t('roles') }}</h2>
|
||||||
<dsh-change-roles-table (selectedRoles)="selectRoles($event)"></dsh-change-roles-table>
|
<dsh-change-roles-table
|
||||||
|
[organization]="data.organization"
|
||||||
|
(selectedRoles)="selectRoles($event)"
|
||||||
|
></dsh-change-roles-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ng-container dshBaseDialogActions>
|
<ng-container dshBaseDialogActions>
|
||||||
|
@ -37,7 +37,7 @@ export class CreateInvitationDialogComponent {
|
|||||||
create() {
|
create() {
|
||||||
return this.invitationsService
|
return this.invitationsService
|
||||||
.createInvitation({
|
.createInvitation({
|
||||||
orgId: this.data.orgId,
|
orgId: this.data.organization.id,
|
||||||
invitationRequest: {
|
invitationRequest: {
|
||||||
invitee: {
|
invitee: {
|
||||||
contact: {
|
contact: {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Organization } from '@vality/swag-organizations';
|
import { Organization } from '@vality/swag-organizations';
|
||||||
|
|
||||||
export type CreateInvitationDialogData = {
|
export type CreateInvitationDialogData = {
|
||||||
orgId: Organization['id'];
|
organization: Organization;
|
||||||
};
|
};
|
||||||
|
@ -43,7 +43,7 @@ export class InvitationsComponent {
|
|||||||
return this.organization$
|
return this.organization$
|
||||||
.pipe(
|
.pipe(
|
||||||
first(),
|
first(),
|
||||||
switchMap(({ id: orgId }) =>
|
switchMap((organization) =>
|
||||||
this.dialog
|
this.dialog
|
||||||
.open<
|
.open<
|
||||||
CreateInvitationDialogComponent,
|
CreateInvitationDialogComponent,
|
||||||
@ -51,7 +51,7 @@ export class InvitationsComponent {
|
|||||||
BaseDialogResponseStatus
|
BaseDialogResponseStatus
|
||||||
>(CreateInvitationDialogComponent, {
|
>(CreateInvitationDialogComponent, {
|
||||||
...this.dialogConfig.large,
|
...this.dialogConfig.large,
|
||||||
data: { orgId },
|
data: { organization },
|
||||||
})
|
})
|
||||||
.afterClosed(),
|
.afterClosed(),
|
||||||
),
|
),
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
(cancel)="cancel()"
|
(cancel)="cancel()"
|
||||||
>
|
>
|
||||||
<dsh-change-roles-table
|
<dsh-change-roles-table
|
||||||
|
[organization]="data.organization"
|
||||||
[roles]="roles$ | async"
|
[roles]="roles$ | async"
|
||||||
controlled
|
controlled
|
||||||
editMode
|
editMode
|
||||||
|
@ -21,7 +21,7 @@ export class EditRolesDialogComponent {
|
|||||||
roles$ = defer(() => this.updateRoles$).pipe(
|
roles$ = defer(() => this.updateRoles$).pipe(
|
||||||
switchMap(() =>
|
switchMap(() =>
|
||||||
this.membersService
|
this.membersService
|
||||||
.getOrgMember({ orgId: this.data.orgId, userId: this.data.userId })
|
.getOrgMember({ orgId: this.data.organization.id, userId: this.data.userId })
|
||||||
.pipe(map((r) => r.roles)),
|
.pipe(map((r) => r.roles)),
|
||||||
),
|
),
|
||||||
untilDestroyed(this),
|
untilDestroyed(this),
|
||||||
@ -45,7 +45,7 @@ export class EditRolesDialogComponent {
|
|||||||
return forkJoin(
|
return forkJoin(
|
||||||
roles.map((memberRole) =>
|
roles.map((memberRole) =>
|
||||||
this.membersService.assignMemberRole({
|
this.membersService.assignMemberRole({
|
||||||
orgId: this.data.orgId,
|
orgId: this.data.organization.id,
|
||||||
userId: this.data.userId,
|
userId: this.data.userId,
|
||||||
memberRole,
|
memberRole,
|
||||||
}),
|
}),
|
||||||
@ -65,7 +65,7 @@ export class EditRolesDialogComponent {
|
|||||||
roles.map((role) =>
|
roles.map((role) =>
|
||||||
this.membersService
|
this.membersService
|
||||||
.removeMemberRole({
|
.removeMemberRole({
|
||||||
orgId: this.data.orgId,
|
orgId: this.data.organization.id,
|
||||||
userId: this.data.userId,
|
userId: this.data.userId,
|
||||||
memberRoleId: role.id,
|
memberRoleId: role.id,
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import { Organization } from '@vality/swag-organizations';
|
||||||
|
|
||||||
export interface EditRolesDialogData {
|
export interface EditRolesDialogData {
|
||||||
orgId: string;
|
organization: Organization;
|
||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ export class MemberComponent implements OnChanges {
|
|||||||
.open<EditRolesDialogComponent, EditRolesDialogData>(EditRolesDialogComponent, {
|
.open<EditRolesDialogComponent, EditRolesDialogData>(EditRolesDialogComponent, {
|
||||||
...this.dialogConfig.large,
|
...this.dialogConfig.large,
|
||||||
data: {
|
data: {
|
||||||
orgId: this.organization.id,
|
organization: this.organization,
|
||||||
userId: this.member.id,
|
userId: this.member.id,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
fxLayoutGap="32px"
|
fxLayoutGap="32px"
|
||||||
>
|
>
|
||||||
<nav [tabPanel]="tabPanel" mat-tab-nav-bar>
|
<nav [tabPanel]="tabPanel" mat-tab-nav-bar>
|
||||||
|
<ng-container *ngFor="let link of links">
|
||||||
<a
|
<a
|
||||||
#rla="routerLinkActive"
|
#rla="routerLinkActive"
|
||||||
*ngFor="let link of links"
|
*ngIf="link.roles | isAccessAllowed: 'some'"
|
||||||
[active]="rla.isActive"
|
[active]="rla.isActive"
|
||||||
[routerLink]="link.path"
|
[routerLink]="link.path"
|
||||||
mat-tab-link
|
mat-tab-link
|
||||||
@ -14,6 +15,7 @@
|
|||||||
>
|
>
|
||||||
<span>{{ link.label$ | async }}</span>
|
<span>{{ link.label$ | async }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
</ng-container>
|
||||||
</nav>
|
</nav>
|
||||||
<mat-tab-nav-panel #tabPanel>
|
<mat-tab-nav-panel #tabPanel>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { TranslocoService } from '@ngneat/transloco';
|
import { TranslocoService } from '@ngneat/transloco';
|
||||||
|
|
||||||
|
import { RoleAccessName } from '@dsh/app/auth';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: 'integrations.component.html',
|
templateUrl: 'integrations.component.html',
|
||||||
})
|
})
|
||||||
@ -13,6 +15,7 @@ export class IntegrationsComponent {
|
|||||||
null,
|
null,
|
||||||
'payment-section',
|
'payment-section',
|
||||||
),
|
),
|
||||||
|
roles: [RoleAccessName.PaymentLinks],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'api-keys',
|
path: 'api-keys',
|
||||||
@ -21,6 +24,7 @@ export class IntegrationsComponent {
|
|||||||
null,
|
null,
|
||||||
'payment-section',
|
'payment-section',
|
||||||
),
|
),
|
||||||
|
roles: [RoleAccessName.ApiKeys],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'webhooks',
|
path: 'webhooks',
|
||||||
@ -29,6 +33,7 @@ export class IntegrationsComponent {
|
|||||||
null,
|
null,
|
||||||
'payment-section',
|
'payment-section',
|
||||||
),
|
),
|
||||||
|
roles: [RoleAccessName.Webhooks],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import { FlexModule } from '@angular/flex-layout';
|
|||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import { TranslocoModule } from '@ngneat/transloco';
|
import { TranslocoModule } from '@ngneat/transloco';
|
||||||
|
|
||||||
|
import { AuthModule } from '@dsh/app/auth';
|
||||||
import { LayoutModule } from '@dsh/components/layout';
|
import { LayoutModule } from '@dsh/components/layout';
|
||||||
import { ScrollUpModule } from '@dsh/components/navigation';
|
import { ScrollUpModule } from '@dsh/components/navigation';
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ import { IntegrationsComponent } from './integrations.component';
|
|||||||
TranslocoModule,
|
TranslocoModule,
|
||||||
ScrollUpModule,
|
ScrollUpModule,
|
||||||
MatTabsModule,
|
MatTabsModule,
|
||||||
|
AuthModule,
|
||||||
],
|
],
|
||||||
declarations: [IntegrationsComponent],
|
declarations: [IntegrationsComponent],
|
||||||
})
|
})
|
||||||
|
@ -5,9 +5,10 @@
|
|||||||
fxLayoutGap="32px"
|
fxLayoutGap="32px"
|
||||||
>
|
>
|
||||||
<nav mat-tab-nav-bar>
|
<nav mat-tab-nav-bar>
|
||||||
|
<ng-container *ngFor="let link of links">
|
||||||
<a
|
<a
|
||||||
#rla="routerLinkActive"
|
#rla="routerLinkActive"
|
||||||
*ngFor="let link of links"
|
*ngIf="link.roles | isAccessAllowed: 'some'"
|
||||||
[active]="rla.isActive"
|
[active]="rla.isActive"
|
||||||
[routerLink]="link.path"
|
[routerLink]="link.path"
|
||||||
mat-tab-link
|
mat-tab-link
|
||||||
@ -15,6 +16,7 @@
|
|||||||
>
|
>
|
||||||
<span>{{ link.label$ | async }}</span>
|
<span>{{ link.label$ | async }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
</ng-container>
|
||||||
</nav>
|
</nav>
|
||||||
<div>
|
<div>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { TranslocoService } from '@ngneat/transloco';
|
import { TranslocoService } from '@ngneat/transloco';
|
||||||
|
|
||||||
|
import { RoleAccessName } from '@dsh/app/auth';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: 'operations.component.html',
|
templateUrl: 'operations.component.html',
|
||||||
})
|
})
|
||||||
@ -13,6 +15,7 @@ export class OperationsComponent {
|
|||||||
null,
|
null,
|
||||||
'payment-section',
|
'payment-section',
|
||||||
),
|
),
|
||||||
|
roles: [RoleAccessName.ViewPayments],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'invoices',
|
path: 'invoices',
|
||||||
@ -21,6 +24,7 @@ export class OperationsComponent {
|
|||||||
null,
|
null,
|
||||||
'payment-section',
|
'payment-section',
|
||||||
),
|
),
|
||||||
|
roles: [RoleAccessName.ViewInvoices],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'refunds',
|
path: 'refunds',
|
||||||
@ -29,6 +33,7 @@ export class OperationsComponent {
|
|||||||
null,
|
null,
|
||||||
'payment-section',
|
'payment-section',
|
||||||
),
|
),
|
||||||
|
roles: [RoleAccessName.ViewRefunds],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
|||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import { TranslocoModule } from '@ngneat/transloco';
|
import { TranslocoModule } from '@ngneat/transloco';
|
||||||
|
|
||||||
|
import { AuthModule } from '@dsh/app/auth';
|
||||||
import { LayoutModule } from '@dsh/components/layout';
|
import { LayoutModule } from '@dsh/components/layout';
|
||||||
import { ScrollUpModule } from '@dsh/components/navigation';
|
import { ScrollUpModule } from '@dsh/components/navigation';
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ import { OperationsComponent } from './operations.component';
|
|||||||
TranslocoModule,
|
TranslocoModule,
|
||||||
ScrollUpModule,
|
ScrollUpModule,
|
||||||
MatTabsModule,
|
MatTabsModule,
|
||||||
|
AuthModule,
|
||||||
],
|
],
|
||||||
declarations: [OperationsComponent],
|
declarations: [OperationsComponent],
|
||||||
})
|
})
|
||||||
|
@ -31,11 +31,18 @@ const PAYMENT_SECTION_ROUTES: Routes = [
|
|||||||
},
|
},
|
||||||
[RoleAccessName.ViewAnalytics],
|
[RoleAccessName.ViewAnalytics],
|
||||||
),
|
),
|
||||||
|
createPrivateRoute(
|
||||||
{
|
{
|
||||||
path: 'operations',
|
path: 'operations',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('./operations/operations.module').then((m) => m.OperationsModule),
|
import('./operations/operations.module').then((m) => m.OperationsModule),
|
||||||
},
|
},
|
||||||
|
[
|
||||||
|
RoleAccessName.ViewPayments,
|
||||||
|
RoleAccessName.ViewInvoices,
|
||||||
|
RoleAccessName.ViewRefunds,
|
||||||
|
],
|
||||||
|
),
|
||||||
createPrivateRoute(
|
createPrivateRoute(
|
||||||
{
|
{
|
||||||
path: 'reports',
|
path: 'reports',
|
||||||
@ -51,11 +58,16 @@ const PAYMENT_SECTION_ROUTES: Routes = [
|
|||||||
// },
|
// },
|
||||||
// [RoleAccessName.ViewPayouts]
|
// [RoleAccessName.ViewPayouts]
|
||||||
// ),
|
// ),
|
||||||
|
createPrivateRoute(
|
||||||
{
|
{
|
||||||
path: 'integrations',
|
path: 'integrations',
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('./integrations/integrations.module').then((m) => m.IntegrationsModule),
|
import('./integrations/integrations.module').then(
|
||||||
|
(m) => m.IntegrationsModule,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
[RoleAccessName.PaymentLinks, RoleAccessName.ApiKeys, RoleAccessName.Webhooks],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -3,7 +3,13 @@
|
|||||||
fxLayout="column"
|
fxLayout="column"
|
||||||
fxLayoutGap="32px"
|
fxLayoutGap="32px"
|
||||||
>
|
>
|
||||||
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign="end" fxLayoutGap="16px">
|
<div
|
||||||
|
*ngIf="'Claims' | isAccessAllowed"
|
||||||
|
fxLayout="column"
|
||||||
|
fxLayout.gt-sm="row"
|
||||||
|
fxLayoutAlign="end"
|
||||||
|
fxLayoutGap="16px"
|
||||||
|
>
|
||||||
<button color="accent" dsh-button (click)="createShop()">{{ t('createShop') }}</button>
|
<button color="accent" dsh-button (click)="createShop()">{{ t('createShop') }}</button>
|
||||||
</div>
|
</div>
|
||||||
<dsh-shops-list
|
<dsh-shops-list
|
||||||
|
@ -4,6 +4,7 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
|||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { TranslocoModule } from '@ngneat/transloco';
|
import { TranslocoModule } from '@ngneat/transloco';
|
||||||
|
|
||||||
|
import { AuthModule } from '@dsh/app/auth';
|
||||||
import { ShopCreationModule } from '@dsh/app/shared/components/shop-creation';
|
import { ShopCreationModule } from '@dsh/app/shared/components/shop-creation';
|
||||||
import { ButtonModule } from '@dsh/components/buttons';
|
import { ButtonModule } from '@dsh/components/buttons';
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ import { ShopsComponent } from './shops.component';
|
|||||||
ShopCreationModule,
|
ShopCreationModule,
|
||||||
ButtonModule,
|
ButtonModule,
|
||||||
TranslocoModule,
|
TranslocoModule,
|
||||||
|
AuthModule,
|
||||||
],
|
],
|
||||||
declarations: [ShopsComponent],
|
declarations: [ShopsComponent],
|
||||||
exports: [ShopsComponent],
|
exports: [ShopsComponent],
|
||||||
|
@ -59,7 +59,7 @@ export const toNavbarItemConfig = ({
|
|||||||
routerLink: NavbarRouterLink.Reports,
|
routerLink: NavbarRouterLink.Reports,
|
||||||
icon: BootstrapIconName.FileText,
|
icon: BootstrapIconName.FileText,
|
||||||
label: reports,
|
label: reports,
|
||||||
roles: [],
|
roles: [RoleAccessName.Reports],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
routerLink: NavbarRouterLink.Integrations,
|
routerLink: NavbarRouterLink.Integrations,
|
||||||
|
@ -5,7 +5,7 @@ import { map, pluck, shareReplay, switchMap } from 'rxjs/operators';
|
|||||||
|
|
||||||
import { MembersService } from '@dsh/app/api/organizations';
|
import { MembersService } from '@dsh/app/api/organizations';
|
||||||
import { SHARE_REPLAY_CONF } from '@dsh/app/custom-operators';
|
import { SHARE_REPLAY_CONF } from '@dsh/app/custom-operators';
|
||||||
import { ContextOrganizationService } from '@dsh/app/shared';
|
import { KeycloakTokenInfoService } from '@dsh/app/shared';
|
||||||
import { Initializable } from '@dsh/app/shared/types';
|
import { Initializable } from '@dsh/app/shared/types';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -16,16 +16,19 @@ export class OrganizationManagementService implements Initializable {
|
|||||||
shareReplay(SHARE_REPLAY_CONF),
|
shareReplay(SHARE_REPLAY_CONF),
|
||||||
);
|
);
|
||||||
isOrganizationOwner$: Observable<boolean> = defer(() =>
|
isOrganizationOwner$: Observable<boolean> = defer(() =>
|
||||||
combineLatest([
|
combineLatest([this.organization$, this.keycloakTokenInfoService.userID$]),
|
||||||
this.organization$,
|
|
||||||
this.contextOrganizationService.organization$.pipe(pluck('party')),
|
|
||||||
]),
|
|
||||||
).pipe(
|
).pipe(
|
||||||
map(([{ owner }, id]) => owner === id),
|
map(([{ owner }, id]) => owner === id),
|
||||||
shareReplay(SHARE_REPLAY_CONF),
|
shareReplay(SHARE_REPLAY_CONF),
|
||||||
);
|
);
|
||||||
isOrganizationAdmin$: Observable<boolean> = this.contextOrganizationService.member$.pipe(
|
isOrganizationAdmin$: Observable<boolean> = combineLatest([
|
||||||
map((member) => member.roles.findIndex((r) => r.roleId === RoleId.Administrator) !== -1),
|
this.members$,
|
||||||
|
this.keycloakTokenInfoService.userID$,
|
||||||
|
]).pipe(
|
||||||
|
map(([members, userId]) => members.find((m) => m.id === userId)),
|
||||||
|
map(
|
||||||
|
(member) => member?.roles?.findIndex?.((r) => r.roleId === RoleId.Administrator) !== -1,
|
||||||
|
),
|
||||||
shareReplay(SHARE_REPLAY_CONF),
|
shareReplay(SHARE_REPLAY_CONF),
|
||||||
);
|
);
|
||||||
hasAdminAccess$: Observable<boolean> = defer(() =>
|
hasAdminAccess$: Observable<boolean> = defer(() =>
|
||||||
@ -39,7 +42,7 @@ export class OrganizationManagementService implements Initializable {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private membersService: MembersService,
|
private membersService: MembersService,
|
||||||
private contextOrganizationService: ContextOrganizationService,
|
private keycloakTokenInfoService: KeycloakTokenInfoService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
init(organization: Organization) {
|
init(organization: Organization) {
|
||||||
|
@ -115,7 +115,7 @@
|
|||||||
"manageWebhooks": "Manage webhooks",
|
"manageWebhooks": "Manage webhooks",
|
||||||
"payments": "Payments",
|
"payments": "Payments",
|
||||||
"viewAnalytics": "View analytics",
|
"viewAnalytics": "View analytics",
|
||||||
"viewApiKey": "View the API key",
|
"viewApiKey": "View API keys",
|
||||||
"viewInvoices": "View invoices",
|
"viewInvoices": "View invoices",
|
||||||
"viewPayments": "View payments",
|
"viewPayments": "View payments",
|
||||||
"viewPayouts": "View payouts",
|
"viewPayouts": "View payouts",
|
||||||
|
@ -115,7 +115,7 @@
|
|||||||
"manageWebhooks": "Управление Webhooks",
|
"manageWebhooks": "Управление Webhooks",
|
||||||
"payments": "Платежи",
|
"payments": "Платежи",
|
||||||
"viewAnalytics": "Просмотр аналитики",
|
"viewAnalytics": "Просмотр аналитики",
|
||||||
"viewApiKey": "Просмотр API ключа",
|
"viewApiKey": "Просмотр API ключей",
|
||||||
"viewInvoices": "Просмотр инвойсов",
|
"viewInvoices": "Просмотр инвойсов",
|
||||||
"viewPayments": "Просмотр платежей",
|
"viewPayments": "Просмотр платежей",
|
||||||
"viewPayouts": "Просмотр возмещений",
|
"viewPayouts": "Просмотр возмещений",
|
||||||
|
Loading…
Reference in New Issue
Block a user