]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Align RGW Account Role forms with updated UX design 69692/head
authorSagar Gopale <sagar.gopale@ibm.com>
Thu, 25 Jun 2026 07:07:25 +0000 (12:37 +0530)
committerSagar Gopale <sagar.gopale@ibm.com>
Thu, 9 Jul 2026 06:42:32 +0000 (12:12 +0530)
Signed-off-by: Sagar Gopale <sagar.gopale@ibm.com>
12 files changed:
src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/accounts.po.ts
src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/roles.po.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-account-role-form/rgw-account-role-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-account-role-form/rgw-account-role-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-accounts-form/rgw-user-accounts-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-accounts-form/rgw-user-accounts-form.component.scss
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-accounts-form/rgw-user-accounts-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-accounts/rgw-user-accounts.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts
src/pybind/mgr/dashboard/frontend/src/app/core/context/context.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/context/context.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/core/core.module.ts

index f58932982fd0ca3669a2f1d666d25d6828671e4c..f172c6265cd5dbe8b1dc6fce7fdc51bdab6b4c76 100644 (file)
@@ -32,6 +32,8 @@ export class AccountsPageHelper extends PageHelper {
     // Enter email
     cy.get('#email').type(account.email);
 
+    cy.contains('button', 'Show advanced settings').click();
+
     // Enter max buckets
     cy.get('input#max_buckets').should('exist').should('have.value', '1000');
 
@@ -49,7 +51,7 @@ export class AccountsPageHelper extends PageHelper {
     cy.get('input#account_checkbox_input').check({ force: true });
 
     // Click the create button and wait for account to be made
-    cy.contains('button', 'Create Account').click();
+    cy.contains('button', 'Create account').click();
 
     this.getFirstTableCell(account.name).should('have.text', account.name);
     this.getTableRow(account.name).within(() => {
@@ -106,6 +108,8 @@ export class AccountsPageHelper extends PageHelper {
     // Enter email
     cy.get('#email').clear().type(account.email);
 
+    cy.contains('button', 'Show advanced settings').click();
+
     // Enter max buckets
     this.selectOption('max_buckets_mode', 'Custom');
     cy.get('input#max_buckets').click().clear().type(account.max_buckets);
@@ -129,7 +133,7 @@ export class AccountsPageHelper extends PageHelper {
     cy.get('input#account_quota_max_objects').clear().type('200');
 
     // Click the create button and wait for account to be made
-    cy.contains('button', 'Edit Account').click();
+    cy.contains('button', 'Edit account').click();
 
     this.getTableRow(account.name).within(() => {
       cy.get('td').eq(this.columnIndex.tenant).should('have.text', account.tenant);
@@ -215,6 +219,8 @@ export class AccountsPageHelper extends PageHelper {
       .find('.cds--form-requirement')
       .should('have.text', ' Please enter a valid email ');
 
+    cy.contains('button', 'Show advanced settings').click();
+
     cy.get('input#max_buckets').click().clear().type('0').blur();
 
     cy.get('label[for=max_buckets]')
@@ -252,6 +258,8 @@ export class AccountsPageHelper extends PageHelper {
       .find('.cds--form-requirement')
       .should('have.text', ' Please enter a valid email ');
 
+    cy.contains('button', 'Show advanced settings').click();
+
     cy.get('input#max_buckets').click().clear().type('0').blur();
 
     cy.get('label[for=max_buckets]')
index 611a060236e791aab6eab44619a407400b9a1254..4ec20aa1105b747a8546dd71f19c87a8f7f00521 100644 (file)
@@ -38,7 +38,7 @@ export class RolesPageHelper extends PageHelper {
     cy.get('cds-number[formControlName="max_session_duration"] input')
       .clear()
       .type(maxSessionDuration.toString());
-    cy.get('cds-modal').contains('button', 'Edit').click();
+    cy.get('cds-modal').contains('button', 'Save').click();
     cy.get('cds-modal').should('not.exist');
 
     this.getRolesTableCell(this.columnIndex.roleName, name)
index 11fff876688822380f8d45ec2a0cc42187b8bead..641b3c9c94e15bb589e9dd1463f894fa69a1390a 100644 (file)
@@ -1,19 +1,38 @@
 <cds-modal size="md" [open]="open" [hasScrollingContent]="false" (overlaySelected)="closeModal()">
   <cds-modal-header (closeSelect)="closeModal()">
-    <h4 cdsModalHeaderLabel class="cds--type-label-01">{{ accountName }}</h4>
-    <h3 cdsModalHeaderHeading i18n>{{ mode }} role</h3>
+    @if (isEdit) {
+      <h4 cdsModalHeaderLabel class="cds--type-label-01" i18n>Role</h4>
+      <h3 cdsModalHeaderHeading i18n>Edit session duration</h3>
+      <p class="cds--modal-header__description cds--type-body-01 cds-mt-3" i18n>
+        Update the maximum session duration for this role.
+      </p>
+    } @else {
+      <h4 cdsModalHeaderLabel class="cds--type-label-01" i18n>User account</h4>
+      <h3 cdsModalHeaderHeading i18n>Create role</h3>
+      <p class="cds--modal-header__description cds--type-body-01 cds-mt-3" i18n>
+        Role grants temporary permissions to trusted users, applications, or services.
+      </p>
+    }
   </cds-modal-header>
 
   <form class="form" #formDir="ngForm" [formGroup]="form">
     <div cdsModalContent>
+      @if (!isEdit) {
+      <!-- Account -->
+      <div class="form-item">
+        <label class="cds--label cds--type-label-01" i18n>Account</label>
+        <p class="cds--type-body-01">{{ accountName }}</p>
+      </div>
+
       <!-- Role Name -->
       <div class="form-item">
         <cds-text-label
           label="Role name"
           for="role_name"
-          cdRequiredField="Role name"
           [invalid]="roleName.isInvalid"
           [invalidText]="roleNameError"
+          helperText="Unique name used to identify this role within the account."
+          i18n-helperText
           >Role name
           <input
             cdsText
@@ -23,7 +42,7 @@
             id="role_name"
             name="role_name"
             formControlName="role_name"
-            placeholder="Placeholder text"
+            placeholder="Enter a role name (e.g. BackupServiceRole)"
             [invalid]="roleName.isInvalid"
             [autofocus]="true"
             modal-primary-focus
         </ng-template>
       </div>
 
+      <!-- Role (Edit only) -->
+      } @else {
+      <div class="form-item">
+        <label class="cds--label cds--type-label-01" i18n>Role</label>
+        <p class="cds--type-body-01">
+          <cds-tooltip-definition
+            align="right"
+            [description]="accountTooltip"
+            [openOnHover]="true"
+            [caret]="true"
+            [highContrast]="true"
+            [dropShadow]="true"
+            [autoAlign]="true"
+          >
+            {{ form.getValue('role_name') }}
+          </cds-tooltip-definition>
+        </p>
+        <ng-template #accountTooltip>
+          <div>
+            User account:<br />
+            <strong>{{ accountName }}</strong>
+          </div>
+        </ng-template>
+      </div>
+      }
+
       <!-- Path & Assume Role Policy Document (Create only) -->
       @if (!isEdit) {
       <!-- Path -->
       <div class="form-item">
         <cds-text-label
-          label="Path"
+          label="Role path"
           for="role_path"
-          cdRequiredField="Path"
           [invalid]="rolePath.isInvalid"
           [invalidText]="pathError"
-          >Path
+          helperText='Specifies the hierarchy or namespace for the role. Use "/" for the root path.'
+          i18n-helperText
+          >Role path
           <input
             cdsText
             cdValidate
             id="role_path"
             name="role_path"
             formControlName="role_path"
-            placeholder="Placeholder text"
+            placeholder="Enter path (/applications/)"
             [invalid]="rolePath.isInvalid"
           />
         </cds-text-label>
           labelInputID="role_assume_policy_doc"
           [invalid]="policyDoc.isInvalid"
           [invalidText]="policyError"
-          cdRequiredField="Trust Policy"
+          helperText="Defines which principals can assume this role. Enter a valid JSON policy document."
+          i18n-helperText
           >Trust policy
           <textarea
             cdsTextArea
             id="role_assume_policy_doc"
             name="role_assume_policy_doc"
             formControlName="role_assume_policy_doc"
-            placeholder="Placeholder text"
+            placeholder="Enter a JSON trust policy document"
             rows="8"
             cols="200"
             [invalid]="policyDoc.isInvalid"
       </div>
       }
 
-      <!-- Max Session Duration (Edit only) -->
+      <!-- Maximum Session Duration (Edit only) -->
       @if (isEdit) {
       <div class="form-item">
         <cds-number
-          label="Max Session Duration (Hours)"
+          label="Maximum session duration (hours)"
           cdValidate
           #maxSessionDuration="cdValidate"
           [invalid]="maxSessionDuration.isInvalid"
     <cd-form-button-panel
       (submitActionEvent)="onSubmit()"
       [form]="form"
-      [submitText]="mode"
+      [submitText]="isEdit ? 'Save' : 'Create'"
       [modalForm]="true"
     ></cd-form-button-panel>
   </form>
index 8cd266cdf468cd0d0387f36b7782abc8c7fb36dc..ca5db5d287e018c469b102f78fba149c17ba540f 100644 (file)
@@ -88,7 +88,8 @@ export class RgwAccountRoleFormComponent extends BaseModal implements OnInit {
           next: () => {
             this.notificationService.show(
               NotificationType.success,
-              $localize`Role updated successfully`
+              $localize`Session duration updated`,
+              $localize`The maximum session duration for role "${this.roleName}" has been updated to ${payload.max_session_duration} hours.`
             );
             this.closeModal();
           },
index 7233732e63f8b9e69c49bd497bc096158b16aeca..9a29716ad9e99d5060fb7f6fac0b1876932f0056 100644 (file)
@@ -7,20 +7,23 @@
           novalidate>
 
       <div i18n="form title"
-           class="form-header">{{ action | titlecase }} {{ resource | upperFirst }}
+           class="form-header cds--type-heading-04 cds-mb-5">{{ action | titlecase }} {{ resource | lowercase }}
       </div>
 
+      <h4 class="cds--type-heading-03 cds-mb-4" i18n>Account details</h4>
+
       <!-- Account Name -->
       <div class="form-item">
-        <cds-text-label label="Account Name"
+        <cds-text-label label="Account name"
                         for="acc_name"
-                        cdRequiredField="Account Name"
                         [invalid]="!accountForm.controls.name.valid && accountForm.controls.name.dirty"
                         [invalidText]="accountIdError"
-                        i18n>Account Name
+                        helperText="Name must start and end in alphanumeric characters (3 to 63). Characters allowed: lowercase, numbers and nonconsecutive dots and hyphens."
+                        i18n-helperText
+                        i18n>Account name
           <input cdsText
                  type="text"
-                 placeholder="Enter account name"
+                 placeholder="for example, team-storage-01"
                  id="acc_name"
                  name="acc_name"
                  formControlName="name"/>
       </div>
       <!-- Tenant -->
       <div class="form-item">
-        <cds-text-label label="tenant"
+        <cds-text-label label="Tenant (optional)"
                         for="tenant"
-                        i18n>Tenant
+                        helperText="Associates this account with an existing tenant namespace."
+                        i18n-helperText
+                        i18n>Tenant (optional)
           <input cdsText
                  type="text"
-                 placeholder="Enter tenant"
+                 placeholder="for example, finance-team"
                  id="tenant"
                  name="tenant"
                  formControlName="tenant"/>
 
       <!-- Email -->
       <div class="form-item">
-        <cds-text-label label="email"
+        <cds-text-label label="Email address (optional)"
                         for="email"
                         [invalid]="!accountForm.controls.email.valid && accountForm.controls.email.dirty"
                         [invalidText]="emailError"
-                        i18n>Email
+                        helperText="Contact for quota alerts and notifications."
+                        i18n-helperText
+                        i18n>Email address (optional)
           <input cdsText
                  type="text"
-                 placeholder="Enter email"
+                 placeholder="for example, admin@example.com"
                  id="email"
                  name="email"
                  formControlName="email"/>
         </ng-template>
       </div>
 
-      <div class="form-item form-item-append"
-           cdsRow>
-        <div cdsCol>
-          <!-- Max. bucket mode -->
-          <ng-container *ngTemplateOutlet="selectModeTemplate;context: { formControl: 'max_buckets_mode' }"></ng-container>
-        </div>
-        <div cdsCol>
-        <!-- Max buckets -->
-        <span *ngIf="1 == accountForm.get('max_buckets_mode').value">
-          <ng-container *ngTemplateOutlet="accountMaxValueTemplate;context: { formControl: 'max_buckets' }"></ng-container>
-          </span>
+      <div class="form-item cds-mt-5 cds-mb-3">
+        <h4 class="cds--type-heading-03" i18n>Advanced settings</h4>
+        <p class="cds--type-body-01" i18n>Configure resource limits and quota for this account</p>
+        <div class="cds-mt-3">
+          <button type="button" class="cds--btn cds--btn--ghost advanced-settings-btn" (click)="showAdvanced = !showAdvanced">
+            {{ showAdvanced ? 'Hide advanced settings' : 'Show advanced settings' }}
+            <svg cdsIcon="chevron--down" *ngIf="!showAdvanced" size="16" class="cds--btn__icon"></svg>
+            <svg cdsIcon="chevron--up" *ngIf="showAdvanced" size="16" class="cds--btn__icon"></svg>
+          </button>
         </div>
       </div>
 
-      <div class="form-item form-item-append"
-           cdsRow>
-        <div cdsCol>
-          <!-- Max. users mode -->
-          <ng-container *ngTemplateOutlet="selectModeTemplate;context: { formControl: 'max_users_mode' }"></ng-container>
-        </div>
-        <div cdsCol>
-        <!-- Max users -->
-        <span *ngIf="1 == accountForm.get('max_users_mode').value">
-          <ng-container *ngTemplateOutlet="accountMaxValueTemplate;context: { formControl: 'max_users' }"></ng-container>
-          </span>
-        </div>
-      </div>
+      <div *ngIf="showAdvanced">
+        <h5 class="cds--type-heading-02 cds-mb-2" i18n>Resource limits</h5>
+        <p class="cds--type-body-01 cds-mb-4" i18n>Set the mode and maximum allowed values for each resource type. Select Custom to specify a limit.</p>
 
-      <div class="form-item form-item-append"
-           cdsRow>
-        <div cdsCol>
-          <!-- Max. roles mode -->
-          <ng-container *ngTemplateOutlet="selectModeTemplate;context: { formControl: 'max_roles_mode' }"></ng-container>
+        <div class="form-item form-item-append" cdsRow>
+          <div cdsCol>
+            <!-- Max. bucket mode -->
+            <ng-container *ngTemplateOutlet="selectModeTemplate;context: { formControl: 'max_buckets_mode' }"></ng-container>
+          </div>
+          <div cdsCol>
+            <!-- Max buckets -->
+            <ng-container *ngTemplateOutlet="accountMaxValueTemplate;context: { formControl: 'max_buckets' }"></ng-container>
+          </div>
         </div>
-        <div cdsCol>
-        <!-- Max roles -->
-        <span *ngIf="1 == accountForm.get('max_roles_mode').value">
-          <ng-container *ngTemplateOutlet="accountMaxValueTemplate;context: { formControl: 'max_roles' }"></ng-container>
-        </span>
-        </div>
-      </div>
 
-      <div class="form-item form-item-append"
-           cdsRow>
-        <div cdsCol>
-          <!-- Max. group mode -->
-          <ng-container *ngTemplateOutlet="selectModeTemplate;context: { formControl: 'max_groups_mode' }"></ng-container>
+        <div class="form-item form-item-append" cdsRow>
+          <div cdsCol>
+            <!-- Max. users mode -->
+            <ng-container *ngTemplateOutlet="selectModeTemplate;context: { formControl: 'max_users_mode' }"></ng-container>
+          </div>
+          <div cdsCol>
+            <!-- Max users -->
+            <ng-container *ngTemplateOutlet="accountMaxValueTemplate;context: { formControl: 'max_users' }"></ng-container>
+          </div>
         </div>
-        <div cdsCol>
-        <!-- Max group -->
-        <span *ngIf="1 == accountForm.get('max_groups_mode').value">
-          <ng-container *ngTemplateOutlet="accountMaxValueTemplate;context: { formControl: 'max_groups' }"></ng-container>
-          </span>
+
+        <div class="form-item form-item-append" cdsRow>
+          <div cdsCol>
+            <!-- Max. roles mode -->
+            <ng-container *ngTemplateOutlet="selectModeTemplate;context: { formControl: 'max_roles_mode' }"></ng-container>
+          </div>
+          <div cdsCol>
+            <!-- Max roles -->
+            <ng-container *ngTemplateOutlet="accountMaxValueTemplate;context: { formControl: 'max_roles' }"></ng-container>
+          </div>
         </div>
-      </div>
 
-      <div class="form-item form-item-append"
-           cdsRow>
-        <div cdsCol>
-          <!-- Max. acess keys mode -->
-          <ng-container *ngTemplateOutlet="selectModeTemplate;context: { formControl: 'max_access_keys_mode' }"></ng-container>
+        <div class="form-item form-item-append" cdsRow>
+          <div cdsCol>
+            <!-- Max. group mode -->
+            <ng-container *ngTemplateOutlet="selectModeTemplate;context: { formControl: 'max_groups_mode' }"></ng-container>
+          </div>
+          <div cdsCol>
+            <!-- Max group -->
+            <ng-container *ngTemplateOutlet="accountMaxValueTemplate;context: { formControl: 'max_groups' }"></ng-container>
+          </div>
         </div>
-        <div cdsCol>
-        <!-- Max acess keys -->
-        <span *ngIf="1 == accountForm.get('max_access_keys_mode').value">
-          <ng-container *ngTemplateOutlet="accountMaxValueTemplate;context: { formControl: 'max_access_keys' }"></ng-container>
-        </span>
+
+        <div class="form-item form-item-append" cdsRow>
+          <div cdsCol>
+            <!-- Max. access keys mode -->
+            <ng-container *ngTemplateOutlet="selectModeTemplate;context: { formControl: 'max_access_keys_mode' }"></ng-container>
+          </div>
+          <div cdsCol>
+            <!-- Max access keys -->
+            <ng-container *ngTemplateOutlet="accountMaxValueTemplate;context: { formControl: 'max_access_keys' }"></ng-container>
+          </div>
         </div>
-      </div>
 
-      <!-- Account Quota -->
-      <div class="form-item">
-        <ng-container *ngTemplateOutlet="quotaTemplate;context: { formControl: {
-                      enabled: 'account_quota_enabled',
-                      unlimitedSize: 'account_quota_max_size_unlimited',
-                      maxSize: 'account_quota_max_size',
-                      unlimitedObjects: 'account_quota_max_objects_unlimited',
-                      maxObjects: 'account_quota_max_objects'
-                      },
-                      quotaType: 'account'
-                      }">
-        </ng-container>
-      </div>
+        <h5 class="cds--type-heading-02 cds-mb-2 cds-mt-5" i18n>Quotas</h5>
+        <p class="cds--type-body-01 cds-mb-4" i18n>Define storage usage limits for the account and the buckets it owns. Quotas help control capacity consumption and enforce resource allocation policies.</p>
 
-      <!-- Bucket Quota -->
-      <div class="form-item">
-        <ng-container *ngTemplateOutlet="quotaTemplate;
-                      context: {
-                      formControl: {
-                      enabled: 'bucket_quota_enabled',
-                      unlimitedSize: 'bucket_quota_max_size_unlimited',
-                      maxSize: 'bucket_quota_max_size',
-                      unlimitedObjects: 'bucket_quota_max_objects_unlimited',
-                      maxObjects: 'bucket_quota_max_objects'
-                      },
-                      quotaType: 'bucket'
-                      }">
-        </ng-container>
+        <!-- Account Quota -->
+        <div class="form-item">
+          <ng-container *ngTemplateOutlet="quotaTemplate;context: { formControl: {
+                        enabled: 'account_quota_enabled',
+                        unlimitedSize: 'account_quota_max_size_unlimited',
+                        maxSize: 'account_quota_max_size',
+                        unlimitedObjects: 'account_quota_max_objects_unlimited',
+                        maxObjects: 'account_quota_max_objects'
+                        },
+                        quotaType: 'account'
+                        }">
+          </ng-container>
+        </div>
+
+        <!-- Bucket Quota -->
+        <div class="form-item">
+          <ng-container *ngTemplateOutlet="quotaTemplate;
+                        context: {
+                        formControl: {
+                        enabled: 'bucket_quota_enabled',
+                        unlimitedSize: 'bucket_quota_max_size_unlimited',
+                        maxSize: 'bucket_quota_max_size',
+                        unlimitedObjects: 'bucket_quota_max_objects_unlimited',
+                        maxObjects: 'bucket_quota_max_objects'
+                        },
+                        quotaType: 'bucket'
+                        }">
+          </ng-container>
+        </div>
       </div>
       <cd-form-button-panel (submitActionEvent)="submit()"
                             [form]="formDir"
-                            [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"
+                            [submitText]="(action | titlecase) + ' ' + (resource | lowercase)"
                             wrappingClass="text-right"></cd-form-button-panel>
 
     </form>
   <cds-select [formControlName]="formControl"
               [name]="formControl"
               [id]="formControl"
-              label="{{formControl.split('_')[1] | upperFirst}} Mode"
+              label="{{formControl.split('_')[1] | upperFirst}} mode"
               (change)="onModeChange($event.target.value, formControl)"
               [helperText]="getHelperTextForMode(formControl)">
     <option value="0">Unlimited</option>
 <ng-template #accountMaxValueTemplate
              let-formControl="formControl"
              [formGroup]="accountForm">
-<cds-number [id]="formControl"
-            [name]="formControl"
-            formControlName="{{formControl}}"
-            label="{{formControl.split('_')[0] | upperFirst}}. {{formControl.split('_').length > 2 ? formControl.split('_')[1]+' '+formControl.split('_')[2]: formControl.split('_')[1]}}"
-            [min]="1"
-            cdRequiredField="{{formControl.split('_')[0] | upperFirst}}. {{formControl.split('_').length > 2 ? formControl.split('_')[1]+' '+formControl.split('_')[2]: formControl.split('_')[1]}}"
-            [invalid]="!accountForm.controls[formControl].valid && accountForm.controls[formControl].dirty"
-            [invalidText]="maxValError"></cds-number>
+  <cds-number *ngIf="accountForm.get(formControl + '_mode').value == '1'"
+              [id]="formControl"
+              [name]="formControl"
+              formControlName="{{formControl}}"
+              label="Maximum {{formControl.replace('max_', '').replace('_', ' ')}}"
+              [min]="1"
+              [invalid]="!accountForm.controls[formControl].valid && accountForm.controls[formControl].dirty"
+              [invalidText]="maxValError"></cds-number>
+  
+  <cds-text-label *ngIf="accountForm.get(formControl + '_mode').value != '1'"
+                  label="Maximum {{formControl.replace('max_', '').replace('_', ' ')}}"
+                  for="{{formControl}}_disabled">
+    Maximum {{formControl.replace('max_', '').replace('_', ' ')}}
+    <input cdsText
+           type="text"
+           [id]="formControl + '_disabled'"
+           name="{{formControl}}_disabled"
+           [ngModel]="'-'"
+           [ngModelOptions]="{standalone: true}"
+           [disabled]="true" />
+  </cds-text-label>
+
 <ng-template #maxValError>
   <span *ngIf="accountForm.showError(formControl, formDir, 'required')"
         i18n>This field is required.</span>
              let-quotaType="quotaType"
              let-formControl="formControl"
              [formGroup]="accountForm">
-  <fieldset class="cds--fieldset">
-    <legend class="cds--label">{{quotaType | upperFirst}} Quota</legend>
-    <div *ngIf="quotaType == 'account';else bucket"
-         class="quota-heading">Set quota on account owned by users.</div>
-    <ng-template #bucket>
-      <div class="quota-heading">
-        Set quota on buckets owned by an account.
-      </div>
-    </ng-template>
+  <fieldset class="cds--fieldset cds-mb-4">
+    <legend class="cds--label">{{quotaType | upperFirst}} quota</legend>
     <!-- Enabled -->
-    <cds-checkbox [formControlName]="formControl.enabled"
-                  [id]="quotaType+'_checkbox'">
-      Enabled
+    <cds-checkbox *ngIf="quotaType == 'account'" [formControlName]="formControl.enabled"
+                  [id]="quotaType+'_checkbox'" class="cds-mb-3">
+      Enable account quota
+    </cds-checkbox >
+    <cds-checkbox *ngIf="quotaType == 'bucket'" [formControlName]="formControl.enabled"
+                  [id]="quotaType+'_checkbox'" class="cds-mb-3">
+      Enable bucket quota
     </cds-checkbox >
+    <p class="cds--type-body-01 cds-mb-4" *ngIf="quotaType == 'account'">Applies a storage quota to each user account.</p>
+    <p class="cds--type-body-01 cds-mb-4" *ngIf="quotaType == 'bucket'">Limits the total storage available across all buckets in an account.</p>
     <div class="quota-sub-block"
          *ngIf="accountForm.controls[formControl.enabled].value">
       <!-- Unlimited size -->
            *ngIf="accountForm.controls[formControl.enabled].value && !accountForm.getValue(formControl.unlimitedSize)">
         <cds-text-label [label]="formControl.maxSize"
                         [for]="formControl.maxSize"
-                        cdRequiredField="Max. size"
                         [invalid]="!accountForm.controls[formControl.maxSize].valid && accountForm.controls[formControl.maxSize].dirty"
                         [invalidText]="quotaSizeError"
-                        i18n>Max. size
+                        i18n>Maximum size
           <input cdsText
                  type="text"
                  placeholder="Enter size"
            *ngIf="accountForm.controls[formControl.enabled].value && !accountForm.getValue(formControl.unlimitedObjects)">
         <cds-text-label [label]="formControl.maxObjects"
                         [for]="formControl.maxObjects"
-                        cdRequiredField="Max. objects"
                         [invalid]="!accountForm.controls[formControl.maxObjects].valid && accountForm.controls[formControl.maxObjects].dirty"
                         [invalidText]="quotaObjectError"
-                        i18n>Max. objects
+                        i18n>Maximum objects
           <input cdsText
                  type="number"
                  placeholder="Enter number of objects"
index 1c1506d7afea7c20eaa436e2a159e5645da6a3c2..d90ab47aeda207c867d90b239a9560a3feb9abba 100644 (file)
@@ -25,6 +25,7 @@ export class RgwUserAccountsFormComponent extends CdForm implements OnInit {
   action: string;
   resource: string;
   editing: boolean = false;
+  showAdvanced: boolean = false;
   submitObservables: Observable<Object>[] = [];
 
   constructor(
index 047b89f621d99aaffa5cff9698c4d5fad3ea0ab2..dddae94c99e73996ac0ae1d47822f4876fdfbe66 100644 (file)
@@ -2,7 +2,7 @@
 <legend i18n>
   User Accounts
   <cd-help-text>
-    Administrators can assign unique credentials to users or applications, enabling granular access control and enhancing security across the cluster.
+    Create and manage accounts and their permissions.
   </cd-help-text>
 </legend>
 <cd-table #table
index 585f3c25f1272f23d0fcbf0efed724656bfff068..86ee46d60fc57ecc37f507288a163f9228ca00f7 100644 (file)
@@ -96,6 +96,8 @@ import ShareIcon from '@carbon/icons/es/share/16';
 import ViewIcon from '@carbon/icons/es/view/16';
 import PasswordIcon from '@carbon/icons/es/password/16';
 import ArrowDownIcon from '@carbon/icons/es/arrow--down/16';
+import ChevronDownIcon from '@carbon/icons/es/chevron--down/16';
+import ChevronUpIcon from '@carbon/icons/es/chevron--up/16';
 import ProgressBarRoundIcon from '@carbon/icons/es/progress-bar--round/32';
 import ToolsIcon from '@carbon/icons/es/tools/32';
 import UserAccessLocked from '@carbon/icons/es/user--access-locked/16';
@@ -248,6 +250,8 @@ export class RgwModule {
       ViewIcon,
       PasswordIcon,
       ArrowDownIcon,
+      ChevronDownIcon,
+      ChevronUpIcon,
       ProgressBarRoundIcon,
       ToolsIcon,
       UserAccessLocked
@@ -287,12 +291,12 @@ const routes: Routes = [
       {
         path: URLVerbs.CREATE,
         component: RgwUserAccountsFormComponent,
-        data: { breadcrumbs: ActionLabels.CREATE }
+        data: { breadcrumbs: $localize`Create account` }
       },
       {
         path: `${URLVerbs.EDIT}/:id`,
         component: RgwUserAccountsFormComponent,
-        data: { breadcrumbs: ActionLabels.EDIT }
+        data: { breadcrumbs: $localize`Edit account` }
       }
     ]
   },
index f04a96b88cfdb62e188dc5616ce4419fec487858..bae77dfdbfd45d345d0c563273d210ec5817b648 100644 (file)
@@ -2,22 +2,24 @@
   <ng-container *ngIf="data.ftMap && data.ftMap.rgw && permissions.rgw.read && isRgwRoute && data.daemons.length > 1">
     <div class="cd-context-bar pt-3 pb-3">
       <span class="me-1"
-            i18n>Selected Object Gateway:</span>
+            i18n>Object gateway:</span>
       <div ngbDropdown
            placement="bottom-left"
            class="d-inline-block ms-2">
         <button ngbDropdownToggle
                 class="btn btn-outline-info ctx-bar-selected-rgw-daemon"
                 i18n-title
-                title="Select Object Gateway">
-          {{ data.selectedDaemon.id }} ( {{ data.selectedDaemon.zonegroup_name }} )
+                title="Object gateway">
+          {{ data.selectedDaemon.id }}
+          <cds-tag type="blue" size="sm">{{ data.selectedDaemon.zonegroup_name }}</cds-tag>
         </button>
         <div ngbDropdownMenu>
           <ng-container *ngFor="let daemon of data.daemons">
             <button ngbDropdownItem
                     class="ctx-bar-available-rgw-daemon"
                     (click)="onDaemonSelection(daemon)">
-              {{ daemon.id }} ( {{ daemon.zonegroup_name }} )
+              {{ daemon.id }}
+              <cds-tag type="blue" size="sm">{{ daemon.zonegroup_name }}</cds-tag>
             </button>
           </ng-container>
         </div>
index 9512e3183ab53e0a012a1ecf009bf0683a7518a5..865e9b26f7090fb24349c17e98c747bd3f90b08d 100644 (file)
@@ -12,6 +12,8 @@ import {
   FeatureTogglesService
 } from '~/app/shared/services/feature-toggles.service';
 import { configureTestBed, RgwHelper } from '~/testing/unit-test-helper';
+import { TagModule } from 'carbon-components-angular';
+import { RgwDaemonService } from '~/app/shared/api/rgw-daemon.service';
 import { ContextComponent } from './context.component';
 
 describe('ContextComponent', () => {
@@ -29,7 +31,7 @@ describe('ContextComponent', () => {
 
   configureTestBed({
     declarations: [ContextComponent],
-    imports: [HttpClientTestingModule, RouterTestingModule]
+    imports: [HttpClientTestingModule, RouterTestingModule, TagModule]
   });
 
   beforeEach(() => {
@@ -46,6 +48,7 @@ describe('ContextComponent', () => {
     ftMap = new FeatureTogglesMap();
     ftMap.rgw = true;
     getFeatureTogglesSpy.and.returnValue(of(ftMap));
+    TestBed.inject(RgwDaemonService).selectDaemon(null);
     fixture = TestBed.createComponent(ContextComponent);
     component = fixture.componentInstance;
   });
@@ -69,13 +72,13 @@ describe('ContextComponent', () => {
     const selectedDaemon = fixture.debugElement.nativeElement.querySelector(
       '.ctx-bar-selected-rgw-daemon'
     );
-    expect(selectedDaemon.textContent).toEqual(' daemon2 ( zonegroup2 ) ');
+    expect(selectedDaemon.textContent).toEqual(' daemon2 zonegroup2');
 
     const availableDaemons = fixture.debugElement.nativeElement.querySelectorAll(
       '.ctx-bar-available-rgw-daemon'
     );
     expect(availableDaemons.length).toEqual(daemonList.length);
-    expect(availableDaemons[0].textContent).toEqual(' daemon1 ( zonegroup1 ) ');
+    expect(availableDaemons[0].textContent).toEqual(' daemon1 zonegroup1');
     component.ngOnDestroy();
   }));
 
@@ -94,7 +97,7 @@ describe('ContextComponent', () => {
     const selectedDaemon = fixture.debugElement.nativeElement.querySelector(
       '.ctx-bar-selected-rgw-daemon'
     );
-    expect(selectedDaemon.textContent).toEqual(' daemon3 ( zonegroup3 ) ');
+    expect(selectedDaemon.textContent).toEqual(' daemon3 zonegroup3');
     component.ngOnDestroy();
   }));
 });
index dc4e1c189ab38d8c9281c1b66afbbcda535f5e80..4ed2ad784b35e716078e3167713d8b12f55e0bcd 100644 (file)
@@ -8,7 +8,8 @@ import {
   PlaceholderModule,
   IconModule,
   ThemeModule,
-  ButtonModule
+  ButtonModule,
+  TagModule
 } from 'carbon-components-angular';
 
 import { ContextComponent } from '~/app/core/context/context.component';
@@ -30,7 +31,8 @@ import { NavigationModule } from './navigation/navigation.module';
     PlaceholderModule,
     IconModule,
     ThemeModule,
-    ButtonModule
+    ButtonModule,
+    TagModule
   ],
   exports: [NavigationModule],
   declarations: [