Creating the "Add note" section in the Schedule modal

Learn how to add custom translation strings for the UI text and create the "Add note" section in the Schedule modal.

Procedure

  1. Set up translation strings and environment configurations.
    1. Create an assets folder under devtoolkit_docker/orderhub-code/buc-app-order/packages/order-search-result/src-custom.
    2. Copy over the assets from the order-shared module to the assets folder that you created.
      Showing the folder structure after copying the assets folder
    3. Open the devtoolkit_docker/orderhub-code/buc-app-order/angular.json file.
    4. Replace the current contents of the projects > order-search-result > architect > build > configurations > merged > assets array with the following entries. The purpose of this step to direct the module to use the customized assets files instead of the files from /order-shared.
                      {
                        "glob": "**",
                        "input": "packages/order-search-result/src-merged/assets",
                        "output": "assets"
                      },  
                      {
                        "glob": "*.json",
                        "input": "packages/order-search-result/src-merged/assets/buc-app-order",
                        "output": "assets/order-search-result"
                      },
                      {
                        "glob": "**",
                        "input": "node_modules/@buc/svc-angular/assets",
                        "output": "assets"
                      },
                      {
                        "glob": "**",
                        "input": "node_modules/@buc/common-components/assets",
                        "output": "assets"
                      }
      
    5. Also replace the contents in the projects > order-search-result > architect > build > configurations > merged-prod > assets array.
    6. Copy the environments folder from 'buc-app-order/packages/order-search-result/src' into 'buc-app-order/packages/order-search-result/src-custom'.
    7. Go to the buc-app-order/packages/order-search-result/src-custom/environments directory.
    8. Add the following line to the end of both the environment.ts and envrionment.prod.ts files.
      environment.customization = true;
    9. Stop and restart the server so that changes to the angular.json and overrides.json files take effect.
      Stop the job in the terminal. Then run:
      yarn stop-app
      yarn start-app
    10. Create a custom folder under 'buc-app-order/packages/order-search-result/src-custom/assets'.
    11. Create an i18n folder under 'buc-app-order/packages/order-search-result/src-custom/assets/custom'.
    12. Create an en.json file under 'buc-app-order/packages/order-search-result/src-custom/assets/custom/i18n.
      The en.json file includes the English literal strings to display in the UI. You can add translated strings by creating other JSON files. Name the files based on the ISO-639 language codes. For example, fr.json for strings in French.
    13. Paste the following JSON content.
      {
          "CUSTOM_ORDER_SEARCH_RESULT": {
              "NOTE": {
                  "LABEL_ADD_NOTE": "Add Note",
                  "LABEL_DATE": "Date",
                  "LABEL_FIELD_REQUIRED": "Required.",
                  "LABEL_USER": "User",
                  "LABEL_NOTE": "Note",
                  "LABEL_REASON_OPTIONAL": "Reason (optional)",
                  "LABEL_CONTACT_TYPE_OPTIONAL": "Contact type (optional)",
                  "LABEL_CONTACT_REFERENCE_OPTIONAL": "Contact reference (optional)",
                  "MSG_SUCCESS_ADD_NOTES": "Notes added successfully.",
                  "MSG_ERROR_ADD_NOTES": "Notes was not added. Try again later."
              }
          }
      }
      
  2. Create a custom folder under buc-app-order/packages/order-search-result/src-custom/app'.
  3. Create a data-services folder under 'buc-app-order/packages/order-search-result/src-custom/app/custom'.
  4. Create a service file add-notes-data.service.ts under the data-services folder and paste the following code snippet. This service calls the 'modifyFulfillmentOptions' OMS API to save the note.
    import { Injectable } from '@angular/core';
    import { BucCommOmsRestAPIService } from '@buc/svc-angular';
    
    @Injectable({
        providedIn: 'root'
    })
    export class AddNotesDataService {
        constructor(private bucCommOmsRestAPIService: BucCommOmsRestAPIService) {
        }
    
        // Below method fetches the list of reason codes to display in 'Reason' dropdown
        getCommonCodeListForReasonCode(enterpriseCode: string, docType: string) {
            const Input = {
                CallingOrganizationCode: enterpriseCode,
                CodeType: 'NOTES_REASON',
                DocumentType: docType,
            };
            return this.bucCommOmsRestAPIService.invokeOMSRESTApi('getCommonCodeList', Input, {});
        }
    
        // Below method fetches the list of contact types to display in 'Contact type' dropdown
        getCommonCodeListForContactType(enterpriseCode: string) {
            const Input = {
                CallingOrganizationCode: enterpriseCode,
                CodeType: 'CONTACT_TYPE'
            };
            return this.bucCommOmsRestAPIService.invokeOMSRESTApi('getCommonCodeList', Input, {});
        }
    
        // Below method saves the notes data by calling modifyFulfillmentOptions OMS API
        changeOrder(order: any) {
            return this.bucCommOmsRestAPIService.invokeOMSRESTApi('modifyFulfillmentOptions', order, {});
        }
    }
    
  5. Create a new add-notes component and its file structure, then make the file changes.
    To do so, first create the following file structure:
    1. Create a directory called add-notes in src-custom/app/custom.
    2. Create the following files in the add-notes directory: add-notes.component.html, add-notes.component.scss, and add-notes.component.ts.

    Then, make the following file changes:

    1. add-notes.component.html
      
      <div class="bx--row">
          <div class="bx--col-lg-16">
              <buc-checkbox [checked]="isAddNoteChecked"
                  [attr.tid]="componentId + '-add-note'"
                  (change)="onIsAddNoteCheckedChange($event)">
                  {{'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_ADD_NOTE' | translate}}
              </buc-checkbox>
          </div>
      </div>
      
      <div class="screen notes-section" *ngIf="isScreenInitialized && isAddNoteChecked">
          <p class="title">
              {{ 'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_ADD_NOTE' | translate }}
          </p>
          <div class="bx--row status">
              <div class="combo-box bx--col-md-4">
                  <div class="d--flex-ai-flex-end">
                      <buc-date-picker label="{{'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_DATE' | translate}}"
                          [placeholder]="i18nDatePlaceholder" [dateFormat]="flatpickrDateFormat" [language]="curLocale"
                          [value]="contactDate" (valueChange)="onDateChange($event)">
                      </buc-date-picker>
                      <div class="oms-spacer8"></div>
                      <buc-time-picker [disabled]="false" [theme]="'light'" [time]="contactTime?.time"
                          [period]="contactTime?.period" (valueChange)="timeChange($event)">
                      </buc-time-picker>
                  </div>
                  <div *ngIf="savePressed && isDateValid" class="d--flex buc-warning bx--form-requirement">
                      {{'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_FIELD_REQUIRED' | translate}}
                  </div>
              </div>
              <div class="bx--col-md-4">
                  <buc-label class="size--sm" [theme]="'light'" [label]="'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_USER' | translate"
                      placeholder="" [(inputValue)]="currentUserId" (inputValueChange)="onText($event, 'conUser')">
                  </buc-label>
                  <div *ngIf="savePressed && !currentUserId" class="d--flex buc-warning bx--form-requirement">
                      {{'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_FIELD_REQUIRED' | translate}}
                  </div>
              </div>
          </div>
      
          <div class="bx--row">
              <div class="combo-box bx--col-md-4">
                  <p class="bx--label">{{'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_NOTE' | translate}}</p>
              </div>
              <div class="combo-box bx--col-md-4">
                  <p class="pull-right">{{ notesText.length }}/{{ 2000 }}</p>
              </div>
          </div>
          <div class="combo-box bx--col-md-12 status">
              <textarea [(ngModel)]="notesText" (inputValueChange)="onText($event, 'notesTxt')" ibmTextArea
                  [attr.tid]="componentId + ''" [rows]=2 class="bx--text-area" aria-label="textarea" maxlength="2000">
                                              </textarea>
              <div *ngIf="savePressed && !notesText" class="d--flex buc-warning bx--form-requirement">
                  {{'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_FIELD_REQUIRED' | translate}}
              </div>
          </div>
      
          <div class="bx--row status">
              <div class="combo-box bx--col-md-4">
                  <buc-dropdown placeholder="" [cozy]="true" [theme]="'dark'" [disabled]="false"
                      [label]="'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_REASON_OPTIONAL' | translate" [items]="reasonCodeList"
                      (selected)="reasonCodeOnSelection($event)">
                  </buc-dropdown>
              </div>
              <div class="combo-box bx--col-md-4">
                  <buc-dropdown placeholder="" [cozy]="true" [theme]="'dark'" [disabled]="false"
                      [label]="'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_CONTACT_TYPE_OPTIONAL' | translate" [items]="contactTypeList"
                      (selected)="contactTypeOnSelection($event)">
                  </buc-dropdown>
              </div>
          </div>
          <div class="bx--row status">
              <div class="combo-box bx--col-md-4">
                  <buc-label class="size--sm" [theme]="'light'"
                      [label]="'CUSTOM_ORDER_SEARCH_RESULT.NOTE.LABEL_CONTACT_REFERENCE_OPTIONAL' | translate" placeholder=""
                      [(inputValue)]="contactRef" (inputValueChange)="onText($event, 'conRef')">
                  </buc-label>
              </div>
          </div>
      </div>
      
    2. add-notes.component.scss:
      
      .bx--modal-content {
          padding-right: 1rem;
      }
      
      .notes-section {
          border-top: 1px solid grey;
          padding-top: 1rem;
          margin-top: 1rem;
      }
      
      .bx--modal-content {
          padding: 1rem 1rem 0 1rem;
      
          .title,
          .subtitle,
          .type-switch,
          form,
          .delivery-type,
          .status,
          .alert-severity,
          .exclusion {
              margin-bottom: 24px;
          }
      
          .status:last-child {
              margin-bottom: 0;
          }
      
          .within-text,
          .within-text-only {
              display: flex;
              align-items: center;
          }
      }
      
      .pull-right {
          text-align: right;
          font-size: 0.75rem;
      }
      
      .oms-spacer8 {
          padding-top: 0.5rem;
          padding-left: 0.1rem;
          background-repeat: no-repeat;
      }
      
      buc-time-picker {
          ::ng-deep ibm-timepicker .bx--time-picker {
              ibm-timepicker-select.bx--time-picker__select {
                  height: 2rem;
                  background-color: white;
              }
          }
      }
      
      ::ng-deep .bx--time-picker__input {
          height: 2rem;
      }
      
    3. add-notes.component.ts:
      
      import { CommonService, Constants, DocTypes, handleOMSErrors, OrderListDataService } from '@buc/order-shared';
      import { BucNotificationService, getCurrentLocale, getCurrentLocaleDateFormat, getFlatPickrDateFormat } from '@buc/common-components';
      import { BucSvcAngularStaticAppInfoFacadeUtil } from '@buc/svc-angular';
      import moment from 'moment';
      import * as momentTime from 'moment-timezone';
      import flatpickr from 'flatpickr';
      import { AddNotesDataService } from '../data-services/add-notes-data.service';
      import { Component, EventEmitter, Inject, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChange } from '@angular/core';
      import { TranslateService } from '@ngx-translate/core';
      import { ModalService, NotificationService } from 'carbon-components-angular';
      import { SimpleChanges } from '@angular/core';
      
      @Component({
          selector: 'buc-add-notes',
          templateUrl: './add-notes.component.html',
          styleUrls: ['./add-notes.component.scss']
      })
      
      export class AddNotesComponent implements OnInit, OnChanges {
      
          nlsMap = {
              'CUSTOM_ORDER_SEARCH_RESULT.NOTE.MSG_SUCCESS_ADD_NOTES': '',
              'CUSTOM_ORDER_SEARCH_RESULT.NOTE.MSG_ERROR_ADD_NOTES': '',
          }
          bucNotificationService: BucNotificationService;
          @Input() scheduleOrderClicked: boolean;
          @Input() componentId: boolean;
          @Input() modalData: any;
          @Output() cancel: EventEmitter<any> = new EventEmitter();
          @Output() onAddNoteCheckedChange: EventEmitter<any> = new EventEmitter();
      
          constructor(
              protected modalService: ModalService,
              public orderListDataService: OrderListDataService,
              public notificationService: NotificationService,
              public translate: TranslateService,
              public commonSvc: CommonService,
              public notesDataService: AddNotesDataService,
              bucNotificationService: BucNotificationService
          ) {
              this.bucNotificationService = new BucNotificationService();
          }
      
          isAddNoteChecked = false;
          isScreenInitialized = false;
          displayData: any;
          reasonCodeList = [];
          contactTypeList = [];
          curLocale: string;
          public i18nDatePlaceholder;
          public flatpickrDateFormat;
      
          reasonCodeValue: any;
          contactTypeValue: any;
          getReasonValue: any;
          getContactValue: any;
          contactRef = '';
          notesText = '';
          contactDateTime;
          contactDate;
          contactTime;
          invalidDate = false;
          currentUserId;
          savePressed = false;
          isDateValid = false;
      
          // To gather all the data required for notes section on opening the modal like dropdown list data, date picker placeholder 
          async initializeNotesSection() {
              this.savePressed = false;
              this.currentUserId = BucSvcAngularStaticAppInfoFacadeUtil.getOmsUserLoginId();
              await this._initTranslations();
              await this.getReasonCodeList();
              await this.getContactTypeList();
              this.isScreenInitialized = true;
              this.setDate();
              this.curLocale = getCurrentLocale();
              if (this.curLocale.startsWith('zh-')) {
                  this.curLocale = 'zh';
              }
              this.i18nDatePlaceholder = getCurrentLocaleDateFormat();
              this.flatpickrDateFormat = getFlatPickrDateFormat();
          }
      
          // To get the list of reason codes to display in 'Reason' dropdown
          async getReasonCodeList() {
              const enterpriseCode = this.displayData.enterpriseCode;
              const documentType = this.displayData.docType;
              const commonCodeList = await this.notesDataService.getCommonCodeListForReasonCode(enterpriseCode, documentType).toPromise();
              if (commonCodeList.CommonCode) {
                  commonCodeList.CommonCode.forEach(element => {
                      this.reasonCodeList.push({
                          content: element.CodeShortDescription ? element.CodeShortDescription :
                              element.CodeValue, value: element.CodeValue
                      });
                  });
              }
          }
      
          // To get the list of contact types to display in 'Contact type' dropdown
          async getContactTypeList() {
              const enterpriseCode = this.displayData.enterpriseCode;
              const commonCodeList = await this.notesDataService.getCommonCodeListForContactType(enterpriseCode).toPromise();
              if (commonCodeList.CommonCode) {
                  commonCodeList.CommonCode.forEach(element => {
                      this.contactTypeList.push({
                          content: element.CodeShortDescription ? element.CodeShortDescription :
                              element.CodeValue, value: element.CodeValue
                      });
                  });
              }
          }
      
          onIsAddNoteCheckedChange(event) {
              this.isAddNoteChecked = event.checked;
              this.onAddNoteCheckedChange.emit();
          }
      
          async addNotes() {
              this.savePressed = true;
              this.orderAddNotes();
          }
      
          // To save the notes on click of 'Schedule' button
          async orderAddNotes() {
              const convertTimestamp = new Date(this.contactDateTime);
              if (this.notesText.length > 0 && this.currentUserId.length > 0 && !this.invalidDate && this.isDateValid === false) {
                  this.reasonCodeValue = this.getReasonValue ? this.reasonCodeList.find(r => r.value === this.getReasonValue).value : '';
                  this.contactTypeValue = this.getContactValue ? this.contactTypeList.find(c => c.value === this.getContactValue).value : '';
      
                  Promise.all(this.modalData.orders.map(async (element) => {
                      const body: any = {
                          OrderHeaderKey: element.OrderHeaderKey,
                          Notes: {
                              Note: [
                                  {
                                      ContactReference: this.contactRef,
                                      ContactTime: convertTimestamp,
                                      ContactType: this.contactTypeValue,
                                      ContactUser: this.currentUserId,
                                      NoteText: this.notesText,
                                      ReasonCode: this.reasonCodeValue
                                  }
                              ]
                          }
                      };
                      if (this.displayData.hasOwnProperty('recordChanges') &&
                          this.displayData.recordChanges === false) {
                          await this.notesDataService.changeOrder(body).toPromise();
                      } else {
                          if (this.displayData.docType === DocTypes.SalesOrder) {
                              const pendingChanges = { PendingChanges: { RecordPendingChanges: 'N' } };
                              Object.assign(body, pendingChanges);
                          }
                          await this.notesDataService.changeOrder(body).toPromise();
                      }
                  })).then(() => {
                      if (this.displayData.showSuccessMsg) {
                          CommonService.notify('success', this.nlsMap['CUSTOM_ORDER_SEARCH_RESULT.NOTE.MSG_SUCCESS_ADD_NOTES']);
                      }
                      this.onCancel();
                  }).catch(async (err) => {
                      const errorMsg = this.nlsMap['CUSTOM_ORDER_SEARCH_RESULT.NOTE.MSG_ERROR_ADD_NOTES'];
                      await handleOMSErrors(err, this.translate, this.bucNotificationService, errorMsg);
                      this.onCancel();
                  });
              }
          }
      
          // To set the date and time picker to current date and time
          setDate() {
              const currentDate = new Date();
              this.contactDateTime = moment(currentDate).format(Constants.DATETIME_FORMAT);
              this.contactDate = flatpickr.formatDate(moment(currentDate).toDate(), 'Z');
              this.contactTime = {
                  time: momentTime(currentDate).format(Constants.TIME_FORMAT),
                  period: momentTime(currentDate).format(Constants.TIME_PERIOD)
              };
          }
      
          // Event that triggers when user changes the date
          onDateChange(event) {
              this.isDateValid = event.length === 0;
              if (event.length) {
                  const newDate = moment(event[0]).format(Constants.DATE_FORMAT);
                  this.contactDateTime = this.contactDateTime.replace(this.contactDateTime.split(' ')[0], newDate);
                  this.contactDate = flatpickr.formatDate(moment(this.contactDateTime).toDate(), 'Z');
              }
          }
      
          // Event that triggers when user changes the time
          timeChange(event) {
              const dateTime = this.contactDateTime.split(' ');
      
              if (this.contactTime.time && event.time) {
                  let invalidTime = false;
                  const tm = event.time.split(':');
                  let hours = Number(tm[0]);
                  const minutes = (tm[1] && Number(tm[1])) || 0;
                  if (this.contactTime.period === 'PM') {
                      if (hours > 12 && hours <= 24) {
                          hours = hours - 12;
                      } else if (hours > 24) {
                          invalidTime = true;
                      }
                  }
                  if (minutes && minutes > 59) {
                      invalidTime = true;
                  }
                  if (!invalidTime) {
                      const h = hours < 10 ? `0${hours}` : `${hours}`;
                      const m = minutes < 10 ? `0${minutes}` : `${minutes}`;
                      dateTime[1] = `${h}:${m}`;
                  }
              } else if (this.contactTime.period && event.timePeriod) {
                  dateTime[2] = event.timePeriod;
              }
      
              const tempDate = new Date(dateTime.join(' '));
              if (tempDate.toString() === 'Invalid Date') {
                  this.invalidDate = true;
              } else {
                  this.invalidDate = false;
                  this.contactDateTime = dateTime.join(' ');
                  this.contactTime = {
                      time: momentTime(tempDate).format(Constants.TIME_FORMAT),
                      period: momentTime(tempDate).format(Constants.TIME_PERIOD)
                  };
              }
          }
      
          async reasonCodeOnSelection(event) {
              this.getReasonValue = event.item.value;
          }
          async contactTypeOnSelection(event) {
              this.getContactValue = event.item.value;
          }
      
          // Event that gets trigger on clicking cancel button
          onCancel() {
              if (this.displayData.parentPage) {
                  this.displayData.parentPage.disableAdd = false;
              }
            this.cancel.emit();
          }
      
          async ngOnInit() {
              await this._initTranslations();
              this.initializeNotesSection();
          }
      
          async ngOnChanges(changes: SimpleChanges) {
              if(changes.scheduleOrderClicked?.currentValue) {
                  if (this.isAddNoteChecked) {
                      await this.addNotes();
                  }
              }
              if (changes.modalData?.currentValue && changes.modalData.currentValue !== changes.modalData.previousValue  && Object.keys(changes.modalData.currentValue).length > 0) {
                  this.displayData = {
                      docType: this.modalData.orders[0].DocumentType,
                      enterpriseCode: this.modalData.orders[0].enterpriseCode,
                      OrderHeaderKey: this.modalData.orders[0].OrderHeaderKey,
                      showSuccessMsg: true
                  };
              }
      
          }
      
          protected async _initTranslations() {
              const keys = Object.keys(this.nlsMap);
              const json = await this.translate.get(keys).toPromise();
              keys.forEach(k => this.nlsMap[k] = json[k]);
          }
      
      }
      
  6. Add an extension service: buc-app-order/packages/order-search-result/src-custom/app/custom/data-services/add-notes-extension.service.ts
    
    import { Injectable } from '@angular/core';
    import { ExtensionService } from '@buc/common-components';
    
    @Injectable()
    
    export class AddNotesExtensionService extends ExtensionService {
    
        userInputs = {
            scheduleOrderClicked: false,
            componentId: '',
            modalData: {}
        };
        originalScheduleOrderFunc;
    
        constructor() {
            super();
        }
    
        createInput() {
            if (!this.parentContext.scheduleOrder) {
                this.userInputs.scheduleOrderClicked = false;
                this.originalScheduleOrderFunc = null;
            }
            if (this.parentContext.componentId !== this.userInputs.componentId) {
                this.userInputs.componentId = this.parentContext.componentId;
    
            }
            if (this.parentContext.modalData !== this.userInputs.modalData) {
                this.userInputs.modalData = this.parentContext.modalData;
            }
            this.userInputObs$.next(this.userInputs);
            this.overrideMethods();
        }
    
        handleOutput() {
            this.userOutputs = {
                cancel: this.onCancel.bind(this),
                onAddNoteCheckedChange: this.onAddNoteCheckedChange.bind(this)
            };
            this.userOutputObs$.next(this.userOutputs);
        }
    
        overrideMethods() {
            if (!this.originalScheduleOrderFunc && this.parentContext.scheduleOrder) {
                this.originalScheduleOrderFunc = this.parentContext.scheduleOrder.bind(this.parentContext);
                this.parentContext.scheduleOrder = (event) => {
                    this.userInputs.scheduleOrderClicked = true;
                    this.userInputObs$.next(this.userInputs);
                    this.originalScheduleOrderFunc(event);
                }
            }
    
        }
    
        onAddNoteCheckedChange() {
            this.userInputs.modalData = this.userInputs.modalData;
            this.userInputObs$.next(this.userInputs);
        }
    
        onCancel() {
    
            this.parentContext.closeModal();
        }
    
    }
    
  7. Register the new component and the extension service in /packages/order-search-result/src-custom/app/app-customization.impl.ts:
    
    import { ExtensionModule } from "@buc/common-components";
    import { SharedExtensionConstants } from "@buc/order-shared";
    import { AddNotesComponent } from "./custom/add-notes/add-notes.component";
    import { AddNotesExtensionService } from "./custom/data-services/add-notes-extension.service";
    
    export class AppCustomizationImpl {
        static readonly components = [AddNotesComponent];
    
        static readonly providers = [];
    
        static readonly imports = [
            ExtensionModule.forRoot([
                {
                    id: SharedExtensionConstants.SCHEDULE_MODAL_SHARED_BOTTOM,
                    component: AddNotesComponent,
                    service: AddNotesExtensionService
                }
            ]),
        ];
    
    }
    
  8. Create a features folder under 'buc-app-order/packages/order-search-result/src-custom/app'.
  9. In the features folder, create an ext-order.module.ts file (buc-app-order/packages/order-search-result/src-custom/app/features/ext-order.module.ts) and then paste the following content.
    
    import { NgModule } from "@angular/core";
    import { CommonModule } from "@angular/common";
    import { TranslateModule } from "@ngx-translate/core";
    import { AddNotesDataService } from "../custom/data-services/add-notes-data.service";
    @NgModule({
      declarations: [],
      imports: [CommonModule, TranslateModule],
      providers: [AddNotesDataService],
      exports: [],
    })
    export class ExtOrderModule {}
    
    Notice the provider object in the code with the CUSTOM_ACTIONS token. Order Hub provides two Injection Tokens. 'CUSTOM_ACTIONS' and 'CUSTOM_FEATURE_ACTIONS' to override existing actions or to provide custom actions.

    The value for the injection token is an array where each element is an object containing two properties:

    • name - The name of the action.
    • action - The service that handles or implements the action. In this tutorial, ScheduleActionService is a default action that you need to override so you also need to add the ScheduleActionService in the same providers array.

    The Injection point needs to be defined in the ext-NNN.module.ts module because it ensures that the TranslateService that is injected in to the action has all bundles loaded by the feature module. Every module in every route application has this module in /src/app/features/ext-NNN.module.ts. This file does not exist in your src-custom folder, therefore you need to create the file.

    Note: Action implementation is separated by component so that customizing the existing action is limited to the service file instead of modifying every component that dispatches the action. By making these changes, the buc-app-order application uses the newly added service whenever the Schedule action is dispatched.
  10. Refresh the Order Hub user interface and go through a Schedule flow to view the changes in the Schedule order modal.
    1. Log in to Order Hub.
    2. Go to Orders > Outbound (DEV mode).
    3. Search for an order.
    4. From the Order search results table, click the checkbox next to an order and then click Schedule.
    5. Click Add note to expose fields where you can add information.
    6. Add some text to the "Notes" section and modify other properties as needed.
    7. Click Schedule.
      A message appears to confirm that the note was added successfully and that the order was scheduled.
    8. Click the order number that you scheduled to go to the Order details page.
    9. Click the Notes tab. Ensure that the notes table includes the note that you added.
      A screenshot of the Notes tab. The tab shows the note entry that the user entered.

Results

You successfully customized the Schedule order modal to include an "Add notes" section.

Complete the next lesson to learn how to deploy the customizations to your environments so that others can use the customizations. Up to now, the customizations are only available to you locally.