]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard : Carbonize -> Report an issue modal 66048/head
authorAbhishek Desai <abhishek.desai1@ibm.com>
Sat, 11 Oct 2025 20:39:08 +0000 (02:09 +0530)
committerAbhishek Desai <abhishek.desai1@ibm.com>
Fri, 24 Oct 2025 07:23:53 +0000 (12:53 +0530)
fixes : https://tracker.ceph.com/issues/73192
Signed-off-by: Abhishek Desai <abhishek.desai1@ibm.com>
(cherry picked from commit 871740fef4427320fb427ab13c3002325019a3d4)

 Conflicts:
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard.module.ts

src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/feedback/feedback.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/feedback/feedback.component.scss
src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/feedback/feedback.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/feedback/feedback.component.ts
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.ts

index fdcdaa762a6c4f013983c95ee5e0204fab6c43a0..d75ed7ebdf95584c66d6ef333ae3af0b8aeb7737 100644 (file)
@@ -19,7 +19,7 @@ import { MdsDashboardSummaryPipe } from './mds-dashboard-summary.pipe';
 import { MgrDashboardSummaryPipe } from './mgr-dashboard-summary.pipe';
 import { MonSummaryPipe } from './mon-summary.pipe';
 import { osdDashboardSummaryPipe } from './osd-dashboard-summary.pipe';
-import { ToggletipModule } from 'carbon-components-angular';
+import { InputModule, ModalModule, SelectModule, ToggletipModule } from 'carbon-components-angular';
 
 @NgModule({
   imports: [
@@ -32,7 +32,10 @@ import { ToggletipModule } from 'carbon-components-angular';
     ReactiveFormsModule,
     DashboardV3Module,
     BaseChartDirective,
-    ToggletipModule
+    ToggletipModule,
+    ModalModule,
+    InputModule,
+    SelectModule
   ],
   declarations: [
     HealthComponent,
index 88ef32507a452324da969d2d4d318146fd83374c..50a1804f35d3b92d676a220dfd94fa6001c0663b 100644 (file)
-<cd-modal [modalRef]="activeModal">
-  <div class="modal-title"
-       i18n>Report an issue</div>
+<cds-modal
+  size="md"
+  [open]="open"
+  [hasScrollingContent]="false"
+  (overlaySelected)="closeModal()"
+  >
+  <cds-modal-header (closeSelect)="closeModal()">
+    <h3 cdsModalHeaderHeading
+        i18n>Report an issue</h3>
+  </cds-modal-header>
 
-  <div class="modal-content">
-    <form name="feedbackForm"
-          [formGroup]="feedbackForm"
-          #formDir="ngForm">
-      <div class="modal-body">
-        <cd-alert-panel *ngIf="!isFeedbackEnabled"
-                        type="error"
-                        i18n>Feedback module is not enabled. Please enable it from <a (click)="redirect()">Cluster-> Manager Modules.</a>
-        </cd-alert-panel>
-        <!-- api_key -->
-        <div class="form-group row mt-3"
-             *ngIf="!isAPIKeySet">
-          <label class="cd-col-form-label required"
-                 for="api_key"
-                 i18n>Ceph Tracker API Key</label>
-          <div class="cd-col-form-input">
-            <input id="api_key"
-                   type="password"
-                   formControlName="api_key"
-                   class="form-control"
-                   placeholder="Add Ceph tracker API key">
-            <span class="invalid-feedback"
-                  *ngIf="feedbackForm.showError('api_key', formDir, 'required')"
-                  i18n>Ceph Tracker API key is required.</span>
-            <span class="invalid-feedback"
-                  *ngIf="feedbackForm.showError('api_key', formDir, 'invalidApiKey')"
-                  i18n>Ceph Tracker API key is invalid.</span>
-          </div>
-        </div>
+  <form name="feedbackForm"
+        [formGroup]="feedbackForm"
+        #formDir="ngForm">
+    <div
+      cdsModalContent
+      class="modal-wrapper"
+    >
+      @if (!isFeedbackEnabled) {
+      <cd-alert-panel type="error"
+                      spacingClass="mb-3"
+                      actionName="Enable"
+                      class="align-items-center"
+                      (action)="enableFeedbackModule()"
+                      i18n-actionName>
+        In order to report an issue, Feedback module must be enabled.
+      </cd-alert-panel>
+      }
+      <!-- api_key -->
+      @if (!isAPIKeySet) {
+      <div class="form-item">
+        <cds-password-label
+          labelInputID="api_key"
+          label="Ceph Tracker API Key"
+          cdRequiredField="Ceph Tracker API Key"
+          [invalid]="!feedbackForm.controls.api_key.valid && feedbackForm.controls.api_key.dirty"
+          [invalidText]="apiKeyError"
+          [helperText]="apiKeyHelperTpl"
+          [disabled]="!isFeedbackEnabled"
+          i18n
+        >
+          <input
+            cdsPassword
+            id="api_key"
+            type="password"
+            formControlName="api_key"
+          />
+        </cds-password-label>
 
-        <!-- project -->
-        <div class="form-group row">
-          <label class="cd-col-form-label required"
-                 for="project"
-                 i18n>Project name</label>
-          <div class="cd-col-form-input">
-            <select class="form-control"
-                    id="project"
-                    formControlName="project">
-              <option ngValue=""
-                      i18n>-- Select a project --</option>
-              <option *ngFor="let projectName of project"
-                      [value]="projectName">{{ projectName }}</option>
-            </select>
-            <span class="invalid-feedback"
-                  *ngIf="feedbackForm.showError('project', formDir, 'required')"
-                  i18n>Project name is required.</span>
-          </div>
-        </div>
+        <ng-template #apiKeyError>
+          @if (feedbackForm.showError('api_key', formDir, 'required')) {
+          <span class="invalid-feedback"
+                i18n> Ceph Tracker API key is required. </span>
+          } @if (feedbackForm.showError('api_key', formDir, 'invalidApiKey')) {
+          <span class="invalid-feedback"
+                i18n> Ceph Tracker API key is invalid. </span>
+          }
+        </ng-template>
 
-        <!-- tracker -->
-        <div class="form-group row">
-          <label class="cd-col-form-label required"
-                 for="tracker"
-                 i18n>Tracker</label>
-          <div class="cd-col-form-input">
-            <select class="form-control"
-                    id="tracker"
-                    formControlName="tracker">
-              <option ngValue=""
-                      i18n>-- Select a tracker --</option>
-              <option *ngFor="let trackerName of tracker"
-                      [value]="trackerName">{{ trackerName }}</option>
-            </select>
-            <span  class="invalid-feedback"
-                   *ngIf="feedbackForm.showError('tracker', formDir, 'required')"
-                   i18n>Tracker name is required.</span>
-          </div>
-        </div>
+        <ng-template #apiKeyHelperTpl>
+          You can obtain your API key from
+          <a href="https://tracker.ceph.com/my/account"
+             target="_blank">https://tracker.ceph.com/my/account
+          </a>
+        </ng-template>
+      </div>
+      }
+
+      <!-- project -->
+      <div class="form-item">
+        <cds-select
+          label="Project name"
+          id="project"
+          formControlName="project"
+          cdRequiredField="Project name"
+          [invalid]="!feedbackForm.controls.project.valid && feedbackForm.controls.project.dirty"
+          [invalidText]="projectError"
+          i18n
+        >
+          <option value="">--- Select a project ---</option>
+          @for (project of projects; track project) {
+          <option [value]="project">{{ project }}</option>
+          }
+        </cds-select>
+
+        <ng-template #projectError>
+          @if (feedbackForm.showError('project', formDir, 'required')) {
+          <span i18n>This field is required!</span>
+          }
+        </ng-template>
+      </div>
 
-        <!-- subject -->
-        <div class="form-group row">
-          <label class="cd-col-form-label required"
-                 for="subject"
-                 i18n>Subject</label>
-          <div class="cd-col-form-input">
-            <input id="subject"
-                   type="text"
-                   formControlName="subject"
-                   class="form-control"
-                   placeholder="Add issue title">
-            <span class="invalid-feedback"
-                  *ngIf="feedbackForm.showError('subject', formDir, 'required')"
-                  i18n>Subject is required.</span>
-          </div>
-        </div>
+      <!-- tracker -->
+      <div class="form-item cds-select-label">
+        <cds-select
+          label="Tracker"
+          id="tracker"
+          formControlName="tracker"
+          cdRequiredField="Tracker"
+          [invalid]="!feedbackForm.controls.tracker.valid && feedbackForm.controls.tracker.dirty"
+          [invalidText]="trackerError"
+          i18n
+        > Tracker
+          @for (trackerName of tracker; track trackerName) {
+          <option [value]="trackerName">{{ trackerName }}</option>
+          }
+        </cds-select>
 
-        <!-- description -->
-        <div class="form-group row">
-          <label class="cd-col-form-label required"
-                 for="description"
-                 i18n>Description</label>
-          <div class="cd-col-form-input">
-            <textarea id="description"
-                      type="text"
-                      formControlName="description"
-                      class="form-control"
-                      placeholder="Add issue description">
-            </textarea>
-            <span class="invalid-feedback"
-                  *ngIf="feedbackForm.showError('description', formDir, 'required')"
-                  i18n>Description is required.</span>
-          </div>
-        </div>
+        <ng-template #trackerError>
+          @if (feedbackForm.showError('tracker', formDir, 'required')) {
+          <span i18n>Tracker name is required.</span>
+          }
+        </ng-template>
+      </div>
+
+      <!-- subject -->
+      <div class="form-item">
+        <cds-text-label
+          cdRequiredField="Subject"
+          [invalid]="!feedbackForm.controls.subject.valid && feedbackForm.controls.subject.dirty"
+          [invalidText]="subjectError"
+          [disabled]="!isFeedbackEnabled"
+          i18n
+        >
+          Subject
+          <input
+            cdsText
+            id="subject"
+            type="text"
+            formControlName="subject"
+            placeholder="Add issue title"
+          />
+        </cds-text-label>
 
+        <ng-template #subjectError>
+          @if (feedbackForm.showError('subject', formDir, 'required')) {
+          <span i18n>Subject is required.</span>
+          }
+        </ng-template>
       </div>
-      <div class="modal-footer">
-        <cd-form-button-panel (submitActionEvent)="onSubmit()"
-                              [form]="feedbackForm"
-                              [submitText]="actionLabels.SUBMIT"
-                              wrappingClass="text-right">
-        </cd-form-button-panel>
+
+      <!-- description -->
+      <div class="form-item">
+        <cds-text-label
+          cdRequiredField="Description"
+          [invalid]="!feedbackForm.controls.description.valid && feedbackForm.controls.description.dirty"
+          [invalidText]="descriptionError"
+          [disabled]="!isFeedbackEnabled"
+          i18n
+        >
+          Description
+          <input
+            cdsText
+            id="description"
+            type="text"
+            formControlName="description"
+            placeholder="Explain your issue"
+          />
+        </cds-text-label>
+
+        <ng-template #descriptionError>
+          @if (feedbackForm.showError('description', formDir, 'required')) {
+          <span i18n>Description is required.</span>
+          }
+        </ng-template>
       </div>
-    </form>
-  </div>
-</cd-modal>
+    </div>
+    @if (isFeedbackEnabled) {
+    <cd-form-button-panel
+      (submitActionEvent)="onSubmit()"
+      [form]="feedbackForm"
+      [submitText]="submit"
+      [modalForm]="true"
+    ></cd-form-button-panel>
+    }
+    @else {
+    <cd-form-button-panel
+      [showSubmit]="false"
+      [modalForm]="true"
+    ></cd-form-button-panel>
+    }
+  </form>
+</cds-modal>
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c817c1872197fc650ba5b9cb5fa986c11921eb61 100644 (file)
@@ -0,0 +1,3 @@
+.modal-wrapper {
+  overflow: visible; /* allow tooltip to expand beyond this block */
+}
index 2deea36a74ce31cb8b9a98c301f6dcf1f36713d9..c82f159f5a206a65d9311a71d5a27c67e7ff09f0 100644 (file)
@@ -11,6 +11,8 @@ import { FeedbackService } from '~/app/shared/api/feedback.service';
 import { ComponentsModule } from '~/app/shared/components/components.module';
 import { configureTestBed, FormHelper } from '~/testing/unit-test-helper';
 import { FeedbackComponent } from './feedback.component';
+import { SharedModule } from '~/app/shared/shared.module';
+import { SelectModule } from 'carbon-components-angular';
 
 describe('FeedbackComponent', () => {
   let component: FeedbackComponent;
@@ -24,7 +26,9 @@ describe('FeedbackComponent', () => {
       HttpClientTestingModule,
       RouterTestingModule,
       ReactiveFormsModule,
-      ToastrModule.forRoot()
+      ToastrModule.forRoot(),
+      SharedModule,
+      SelectModule
     ],
     declarations: [FeedbackComponent],
     providers: [NgbActiveModal]
@@ -43,7 +47,7 @@ describe('FeedbackComponent', () => {
 
   it('should open the form in a modal', () => {
     const nativeEl = fixture.debugElement.nativeElement;
-    expect(nativeEl.querySelector('cd-modal')).not.toBe(null);
+    expect(nativeEl.querySelector('cds-modal')).not.toBe(null);
   });
 
   it('should redirect to mgr-modules if feedback module is not enabled', () => {
index ac53edef28b8f6a865ef5dbda0d1e1d1ae5070a6..e95621fc5250ed48e026303ad55019c0d9787f21 100644 (file)
@@ -1,13 +1,12 @@
 import { Component, OnDestroy, OnInit } from '@angular/core';
 import { UntypedFormControl, Validators } from '@angular/forms';
-import { Router } from '@angular/router';
-
-import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
 import { Subscription } from 'rxjs';
 
 import { FeedbackService } from '~/app/shared/api/feedback.service';
+import { MgrModuleService } from '~/app/shared/api/mgr-module.service';
 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
+import { CdForm } from '~/app/shared/forms/cd-form';
 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
 import { NotificationService } from '~/app/shared/services/notification.service';
 
@@ -16,9 +15,9 @@ import { NotificationService } from '~/app/shared/services/notification.service'
   templateUrl: './feedback.component.html',
   styleUrls: ['./feedback.component.scss']
 })
-export class FeedbackComponent implements OnInit, OnDestroy {
+export class FeedbackComponent extends CdForm implements OnInit, OnDestroy {
   title = 'Feedback';
-  project: any = [
+  projects: any = [
     'dashboard',
     'block',
     'objects',
@@ -31,19 +30,20 @@ export class FeedbackComponent implements OnInit, OnDestroy {
   tracker: string[] = ['bug', 'feature'];
   api_key: string;
   keySub: Subscription;
-
+  submit: string;
   feedbackForm: CdFormGroup;
   isAPIKeySet = false;
   isFeedbackEnabled = true;
 
   constructor(
     private feedbackService: FeedbackService,
-    public activeModal: NgbActiveModal,
     public actionLabels: ActionLabelsI18n,
-    public secondaryModal: NgbModal,
-    private notificationService: NotificationService,
-    private router: Router
-  ) {}
+    public mgrModuleService: MgrModuleService,
+    private notificationService: NotificationService
+  ) {
+    super();
+    this.submit = $localize`Submit`;
+  }
 
   ngOnInit() {
     this.createForm();
@@ -64,7 +64,7 @@ export class FeedbackComponent implements OnInit, OnDestroy {
   private createForm() {
     this.feedbackForm = new CdFormGroup({
       project: new UntypedFormControl('', Validators.required),
-      tracker: new UntypedFormControl('', Validators.required),
+      tracker: new UntypedFormControl(this.tracker[0], Validators.required),
       subject: new UntypedFormControl('', Validators.required),
       description: new UntypedFormControl('', Validators.required),
       api_key: new UntypedFormControl('', Validators.required)
@@ -97,13 +97,19 @@ export class FeedbackComponent implements OnInit, OnDestroy {
           this.feedbackForm.setErrors({ cdSubmitButton: true });
         },
         complete: () => {
-          this.activeModal.close();
+          this.closeModal();
         }
       });
   }
 
-  redirect() {
-    this.activeModal.close();
-    this.router.navigate(['/mgr-modules']);
+  enableFeedbackModule() {
+    this.mgrModuleService.updateModuleState(
+      'feedback',
+      false,
+      null,
+      null,
+      'Enabled Feedback Module',
+      true
+    );
   }
 }
index 30bf6f8efc0c6f9ec78d308af7ee012cba08c40c..bc9a04ba28bb2151c3d756edeaf03b9aa86f51ff 100644 (file)
@@ -1,14 +1,11 @@
 import { Component, OnInit } from '@angular/core';
 
-import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
-
 import { Icons } from '~/app/shared/enum/icons.enum';
 import { DocService } from '~/app/shared/services/doc.service';
 
 import { AboutComponent } from '../about/about.component';
 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
 import { FeedbackComponent } from '~/app/ceph/shared/feedback/feedback.component';
-import { ModalService } from '~/app/shared/services/modal.service';
 
 @Component({
   selector: 'cd-dashboard-help',
@@ -18,13 +15,8 @@ import { ModalService } from '~/app/shared/services/modal.service';
 export class DashboardHelpComponent implements OnInit {
   docsUrl: string;
   icons = Icons;
-  bsModalRef: NgbModalRef;
 
-  constructor(
-    private docService: DocService,
-    private modalCdsService: ModalCdsService,
-    private modalService: ModalService
-  ) {}
+  constructor(private docService: DocService, private modalCdsService: ModalCdsService) {}
 
   ngOnInit() {
     this.docService.subscribeOnce('dashboard', (url: string) => {
@@ -37,6 +29,6 @@ export class DashboardHelpComponent implements OnInit {
   }
 
   openFeedbackModal() {
-    this.bsModalRef = this.modalService.show(FeedbackComponent, null, { size: 'lg' });
+    this.modalCdsService.show(FeedbackComponent);
   }
 }