return cy.get('.cds--table-expand__button').first();
}
+ getResourcePage(content?: string) {
+ this.waitDataTableToLoad();
+ if (content) {
+ return cy
+ .contains('[cdstablerow] [cdstabledata]', content)
+ .parent('[cdstablerow]')
+ .contains('[cdstabledata] a', new RegExp(`^${content}$`));
+ }
+ return cy.get('[cdstablerow] [cdstabledata] a').first();
+ }
+
/**
* Gets column headers of table
*/
pages = pages;
columnIndex = {
- tenant: 2,
- account_id: 3,
- email: 4,
- max_users: 5,
- max_roles: 6,
- max_groups: 7,
- max_buckets: 8,
- max_access_keys: 9
+ tenant: 1,
+ account_id: 2,
+ email: 3,
+ max_users: 4,
+ max_roles: 5,
+ max_groups: 6,
+ max_buckets: 7,
+ max_access_keys: 8
};
@PageHelper.restrictTo(pages.create.url)
cy.get('td').eq(this.columnIndex.max_access_keys).should('have.text', 4);
});
- this.getExpandCollapseElement(account.name).click().wait(1000);
+ this.getResourcePage(account.name).click();
- cy.get('[data-testid="datatable-row-detail"]').first().as('accountDetailsTable');
- cy.get('@accountDetailsTable').find('legend').its(0).as('accountQuota');
- cy.get('@accountQuota').should('have.text', 'Account quota');
- cy.get('@accountDetailsTable').find('cd-table-key-value').its(0).as('accountQuotaTable');
+ cy.contains('section h2.user-account-resource-section__title', /Account\s+Quota/i)
+ .should('be.visible')
+ .closest('section')
+ .find('cd-table-key-value')
+ .first()
+ .as('accountQuotaTable');
cy.get('@accountQuotaTable')
.find('tbody tr')
.first()
.last()
.should('have.text', 'Yes');
- cy.get('@accountDetailsTable').find('legend').its(1).as('bucketQuota');
- cy.get('@bucketQuota').should('have.text', 'Bucket quota');
- cy.get('@accountDetailsTable').find('cd-table-key-value').its(1).as('bucketQuotaTable');
+ cy.contains('section h2.user-account-resource-section__title', /Bucket\s+Quota/i)
+ .should('be.visible')
+ .closest('section')
+ .find('cd-table-key-value')
+ .first()
+ .as('bucketQuotaTable');
cy.get('@bucketQuotaTable')
.find('tbody tr')
.first()
.find('td')
.last()
.should('have.text', 'Yes');
+
+ this.navigateTo();
}
@PageHelper.restrictTo(pages.create.url)
.eq(this.columnIndex.max_access_keys)
.should('have.text', account.max_access_keys);
});
- this.getExpandCollapseElement(account.name).click().wait(1000);
+ this.getResourcePage(account.name).click();
- cy.get('[data-testid="datatable-row-detail"]').first().as('accountDetailsTable');
- cy.get('@accountDetailsTable').find('legend').eq(0).as('accountQuota');
- cy.get('@accountQuota').should('have.text', 'Account quota');
- cy.get('@accountDetailsTable').find('cd-table-key-value').eq(0).as('accountQuotaTable');
+ cy.contains('section h2.user-account-resource-section__title', /Account\s+Quota/i)
+ .should('be.visible')
+ .closest('section')
+ .find('cd-table-key-value')
+ .first()
+ .as('accountQuotaTable');
cy.get('@accountQuotaTable').find('tbody tr').should('have.length', 3);
cy.get('@accountQuotaTable')
.find('tbody tr')
cy.get('td').last().should('have.text', '1.2 GiB');
});
- cy.get('@accountDetailsTable').find('legend').eq(1).as('bucketQuota');
- cy.get('@bucketQuota').should('have.text', 'Bucket quota');
- cy.get('@accountDetailsTable').find('cd-table-key-value').eq(1).as('bucketQuotaTable');
+ cy.contains('section h2.user-account-resource-section__title', /Bucket\s+Quota/i)
+ .should('be.visible')
+ .closest('section')
+ .find('cd-table-key-value')
+ .first()
+ .as('bucketQuotaTable');
cy.get('@bucketQuotaTable')
.find('tbody tr')
.first()
cy.get('td').first().should('have.text', 'Maximum size');
cy.get('td').last().should('have.text', '1.2 GiB');
});
+
+ this.navigateTo();
}
invalidCreate() {
beforeEach(() => {
cy.login();
accounts.navigateTo();
- accounts.getExpandCollapseElement(accountName).click();
- cy.contains('cds-tab-headers button[role="tab"]', 'Roles').click();
+ accounts.getResourcePage(accountName).click();
+ cy.contains('cds-sidenav-item a', /^Roles$/).click();
+ cy.location('hash').should('include', '/roles');
// Wait for the roles list to render
cy.get('cd-rgw-account-roles-list').should('exist');
});
accounts
.getTableRow(account.name)
.find('td')
- .eq(3)
+ .eq(2)
.invoke('text')
.then((acc_id: string) => {
cy.log(acc_id);
+++ /dev/null
-@if (selection) {
- <cds-tabs>
- <cds-tab
- heading="Quotas"
- i18n-heading
- >
- <div class="row">
- @if (selection.quota) {
- <div class="col-sm-6">
- <legend i18n>Account quota</legend>
- <cd-table-key-value [data]="quota"> </cd-table-key-value>
- </div>
- }
- @if (selection.bucket_quota) {
- <div class="col-sm-6">
- <legend i18n>Bucket quota</legend>
- <cd-table-key-value [data]="bucket_quota"> </cd-table-key-value>
- </div>
- }
- </div>
- </cds-tab>
- <cds-tab
- heading="Roles"
- i18n-heading
- >
- <div class="mt-4">
- <cd-rgw-account-roles-list
- [accountId]="selection.id"
- [accountName]="selection.name"
- ></cd-rgw-account-roles-list>
- </div>
- </cds-tab>
- </cds-tabs>
-}
+++ /dev/null
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { RgwUserAccountsDetailsComponent } from './rgw-user-accounts-details.component';
-import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
-import { TableKeyValueComponent } from '~/app/shared/datatable/table-key-value/table-key-value.component';
-import { CdDatePipe } from '~/app/shared/pipes/cd-date.pipe';
-
-describe('RgwUserAccountsDetailsComponent', () => {
- let component: RgwUserAccountsDetailsComponent;
- let fixture: ComponentFixture<RgwUserAccountsDetailsComponent>;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [RgwUserAccountsDetailsComponent, TableKeyValueComponent],
- providers: [DimlessBinaryPipe, CdDatePipe]
- }).compileComponents();
-
- fixture = TestBed.createComponent(RgwUserAccountsDetailsComponent);
- component = fixture.componentInstance;
- component.selection = { quota: {}, bucket_quota: {} };
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
+++ /dev/null
-import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
-import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
-
-@Component({
- selector: 'cd-rgw-user-accounts-details',
- templateUrl: './rgw-user-accounts-details.component.html',
- styleUrls: ['./rgw-user-accounts-details.component.scss'],
- standalone: false
-})
-export class RgwUserAccountsDetailsComponent implements OnChanges {
- @Input()
- selection: any;
- quota = {};
- bucket_quota = {};
-
- constructor(private dimlessBinary: DimlessBinaryPipe) {}
-
- ngOnChanges(changes: SimpleChanges): void {
- if (changes.selection && changes.selection.currentValue) {
- this.quota = this.createDisplayValues('quota');
- this.bucket_quota = this.createDisplayValues('bucket_quota');
- }
- }
-
- createDisplayValues(quota_type: string) {
- return {
- Enabled: this.selection[quota_type].enabled ? 'Yes' : 'No',
- 'Maximum size': this.selection[quota_type].enabled
- ? this.selection[quota_type].max_size <= -1
- ? 'Unlimited'
- : this.dimlessBinary.transform(this.selection[quota_type].max_size)
- : '-',
- 'Maximum objects': this.selection[quota_type].enabled
- ? this.selection[quota_type].max_objects <= -1
- ? 'Unlimited'
- : this.selection[quota_type].max_objects
- : '-'
- };
- }
-}
--- /dev/null
+import { convertToParamMap } from '@angular/router';
+import { expect } from '@jest/globals';
+
+import { RgwAccountDetailsBreadcrumbResolver } from './rgw-account-details-breadcrumb.resolver';
+
+describe('RgwAccountDetailsBreadcrumbResolver', () => {
+ it('should include account name in breadcrumb', () => {
+ const resolver = new RgwAccountDetailsBreadcrumbResolver();
+ const route: any = {
+ url: [{ path: 'account1' }],
+ pathFromRoot: [
+ { url: [] },
+ { url: [{ path: 'rgw' }] },
+ { url: [{ path: 'accounts' }] },
+ { url: [{ path: 'account1' }] }
+ ],
+ paramMap: convertToParamMap({ accountName: 'account1' }),
+ data: {
+ account: {
+ name: 'account1'
+ }
+ }
+ };
+
+ const result = resolver.resolve(route);
+
+ expect(result.length).toBe(1);
+ expect(result[0].text).toBe('account1');
+ expect(result[0].path).toBe('/rgw/accounts/account1');
+ });
+});
--- /dev/null
+import { Injectable } from '@angular/core';
+import { ActivatedRouteSnapshot } from '@angular/router';
+
+import { BreadcrumbsResolver, IBreadcrumb } from '~/app/shared/models/breadcrumbs';
+import { Account } from '../models/rgw-user-accounts';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class RgwAccountDetailsBreadcrumbResolver extends BreadcrumbsResolver {
+ resolve(route: ActivatedRouteSnapshot): IBreadcrumb[] {
+ const account = route.data?.account as Account | null;
+ const accountName = account?.name || route.paramMap.get('accountName') || '';
+
+ return [{ text: accountName, path: this.getFullPath(route) }];
+ }
+}
--- /dev/null
+import { TestBed } from '@angular/core/testing';
+import { convertToParamMap } from '@angular/router';
+import { of } from 'rxjs';
+
+import { RgwUserAccountsService } from '~/app/shared/api/rgw-user-accounts.service';
+import { RgwAccountDetailsResolver } from './rgw-account-details.resolver';
+
+describe('RgwAccountDetailsResolver', () => {
+ let resolver: RgwAccountDetailsResolver;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ RgwAccountDetailsResolver,
+ {
+ provide: RgwUserAccountsService,
+ useValue: {
+ list: () =>
+ of([
+ {
+ id: 'RGW11111111111111111',
+ name: 'account1'
+ },
+ {
+ id: 'RGW22222222222222222',
+ name: 'account2'
+ }
+ ])
+ }
+ }
+ ]
+ });
+
+ resolver = TestBed.inject(RgwAccountDetailsResolver);
+ });
+
+ it('should resolve account by account name', (done) => {
+ const route: any = {
+ paramMap: convertToParamMap({ accountName: 'account2' })
+ };
+
+ resolver.resolve(route).subscribe((account) => {
+ if (account?.id !== 'RGW22222222222222222') {
+ done(new Error('Failed to resolve account by account name'));
+ return;
+ }
+
+ done();
+ });
+ });
+});
--- /dev/null
+import { Injectable } from '@angular/core';
+import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
+import { Observable, of } from 'rxjs';
+import { catchError, map } from 'rxjs/operators';
+
+import { Account } from '../models/rgw-user-accounts';
+import { RgwUserAccountsService } from '~/app/shared/api/rgw-user-accounts.service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class RgwAccountDetailsResolver implements Resolve<Account | null> {
+ constructor(private rgwUserAccountsService: RgwUserAccountsService) {}
+
+ resolve(route: ActivatedRouteSnapshot): Observable<Account | null> {
+ const accountName = route.paramMap.get('accountName') ?? '';
+ if (!accountName) {
+ return of(null);
+ }
+
+ return this.rgwUserAccountsService.list(true).pipe(
+ map((accounts: Account[]) => {
+ const account = accounts.find((item) => item.name === accountName);
+ return account ?? null;
+ }),
+ catchError(() => of(null))
+ );
+ }
+}
--- /dev/null
+@if (selection) {
+ @switch (section) {
+ @case ('overview') {
+ <cd-resource-overview-card
+ title="Account details"
+ [columns]="4"
+ [fields]="overviewField"
+ >
+ </cd-resource-overview-card>
+
+ @if (selection.quota) {
+ <section class="cds-mt-6 cds-mb-6">
+ <h2
+ class="user-account-resource-section__title cds--type-heading-03"
+ i18n
+ >
+ Account Quota
+ </h2>
+ <p
+ class="user-account-resource-section__description cds--type-body-compact-01"
+ i18n
+ >
+ View the account quota details, including maximum object and size.
+ </p>
+ <cd-table-key-value [data]="quota"></cd-table-key-value>
+ </section>
+ }
+
+ @if (selection.bucket_quota) {
+ <section class="cds-mb-6">
+ <h2
+ class="user-account-resource-section__title cds--type-heading-03"
+ i18n
+ >
+ Bucket Quota
+ </h2>
+ <p
+ class="user-account-resource-section__description cds--type-body-compact-01"
+ i18n
+ >
+ View the bucket quota details, including maximum object and size.
+ </p>
+ <cd-table-key-value [data]="bucket_quota"></cd-table-key-value>
+ </section>
+ }
+ }
+ @case ('roles') {
+ <cd-rgw-account-roles-list
+ [accountId]="selection.id"
+ [accountName]="selection.name"
+ >
+ </cd-rgw-account-roles-list>
+ }
+ }
+} @else if (notFound) {
+ <cd-alert-panel
+ type="error"
+ i18n
+ >No account found.</cd-alert-panel
+ >
+}
--- /dev/null
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { Subscription } from 'rxjs';
+import { Account } from '../models/rgw-user-accounts';
+import { OverviewField } from '~/app/shared/components/resource-overview-card/resource-overview-card.component';
+import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
+
+@Component({
+ selector: 'cd-rgw-user-accounts-resource-page',
+ templateUrl: './rgw-user-accounts-resource-page.component.html',
+ styleUrls: ['./rgw-user-accounts-resource-page.component.scss'],
+ standalone: false
+})
+export class RgwUserAccountsResourcePageComponent implements OnInit, OnDestroy {
+ private sub = new Subscription();
+
+ section = '';
+ selection?: Account;
+ notFound = false;
+ overviewField: OverviewField[] = [];
+ quota: Record<string, string | number> = {};
+ bucket_quota: Record<string, string | number> = {};
+
+ constructor(
+ private route: ActivatedRoute,
+ private dimlessBinary: DimlessBinaryPipe
+ ) {}
+
+ ngOnInit(): void {
+ this.section = this.route.snapshot.data['section'] ?? 'overview';
+
+ this.sub.add(
+ this.route.parent?.data.subscribe((data) => {
+ const account = (data?.account ?? null) as Account | null;
+ this.applyAccount(account);
+ })
+ );
+ }
+
+ ngOnDestroy(): void {
+ this.sub.unsubscribe();
+ }
+
+ private applyAccount(account: Account | null): void {
+ this.notFound = !account;
+ if (!account) {
+ this.selection = undefined;
+ this.overviewField = [];
+ this.quota = {};
+ this.bucket_quota = {};
+ return;
+ }
+
+ this.selection = account;
+ this.overviewField = this.buildOverviewFields(account);
+ this.quota = this.createDisplayValues('quota');
+ this.bucket_quota = this.createDisplayValues('bucket_quota');
+ }
+
+ private buildOverviewFields(account: Account): OverviewField[] {
+ return [
+ {
+ label: $localize`Name`,
+ value: account.name
+ },
+ {
+ label: $localize`Tenant`,
+ value: account.tenant
+ },
+ {
+ label: $localize`Account id`,
+ value: account.id
+ },
+ {
+ label: $localize`Email address`,
+ value: account.email
+ },
+ {
+ label: $localize`Max users`,
+ value: account.max_users
+ },
+ {
+ label: $localize`Max roles`,
+ value: account.max_roles
+ },
+ {
+ label: $localize`Max groups`,
+ value: account.max_groups
+ },
+ {
+ label: $localize`Max. buckets`,
+ value: account.max_buckets
+ },
+ {
+ label: $localize`Max access keys`,
+ value: account.max_access_keys
+ }
+ ];
+ }
+
+ private createDisplayValues(
+ quotaType: 'quota' | 'bucket_quota'
+ ): Record<string, string | number> {
+ if (!this.selection?.[quotaType]) {
+ return {};
+ }
+
+ const quota = this.selection[quotaType];
+ return {
+ [$localize`Enabled`]: quota.enabled ? $localize`Yes` : $localize`No`,
+ [$localize`Maximum size`]: quota.enabled
+ ? quota.max_size <= -1
+ ? $localize`Unlimited`
+ : this.dimlessBinary.transform(quota.max_size)
+ : '-',
+ [$localize`Maximum objects`]: quota.enabled
+ ? quota.max_objects <= -1
+ ? $localize`Unlimited`
+ : quota.max_objects
+ : '-'
+ };
+ }
+}
--- /dev/null
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute } from '@angular/router';
+import { of } from 'rxjs';
+
+import { RgwUserAccountsResourcePageComponent } from './rgw-user-accounts-resource-page.component';
+import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
+
+describe('RgwUserAccountsResourcePageComponent', () => {
+ let component: RgwUserAccountsResourcePageComponent;
+ let fixture: ComponentFixture<RgwUserAccountsResourcePageComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [RgwUserAccountsResourcePageComponent],
+ providers: [
+ DimlessBinaryPipe,
+ {
+ provide: ActivatedRoute,
+ useValue: {
+ parent: {
+ data: of({
+ account: {
+ id: 'RGW11111111111111111',
+ tenant: '',
+ name: 'account1',
+ email: 'account1@ceph.com',
+ quota: {
+ enabled: false,
+ check_on_raw: false,
+ max_size: -1,
+ max_size_kb: -1,
+ max_objects: -1
+ },
+ bucket_quota: {
+ enabled: false,
+ check_on_raw: false,
+ max_size: -1,
+ max_size_kb: -1,
+ max_objects: -1
+ },
+ max_users: 1000,
+ max_roles: 1000,
+ max_groups: 1000,
+ max_buckets: 1000,
+ max_access_keys: 4
+ }
+ })
+ },
+ snapshot: {
+ data: {
+ section: 'overview'
+ }
+ }
+ }
+ }
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(RgwUserAccountsResourcePageComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should load account overview data', () => {
+ expect(component.selection?.name).toBe('account1');
+ expect(component.overviewField.length).toBeGreaterThan(0);
+ expect(component.overviewField.find((field) => field.label === 'Max users')?.value).toBe(1000);
+ });
+});
--- /dev/null
+<cd-sidebar-layout
+ class="rgw-account-details-layout"
+ [title]="accountName || accountId"
+ [items]="sidebarItems"
+>
+</cd-sidebar-layout>
--- /dev/null
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, convertToParamMap } from '@angular/router';
+import { of } from 'rxjs';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+
+import { RgwUserAccountsResourceSidebarComponent } from './rgw-user-accounts-resource-sidebar.component';
+
+describe('RgwUserAccountsResourceSidebarComponent', () => {
+ let component: RgwUserAccountsResourceSidebarComponent;
+ let fixture: ComponentFixture<RgwUserAccountsResourceSidebarComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [RgwUserAccountsResourceSidebarComponent],
+ providers: [
+ {
+ provide: ActivatedRoute,
+ useValue: {
+ paramMap: of(convertToParamMap({ accountName: 'account1' })),
+ data: of({
+ account: {
+ id: 'RGW11111111111111111',
+ name: 'account1'
+ }
+ })
+ }
+ }
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(RgwUserAccountsResourceSidebarComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should build sidebar items', () => {
+ expect(component.sidebarItems.length).toBe(2);
+ });
+
+ it('should expose resolved account name for header', () => {
+ expect(component.accountName).toBe('account1');
+ });
+});
--- /dev/null
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+import { Subscription } from 'rxjs';
+import { SidebarItem } from '~/app/shared/components/sidebar-layout/sidebar-layout.component';
+import { Account } from '../models/rgw-user-accounts';
+
+@Component({
+ selector: 'cd-rgw-user-accounts-resource-sidebar',
+ templateUrl: './rgw-user-accounts-resource-sidebar.component.html',
+ styleUrls: ['./rgw-user-accounts-resource-sidebar.component.scss'],
+ standalone: false
+})
+export class RgwUserAccountsResourceSidebarComponent implements OnInit, OnDestroy {
+ private sub = new Subscription();
+ readonly basePath = '/rgw/accounts';
+ accountNameRoute = '';
+ accountId = '';
+ accountName = '';
+ sidebarItems: SidebarItem[] = [];
+
+ constructor(private route: ActivatedRoute) {}
+
+ ngOnInit(): void {
+ this.sub.add(
+ this.route.paramMap.subscribe((pm: ParamMap) => {
+ this.accountNameRoute = pm.get('accountName') ?? '';
+ this.buildSidebarItems();
+ })
+ );
+
+ this.sub.add(
+ this.route.data.subscribe((data) => {
+ const account = (data?.account ?? null) as Account | null;
+ this.accountId = account?.id ?? '';
+ this.accountName = account?.name ?? this.accountNameRoute;
+ })
+ );
+ }
+
+ ngOnDestroy(): void {
+ this.sub.unsubscribe();
+ }
+
+ private buildSidebarItems(): void {
+ this.sidebarItems = [
+ {
+ label: $localize`Overview`,
+ route: [this.basePath, this.accountNameRoute, 'overview'],
+ routerLinkActiveOptions: { exact: true }
+ },
+ {
+ label: $localize`Roles`,
+ route: [this.basePath, this.accountNameRoute, 'roles'],
+ routerLinkActiveOptions: { exact: true }
+ }
+ ];
+ }
+}
[columns]="columns"
selectionType="single"
columnMode="flex"
- [hasDetails]="true"
- (setExpandedRow)="setExpandedRow($event)"
+ [hasDetails]="false"
(updateSelection)="updateSelection($event)"
identifier="id"
(fetchData)="getAccountsList($event)"
[tableActions]="tableActions"
>
</cd-table-actions>
- <cd-rgw-user-accounts-details
- *cdTableDetail
- [selection]="expandedRow"
- >
- </cd-rgw-user-accounts-details>
</cd-table>
permission: Permission;
tableActions: CdTableAction[] = [];
columns: CdTableColumn[] = [];
- accounts: Account[] = [];
+ accounts: (Account & { cdLink?: string })[] = [];
selection: CdTableSelection = new CdTableSelection();
declare staleTimeout: number;
{
name: $localize`Name`,
prop: 'name',
- flexGrow: 1
+ flexGrow: 1,
+ cellTransformation: CellTemplate.routerLink
},
{
name: $localize`Tenant`,
this.setTableRefreshTimeout();
this.rgwUserAccountsService.list(true).subscribe({
next: (accounts: Account[]) => {
- this.accounts = accounts;
+ this.accounts = accounts.map((account) => ({
+ ...account,
+ cdLink: `/rgw/accounts/${encodeURIComponent(account.name)}/overview`
+ }));
},
error: () => {
if (context) {
import { CephSharedModule } from '../shared/ceph-shared.module';
import { RgwUserAccountsComponent } from './rgw-user-accounts/rgw-user-accounts.component';
import { RgwUserAccountsFormComponent } from './rgw-user-accounts-form/rgw-user-accounts-form.component';
-import { RgwUserAccountsDetailsComponent } from './rgw-user-accounts-details/rgw-user-accounts-details.component';
+import { RgwUserAccountsResourceSidebarComponent } from './rgw-user-accounts-resource-sidebar/rgw-user-accounts-resource-sidebar.component';
+import { RgwUserAccountsResourcePageComponent } from './rgw-user-accounts-resource-page/rgw-user-accounts-resource-page.component';
+import { RgwAccountDetailsResolver } from './rgw-user-accounts-resource-page/rgw-account-details.resolver';
+import { RgwAccountDetailsBreadcrumbResolver } from './rgw-user-accounts-resource-page/rgw-account-details-breadcrumb.resolver';
import { RgwStorageClassDetailsComponent } from './rgw-storage-class-details/rgw-storage-class-details.component';
import { RgwStorageClassFormComponent } from './rgw-storage-class-form/rgw-storage-class-form.component';
import { RgwBucketTieringFormComponent } from './rgw-bucket-tiering-form/rgw-bucket-tiering-form.component';
RgwBucketDetailsComponent,
RgwUserListComponent,
RgwUserDetailsComponent,
- RgwBucketFormComponent,
RgwUserFormComponent,
RgwUserSwiftKeyModalComponent,
RgwUserS3KeyModalComponent,
RgwMultisiteTabsComponent,
RgwUserAccountsComponent,
RgwUserAccountsFormComponent,
- RgwUserAccountsDetailsComponent,
+ RgwUserAccountsResourceSidebarComponent,
+ RgwUserAccountsResourcePageComponent,
RgwStorageClassListComponent,
RgwStorageClassDetailsComponent,
RgwStorageClassFormComponent,
{
path: `${URLVerbs.EDIT}/:id`,
component: RgwUserAccountsFormComponent,
- data: { breadcrumbs: $localize`Edit account` }
+ data: { breadcrumbs: ActionLabels.EDIT }
+ },
+ {
+ path: ':accountName',
+ component: RgwUserAccountsResourceSidebarComponent,
+ data: { breadcrumbs: RgwAccountDetailsBreadcrumbResolver },
+ resolve: {
+ account: RgwAccountDetailsResolver
+ },
+ children: [
+ { path: '', redirectTo: 'overview', pathMatch: 'full' },
+ {
+ path: 'overview',
+ component: RgwUserAccountsResourcePageComponent,
+ data: { breadcrumbs: 'Overview', section: 'overview' }
+ },
+ {
+ path: 'roles',
+ component: RgwUserAccountsResourcePageComponent,
+ data: { breadcrumbs: 'Roles', section: 'roles' }
+ }
+ ]
}
]
},