diff --git a/docker-compose.yml b/docker-compose.yml index 0d34a70c7..57c8095af 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,6 +44,9 @@ services: SERILOG__WRITETO__0__ARGS__THEME: Serilog.Sinks.SystemConsole.Themes.ConsoleTheme::None, Serilog.Sinks.Console SERILOG__WRITETO__0__NAME: Console SERILOG__MINIMUMLEVEL__DEFAULT: ${SERILOG__MINIMUMLEVEL__DEFAULT:-Information} + ORDSDATASERVICE__ADDRESS: ${ORDSDATASERVICE__ADDRESS} + ORDSDATASERVICE__PASSWORD: ${ORDSDATASERVICE__PASSWORD} + ORDSDATASERVICE__USERNAME: ${ORDSDATASERVICE__USERNAME} ports: - "5080:8080" restart: always diff --git a/src/frontend/citizen-portal/src/app/components/disputant-form/disputant-form.component.html b/src/frontend/citizen-portal/src/app/components/disputant-form/disputant-form.component.html index a922cf72f..dbc798412 100644 --- a/src/frontend/citizen-portal/src/app/components/disputant-form/disputant-form.component.html +++ b/src/frontend/citizen-portal/src/app/components/disputant-form/disputant-form.component.html @@ -231,10 +231,14 @@

Contact details

minlength="7" maxlength="9" /> - - Must be a valid driver's licence number of seven to nine digits - + + + Must be a valid driver's licence number of seven to nine digits. + + + Must be a valid driver's licence number with a maximum of 20 characters. + + diff --git a/src/frontend/citizen-portal/src/app/components/disputant-form/disputant-form.component.ts b/src/frontend/citizen-portal/src/app/components/disputant-form/disputant-form.component.ts index b5b5a6c1e..c104ba8aa 100644 --- a/src/frontend/citizen-portal/src/app/components/disputant-form/disputant-form.component.ts +++ b/src/frontend/citizen-portal/src/app/components/disputant-form/disputant-form.component.ts @@ -78,31 +78,41 @@ export class DisputantFormComponent implements OnInit, AfterViewInit { } else { this.countryFormControl.setValue(country); } - + let province = this.provincesAndStates.filter(i => i.provAbbreviationCd === this.form.value.address_province).shift(); if (!this.form.value.address_province) { this.provinceFormControl.setValue(this.bc); } else { this.provinceFormControl.setValue(province); } - + let form = this.form as NoticeOfDisputeFormGroup; - // search for drivers licence province using abbreviation e.g. BC if (form.value.drivers_licence_province) { let foundProvinces = this.provincesAndStates.filter(x => x.provAbbreviationCd === form.value.drivers_licence_province).shift(); if (foundProvinces) { this.driversLicenceProvinceFormControl.setValue(foundProvinces); - } else{ + } else { this.driversLicenceProvinceFormControl.setValue(null); } - } else if (form.controls.drivers_licence_province) { // have control but no value - if(this.mode !== DisputeFormMode.UPDATE) { + } else if (form.controls.drivers_licence_province) { + if (this.mode !== DisputeFormMode.UPDATE) { this.driversLicenceProvinceFormControl.setValue(this.bc); } else { this.driversLicenceProvinceFormControl.setValue(null); - } + } } - + + // Check for the driver's licence number validity on load + const driversLicenceNumber = form.controls.drivers_licence_number.value; + const isBC = this.driversLicenceProvinceFormControl.value?.provId === this.bc.provId; + const isInvalid = isBC ? !(driversLicenceNumber && driversLicenceNumber.length >= 7 && driversLicenceNumber.length <= 9 && /^[0-9]+$/.test(driversLicenceNumber)) : + !(driversLicenceNumber && driversLicenceNumber.length <= 20); + + if (isInvalid) { + form.controls.drivers_licence_number.markAsTouched(); + form.controls.drivers_licence_number.markAsDirty(); + } + if (this.preferEmail !== undefined && this.preferEmail !== true) { this.form.controls.email_address.disable(); this.optOut = true; @@ -180,10 +190,17 @@ export class DisputantFormComponent implements OnInit, AfterViewInit { } if (province != null && province.provId === this.bc.provId) { - form.controls.drivers_licence_number.setValidators([Validators.maxLength(9)]); - form.controls.drivers_licence_number.addValidators([Validators.minLength(7)]); + form.controls.drivers_licence_number.setValidators([ + Validators.required, + Validators.minLength(7), + Validators.maxLength(9), + Validators.pattern(/^\d+$/) // Ensure it's numeric + ]); } else { - form.controls.drivers_licence_number.setValidators([Validators.maxLength(20)]); + form.controls.drivers_licence_number.setValidators([ + Validators.required, + Validators.maxLength(20) + ]); } form.controls.drivers_licence_number.updateValueAndValidity(); diff --git a/src/frontend/citizen-portal/src/app/components/dispute-submit-success/dispute-submit-success.component.html b/src/frontend/citizen-portal/src/app/components/dispute-submit-success/dispute-submit-success.component.html index ef74cb886..efcbb7124 100644 --- a/src/frontend/citizen-portal/src/app/components/dispute-submit-success/dispute-submit-success.component.html +++ b/src/frontend/citizen-portal/src/app/components/dispute-submit-success/dispute-submit-success.component.html @@ -28,8 +28,7 @@

Ticket request sent successfully

Next Steps

- A member of the court registry will review your request and you will receive an email to the address you supplied - which will inform you if the ticket request has been approved or rejected, and what to expect next. + A member of the court registry will review your request and inform you if the ticket request has been approved or rejected, and what to expect next.

A confirmation email of the request information will be sent to: @@ -37,13 +36,11 @@

Ticket request sent successfully

{{ noticeOfDispute.email_address }}

- A member of the court registry will review your request and inform you if the ticket request has been approved or - rejected, and what to expect next. + A member of the court registry will review your request and inform you if the ticket request has been approved or rejected, and what to expect next.

- A member of the court registry will review your request and you will receive an email to the address you supplied - which will inform you if the dispute update request has been approved or rejected, and what to expect next. + A member of the court registry will review your request and inform you if the ticket request has been approved or rejected, and what to expect next.


diff --git a/src/frontend/citizen-portal/src/app/components/email-verification/email-verification.component.html b/src/frontend/citizen-portal/src/app/components/email-verification/email-verification.component.html index 5e01ceb6a..ed44937b4 100644 --- a/src/frontend/citizen-portal/src/app/components/email-verification/email-verification.component.html +++ b/src/frontend/citizen-portal/src/app/components/email-verification/email-verification.component.html @@ -17,8 +17,7 @@

Email successfully confirmed.

Next Steps

- A member of the court registry will review your request and you will receive an email to the address you supplied - which will inform you if the ticket request has been approved or rejected, and what to expect next. + A member of the court registry will review your request and inform you if the ticket request has been approved or rejected, and what to expect next.

A confirmation email of the request information will be sent to the email address you supplied. @@ -40,8 +39,7 @@

Your email address could NOT be verified.

- A member of the court registry will review your request and you will be notified if the ticket request has been - approved or rejected, and what to expect next. + A member of the court registry will review your request and inform you if the ticket request has been approved or rejected, and what to expect next.
diff --git a/src/frontend/citizen-portal/src/app/components/landing/landing.component.html b/src/frontend/citizen-portal/src/app/components/landing/landing.component.html index 14c12b4db..05bb95596 100644 --- a/src/frontend/citizen-portal/src/app/components/landing/landing.component.html +++ b/src/frontend/citizen-portal/src/app/components/landing/landing.component.html @@ -93,7 +93,7 @@

Useful links

- RoadSafetyBC + Intersection Safety Camera Tickets (RoadSafetyBC)
Governing drivers, putting road safety policies in place, and diff --git a/src/frontend/citizen-portal/src/app/components/ticket-landing/ticket-landing.component.html b/src/frontend/citizen-portal/src/app/components/ticket-landing/ticket-landing.component.html index 63e296447..5cba4c93d 100644 --- a/src/frontend/citizen-portal/src/app/components/ticket-landing/ticket-landing.component.html +++ b/src/frontend/citizen-portal/src/app/components/ticket-landing/ticket-landing.component.html @@ -9,6 +9,12 @@
  • want to raise a defence
  • +
    + If you received an Intersection Safety Camera ticket (ticket number starts with an 'S'), click + + here + for more information. +
    diff --git a/src/frontend/citizen-portal/src/app/components/ticket-landing/ticket-landing.component.ts b/src/frontend/citizen-portal/src/app/components/ticket-landing/ticket-landing.component.ts index 22ac3e1d5..4ae51d135 100644 --- a/src/frontend/citizen-portal/src/app/components/ticket-landing/ticket-landing.component.ts +++ b/src/frontend/citizen-portal/src/app/components/ticket-landing/ticket-landing.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { AppConfigService } from 'app/services/app-config.service'; @Component({ selector: 'app-ticket-landing', @@ -7,7 +8,10 @@ import { Component } from '@angular/core'; }) export class TicketLandingComponent { + roadSafetyBCVisitUsLink: string; constructor( + private appConfigService: AppConfigService ) { + this.roadSafetyBCVisitUsLink = this.appConfigService.roadSafetyBCVisitUsLink; } } diff --git a/src/frontend/citizen-portal/src/app/services/dispute.service.ts b/src/frontend/citizen-portal/src/app/services/dispute.service.ts index abd627fd6..4f5653574 100644 --- a/src/frontend/citizen-portal/src/app/services/dispute.service.ts +++ b/src/frontend/citizen-portal/src/app/services/dispute.service.ts @@ -133,7 +133,7 @@ export class DisputeService { const data: DialogOptions = { titleKey: "Warning", actionType: "warn", - messageKey: `You could not be authenticated as the contact noted on this dispute. ` + err, + messageKey: `You could not be authenticated as the contact noted on this dispute as your information does not match your BC Services Card information.`, actionTextKey: "Close", cancelHide: true }; diff --git a/src/frontend/citizen-portal/src/app/shared/dialogs/image-ticket-not-found-dialog/image-ticket-not-found-dialog.component.ts b/src/frontend/citizen-portal/src/app/shared/dialogs/image-ticket-not-found-dialog/image-ticket-not-found-dialog.component.ts index e88cdfe2c..473ddba91 100644 --- a/src/frontend/citizen-portal/src/app/shared/dialogs/image-ticket-not-found-dialog/image-ticket-not-found-dialog.component.ts +++ b/src/frontend/citizen-portal/src/app/shared/dialogs/image-ticket-not-found-dialog/image-ticket-not-found-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, ChangeDetectionStrategy} from '@angular/core'; +import { Component, Inject, ChangeDetectionStrategy, HostListener} from '@angular/core'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; import { MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA, MatLegacyDialogRef as MatDialogRef } from '@angular/material/legacy-dialog'; @@ -59,5 +59,15 @@ export class ImageTicketNotFoundDialogComponent { ...options, }; } + @HostListener('document:keydown', ['$event']) + public preventEvent(event: KeyboardEvent): void { + if (/error/i.test(this.options.messageKey)) { + event.preventDefault(); + event.stopPropagation(); + if (event.key === 'Enter') { + this.dialogRef.close(); + } + } + } } diff --git a/src/frontend/staff-portal/src/app/components/jj-dispute-info/jj-dispute/jj-dispute.component.ts b/src/frontend/staff-portal/src/app/components/jj-dispute-info/jj-dispute/jj-dispute.component.ts index 24e1c649d..4945ad531 100644 --- a/src/frontend/staff-portal/src/app/components/jj-dispute-info/jj-dispute/jj-dispute.component.ts +++ b/src/frontend/staff-portal/src/app/components/jj-dispute-info/jj-dispute/jj-dispute.component.ts @@ -19,6 +19,7 @@ import { TabType } from '@shared/enums/tab-type.enum'; import { Dispute } from 'app/services/dispute.service'; import { DisputeStatus } from '@shared/consts/DisputeStatus.model'; import { HearingType } from '@shared/consts/HearingType.model'; +import { ChangeDetectorRef } from '@angular/core'; @Component({ selector: 'app-jj-dispute', @@ -131,7 +132,8 @@ export class JJDisputeComponent implements OnInit { private lookupsService: LookupsService, private config: ConfigService, private documentService: DocumentService, - private historyRecordService: HistoryRecordService + private historyRecordService: HistoryRecordService, + private cdr: ChangeDetectorRef, ) { this.authService.jjList$.subscribe(result => { this.jjList = result; @@ -554,18 +556,40 @@ export class JJDisputeComponent implements OnInit { onUpload(files: FileList) { if (files.length <= 0) return; - - // upload to coms + + // Initially, set the status to "waiting for virus scan..." + let item: FileMetadata = { + fileId: '', + fileName: files[0].name, + virusScanStatus: "waiting for virus scan..." + }; + + // Add the item to the fileData array + this.lastUpdatedJJDispute.fileData.push(item); + + // Manually trigger change detection to ensure the UI is refreshed + this.cdr.detectChanges(); + + // Upload the file this.documentService.apiDocumentPost(this.lastUpdatedJJDispute.noticeOfDisputeGuid, this.fileTypeToUpload, files[0], this.lastUpdatedJJDispute.id) .subscribe(fileId => { - - // add to display of files in DCF - let item: FileMetadata = { fileId: fileId, fileName: files[0].name, virusScanStatus: "waiting for virus scan..." }; - this.lastUpdatedJJDispute.fileData.push(item); + // Once the file is uploaded, update the fileId and status + item.fileId = fileId; + item.virusScanStatus = ""; // or any other status + + // Manually trigger change detection again to update the view + this.cdr.detectChanges(); + + // Call refreshFileHistory() to update the history if needed this.refreshFileHistory(); + }, error => { + // If the upload fails, set an error message + item.virusScanStatus = "upload failed"; + this.cdr.detectChanges(); // Ensure the component updates in case of error }); } + onPrint(isCompleteVersion: boolean) { var type = DcfTemplateType.DcfTemplate; if (!isCompleteVersion) { diff --git a/src/frontend/staff-portal/src/app/components/jj-workbench/jj-dispute-digital-case-file/jj-dispute-digital-case-file.component.ts b/src/frontend/staff-portal/src/app/components/jj-workbench/jj-dispute-digital-case-file/jj-dispute-digital-case-file.component.ts index fa64092f6..6dc9a6466 100644 --- a/src/frontend/staff-portal/src/app/components/jj-workbench/jj-dispute-digital-case-file/jj-dispute-digital-case-file.component.ts +++ b/src/frontend/staff-portal/src/app/components/jj-workbench/jj-dispute-digital-case-file/jj-dispute-digital-case-file.component.ts @@ -49,7 +49,7 @@ export class JJDisputeDigitalCaseFileComponent implements OnInit { ngOnInit(): void { let dataFilter: TableFilter = this.tableFilterService.tableFilters[this.tabIndex]; - dataFilter.status = dataFilter.status ?? ""; + //dataFilter.status = dataFilter.status ?? ""; this.filters = dataFilter; this.previousFilters = { ...dataFilter }; this.currentPage = this.tableFilterService.currentPage[this.tabIndex]; diff --git a/src/frontend/staff-portal/src/app/components/staff-workbench/dispute-decision-inbox/dispute-decision-inbox.component.ts b/src/frontend/staff-portal/src/app/components/staff-workbench/dispute-decision-inbox/dispute-decision-inbox.component.ts index 1663f3736..df8629413 100644 --- a/src/frontend/staff-portal/src/app/components/staff-workbench/dispute-decision-inbox/dispute-decision-inbox.component.ts +++ b/src/frontend/staff-portal/src/app/components/staff-workbench/dispute-decision-inbox/dispute-decision-inbox.component.ts @@ -69,7 +69,7 @@ export class DisputeDecisionInboxComponent implements OnInit { public ngOnInit() { let dataFilter: TableFilter = this.tableFilterService.tableFilters[this.tabIndex]; - dataFilter.status = dataFilter.status ?? ""; + //dataFilter.status = dataFilter.status ?? ""; this.filters = dataFilter; this.previousFilters = { ...dataFilter }; this.currentPage = this.tableFilterService.currentPage[this.tabIndex]; diff --git a/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-inbox/ticket-inbox.component.html b/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-inbox/ticket-inbox.component.html index 527df26cb..c45d0e2ad 100644 --- a/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-inbox/ticket-inbox.component.html +++ b/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-inbox/ticket-inbox.component.html @@ -1,6 +1,6 @@ - + [statusFilterDefault]="statusFilterDefault" (onFilterChanged)="onApplyFilter($event)"> +
    {{ newCount }} NEW ticket diff --git a/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-inbox/ticket-inbox.component.ts b/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-inbox/ticket-inbox.component.ts index 61416aff6..e3b2961dd 100644 --- a/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-inbox/ticket-inbox.component.ts +++ b/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-inbox/ticket-inbox.component.ts @@ -5,7 +5,7 @@ import { DisputeService, Dispute } from 'app/services/dispute.service'; import { DisputeRequestCourtAppearanceYn, DisputeDisputantDetectedOcrIssues, DisputeStatus, DisputeSystemDetectedOcrIssues, PagedDisputeListItemCollection, SortDirection } from 'app/api'; import { LoggerService } from '@core/services/logger.service'; import { AuthService, KeycloakProfile } from 'app/services/auth.service'; -import { TableFilter, TableFilterKeys } from '@shared/models/table-filter-options.model'; +import { TableFilter, TableFilterKeys, TableFilterStatus, TableFilterStatusOptions, TableFilterStatusDefault } from '@shared/models/table-filter-options.model'; import { TableFilterService } from 'app/services/table-filter.service'; @Component({ @@ -22,7 +22,8 @@ export class TicketInboxComponent implements OnInit { dataSource = new MatTableDataSource(this.disputes); tableFilterKeys: TableFilterKeys[] = ["dateSubmittedFrom", "dateSubmittedTo", "disputantSurname", "status", "ticketNumber"]; - statusFilterOptions = [DisputeStatus.New, DisputeStatus.Processing, DisputeStatus.Validated, DisputeStatus.Rejected, DisputeStatus.Cancelled, DisputeStatus.Concluded]; + statusFilterOptions = TableFilterStatusOptions; + statusFilterDefault = TableFilterStatusDefault; displayedColumns: string[] = [ '__RedGreenAlert', @@ -47,6 +48,7 @@ export class TicketInboxComponent implements OnInit { sortBy: Array = ["submittedTs"]; sortDirection: Array = [SortDirection.Desc]; newCount: number = 0; + newCountShow: boolean = false; filters: TableFilter = new TableFilter(); previousFilters: TableFilter = new TableFilter(); @@ -71,7 +73,7 @@ export class TicketInboxComponent implements OnInit { // when authentication token available, get data let dataFilter: TableFilter = this.tableFilterService.tableFilters[this.tabIndex]; - dataFilter.status = dataFilter.status ?? ""; + //dataFilter.status = dataFilter.status ?? []; this.filters = dataFilter; this.previousFilters = { ...dataFilter }; this.currentPage = this.tableFilterService.currentPage[this.tabIndex]; @@ -129,6 +131,8 @@ export class TicketInboxComponent implements OnInit { this.filters = dataFilters; this.previousFilters = { ...dataFilters }; this.getAllDisputes(); + + this.newCountShow = (this.filters && this.filters.status) ? this.filters.status.mapping.includes(DisputeStatus.New) : false; } backWorkbench(element) { diff --git a/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-info/ticket-info.component.html b/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-info/ticket-info.component.html index 0316184f5..74f0f7ab1 100644 --- a/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-info/ticket-info.component.html +++ b/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-info/ticket-info.component.html @@ -2,9 +2,9 @@
    - - arrow_back Back to TRM home - +
    @@ -41,15 +41,18 @@

    Retrieving ticket information...

    - +

    Ticket Image

    -
    - +
    + Click to expand ticket image
    @@ -72,12 +75,15 @@

    Violation Ticket Details

    Ticket number - - + {{ "error.required" | translate }} - + This ticket number is not valid. It must be 2 letters and 8 numbers. @@ -88,8 +94,10 @@

    Violation Ticket Details

    Date - - + + {{ "error.required" | translate }} @@ -100,11 +108,14 @@

    Violation Ticket Details

    Time - - + + {{ "error.required" | translate }} - + Invalid time. Use 24hr format. @@ -124,11 +135,14 @@

    Personal Information

    Surname - - + + {{ "error.required" | translate }} - + {{ "error.max_length" | translate }}30 @@ -139,11 +153,14 @@

    Personal Information

    Given name(s) - - + + {{ "error.required" | translate }} - + {{ "error.max_length" | translate }}30 per name @@ -155,7 +172,9 @@

    Personal Information

    Prov/State of DL - + {{bc.provNm}} {{ @@ -165,7 +184,8 @@

    Personal Information

    state.provNm }}
    - {{ "error.required" | translate }} + {{ "error.required" | translate + }}
    @@ -181,12 +201,26 @@

    Personal Information

    - + {{ "error.required" | translate }} - + Must be a valid BC driver's licence number of seven to nine digits + + + Must be a valid driver's licence number with a maximum of 20 characters. + + + + Must be a numeric value for BC driver's licence +
    @@ -216,8 +250,10 @@

    Count 1

    [value]="statute.__statuteString"> {{ statute.__statuteString }} - {{ "error.required" | translate }} - Invalid statute selection. + {{ "error.required" | translate + }} + Invalid statute selection.
    @@ -226,12 +262,12 @@

    Count 1

    [ngClass]="{'underErrThreshold': applyUnderErrThreshold('counts.count_no_1.ticketed_amount'), 'overErrThreshold': applyOverErrThreshold('counts.count_no_1.ticketed_amount')}"> Fine amount ($) - {{ "error.required" | translate }} + {{ "error.required" | translate + }}
    -
    @@ -275,12 +313,12 @@

    Count 2

    [ngClass]="{'underErrThreshold': applyUnderErrThreshold('counts.count_no_2.ticketed_amount'), 'overErrThreshold': applyOverErrThreshold('counts.count_no_2.ticketed_amount')}"> Fine amount ($) - {{ "error.required" | translate }} + {{ "error.required" | translate + }}
    -
    @@ -324,12 +364,12 @@

    Count 3

    [ngClass]="{'underErrThreshold': applyUnderErrThreshold('counts.count_no_3.ticketed_amount'), 'overErrThreshold': applyOverErrThreshold('counts.count_no_3.ticketed_amount')}"> Fine amount ($) - {{ "error.required" | translate }} + {{ "error.required" | translate + }}
    -
    @@ -374,7 +416,7 @@

    Court Location

    Save
    @@ -539,7 +581,7 @@

    {{ "label.who_contacting" | translate }}

    {{ "label.mailing_address" | translate }} - + {{ "error.required" | translate }} {{ "error.max_length" | translate @@ -569,7 +611,7 @@

    {{ "label.who_contacting" | translate }}

    {{ "label.city" | translate }} - + {{ "error.required" | translate }} {{ "error.max_length" | @@ -580,8 +622,10 @@

    {{ "label.who_contacting" | translate }}

    - {{ "label.province" | translate }} - {{ "label.state" | translate }} + {{ "label.province" | + translate }} + {{ "label.state" | translate + }} @@ -601,8 +645,7 @@

    {{ "label.who_contacting" | translate }}

    {{ "label.province_state" | translate }} - {{ + {{ "error.required" | translate }} {{ "error.max_length" | translate }}30 @@ -616,21 +659,23 @@

    {{ "label.who_contacting" | translate }}

    - {{ "label.postal_code" | translate }} - {{ "label.postal_code" | + translate }} + Zip Code - {{ "label.postal_zip" | translate }} - - {{ "error.required" | translate }} + {{ "error.required" | translate + }} {{ "error.postal_zip" | translate }} @@ -646,9 +691,11 @@

    {{ "label.who_contacting" | translate }}

    - - {{ "error.phone_number" | translate }} + + {{ "error.phone_number" | + translate }}
    @@ -657,7 +704,7 @@

    {{ "label.who_contacting" | translate }}

    {{ "label.email_address" | translate }} - + @@ -693,7 +740,8 @@

    {{ "label.who_contacting" | translate }}

    {{ "label.dl_number" | translate }} @@ -736,14 +784,12 @@

    {{ "label.who_contacting" | translate }}

    (click)="reject()"> Reject -
    - +
    diff --git a/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-info/ticket-info.component.ts b/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-info/ticket-info.component.ts index b6062a55c..7f0b7bcc9 100644 --- a/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-info/ticket-info.component.ts +++ b/src/frontend/staff-portal/src/app/components/staff-workbench/ticket-info/ticket-info.component.ts @@ -1,6 +1,17 @@ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, +} from '@angular/core'; import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { MatLegacyDialog as MatDialog, MatLegacyDialogConfig as MatDialogConfig } from '@angular/material/legacy-dialog'; +import { + MatLegacyDialog as MatDialog, + MatLegacyDialogConfig as MatDialogConfig, +} from '@angular/material/legacy-dialog'; import { ActivatedRoute } from '@angular/router'; import { LoggerService } from '@core/services/logger.service'; import { UtilsService } from '@core/services/utils.service'; @@ -8,13 +19,24 @@ import { FormControlValidators } from '@core/validators/form-control.validators' import { Dispute, DisputeService } from '../../../services/dispute.service'; import { CountryCodeValue, ProvinceCodeValue } from '@config/config.model'; import { ConfigService } from '@config/config.service'; -import { DisputeContactTypeCd, ViolationTicket, ViolationTicketCount, ViolationTicketCountIsAct, ViolationTicketCountIsRegulation, DisputeStatus, DcfTemplateType } from 'app/api'; +import { + DisputeContactTypeCd, + ViolationTicket, + ViolationTicketCount, + ViolationTicketCountIsAct, + ViolationTicketCountIsRegulation, + DisputeStatus, + DcfTemplateType, +} from 'app/api'; import { LookupsService, Statute } from 'app/services/lookups.service'; import { DialogOptions } from '@shared/dialogs/dialog-options.model'; import { ConfirmReasonDialogComponent } from '@shared/dialogs/confirm-reason-dialog/confirm-reason-dialog.component'; import { ConfirmDialogComponent } from '@shared/dialogs/confirm-dialog/confirm-dialog.component'; import { TicketImageDialogComponent } from '@shared/dialogs/ticket-image-dialog/ticket-image-dialog.component'; -import { ViolationTicketService, OCRMessageToDisplay } from 'app/services/violation-ticket.service'; +import { + ViolationTicketService, + OCRMessageToDisplay, +} from 'app/services/violation-ticket.service'; import { StringUtils } from '@core/utils/string-utils.class'; import { TicketImageContainerComponent } from '@shared/dialogs/ticket-image-container/ticket-image-container.component'; @@ -26,7 +48,8 @@ import { TicketImageContainerComponent } from '@shared/dialogs/ticket-image-cont export class TicketInfoComponent implements OnInit { @Input() public disputeInfo: Dispute; @Output() public backInbox: EventEmitter = new EventEmitter(); - @ViewChild(TicketImageContainerComponent) ticketImageContainer: TicketImageContainerComponent; + @ViewChild(TicketImageContainerComponent) + ticketImageContainer: TicketImageContainerComponent; public isMobile: boolean; public previousButtonIcon = 'keyboard_arrow_left'; public validateClicked = false; @@ -47,7 +70,7 @@ export class TicketInfoComponent implements OnInit { public states: ProvinceCodeValue[]; public initialDisputeValues: Dispute; public imageToShow: any; - public errorThreshold: number = 0.800; + public errorThreshold: number = 0.8; public courtLocationFlag: OCRMessageToDisplay; public IsAct = ViolationTicketCountIsAct; public IsRegulation = ViolationTicketCountIsRegulation; @@ -66,8 +89,8 @@ export class TicketInfoComponent implements OnInit { public collapseObj: any = { ticketInformation: true, contactInformation: true, - imageInformation: true - } + imageInformation: true, + }; constructor( protected route: ActivatedRoute, protected formBuilder: FormBuilder, @@ -87,8 +110,14 @@ export class TicketInfoComponent implements OnInit { this.isMobile = this.utilsService.isMobile(); if (this.config.provincesAndStates) { - this.provinces = this.config.provincesAndStates.filter(x => x.ctryId === this.canada.ctryId && x.provAbbreviationCd !== this.bc.provAbbreviationCd); - this.states = this.config.provincesAndStates.filter(x => x.ctryId === this.usa.ctryId); + this.provinces = this.config.provincesAndStates.filter( + (x) => + x.ctryId === this.canada.ctryId && + x.provAbbreviationCd !== this.bc.provAbbreviationCd + ); + this.states = this.config.provincesAndStates.filter( + (x) => x.ctryId === this.usa.ctryId + ); } } @@ -102,7 +131,10 @@ export class TicketInfoComponent implements OnInit { homePhoneNumber: [null, [Validators.maxLength(20)]], emailAddress: [null, [Validators.email, Validators.maxLength(100)]], disputantSurname: [null, [Validators.required, Validators.maxLength(30)]], - disputantGivenNames: [null, [Validators.required, Validators.maxLength(92)]], + disputantGivenNames: [ + null, + [Validators.required, Validators.maxLength(92)], + ], contactTypeCd: [null, [Validators.required]], contactSurnameNm: [null, [Validators.maxLength(30)]], contactGivenNames: [null, [Validators.maxLength(92)]], @@ -115,7 +147,10 @@ export class TicketInfoComponent implements OnInit { addressProvinceCountryId: [null], addressProvinceSeqNo: [null], postalCode: [null], // space needs to be added back to the middle for display - driversLicenceNumber: [null, [Validators.minLength(7), Validators.maxLength(9)]], + driversLicenceNumber: [ + null, + [Validators.minLength(7), Validators.maxLength(9)], + ], driversLicenceProvince: [null, [Validators.maxLength(30)]], driversLicenceProvinceProvId: [null], driversLicenceCountryId: [null], @@ -124,9 +159,18 @@ export class TicketInfoComponent implements OnInit { violationTicket: this.formBuilder.group({ ticketNumber: [null, Validators.required], courtLocation: [null, [Validators.required, Validators.maxLength(50)]], - disputantSurname: [null, [Validators.required, Validators.maxLength(30)]], - disputantGivenNames: [null, [Validators.required, Validators.maxLength(92)]], - disputantDriversLicenceNumber: [null, [Validators.minLength(7), Validators.maxLength(9)]], + disputantSurname: [ + null, + [Validators.required, Validators.maxLength(30)], + ], + disputantGivenNames: [ + null, + [Validators.required, Validators.maxLength(92)], + ], + disputantDriversLicenceNumber: [ + null, + [Validators.minLength(7), Validators.maxLength(9)], + ], driversLicenceProvince: [null, [Validators.maxLength(30)]], driversLicenceCountry: [null], issuedTs: [null, Validators.required], @@ -138,7 +182,7 @@ export class TicketInfoComponent implements OnInit { subsection: [null], paragraph: [null], fullDescription: [null], - ticketedAmount: [null, [FormControlValidators.currency]] + ticketedAmount: [null, [FormControlValidators.currency]], }), violationTicketCount2: this.formBuilder.group({ actOrRegulationNameCode: [null], @@ -148,7 +192,7 @@ export class TicketInfoComponent implements OnInit { subsection: [null], paragraph: [null], fullDescription: [null], - ticketedAmount: [null, [FormControlValidators.currency]] + ticketedAmount: [null, [FormControlValidators.currency]], }), violationTicketCount3: this.formBuilder.group({ actOrRegulationNameCode: [null], @@ -158,12 +202,43 @@ export class TicketInfoComponent implements OnInit { subsection: [null], paragraph: [null], fullDescription: [null], - ticketedAmount: [null, [FormControlValidators.currency]] + ticketedAmount: [null, [FormControlValidators.currency]], }), - violationDate: [null, [Validators.required]], // api returns issued date, extract date from that - violationTime: [null, [Validators.required, Validators.pattern(/^(0[0-9]|1[0-9]|2[0-3])[0-5][0-9]$/)]], // api returns issued date, extract time from that - }) + violationDate: [null, [Validators.required]], // api returns issued date, extract date from that + violationTime: [ + null, + [ + Validators.required, + Validators.pattern(/^(0[0-9]|1[0-9]|2[0-3])[0-5][0-9]$/), + ], + ], // api returns issued date, extract time from that + }), }); + // Custom validator for driversLicenceNumber + this.form + .get('violationTicket') + .get('driversLicenceProvince') + .valueChanges.subscribe((value) => { + if (value === this.bc.provAbbreviationCd) { + this.form + .get('violationTicket') + .get('disputantDriversLicenceNumber') + .setValidators([ + Validators.maxLength(9), + Validators.minLength(7), + Validators.pattern(/^\d+$/), // Only numeric values + ]); + } else { + this.form + .get('violationTicket') + .get('disputantDriversLicenceNumber') + .setValidators([Validators.maxLength(20)]); + } + this.form + .get('violationTicket') + .get('disputantDriversLicenceNumber') + .updateValueAndValidity(); + }); // retreive fresh copy from db this.getDispute(); } @@ -199,87 +274,128 @@ export class TicketInfoComponent implements OnInit { } public onCountryChange(ctryId: number) { - setTimeout(() => { this.form.get('postalCode').setValidators([Validators.maxLength(6)]); - this.form.get('addressProvince').setValidators([Validators.maxLength(30)]); + this.form + .get('addressProvince') + .setValidators([Validators.maxLength(30)]); this.form.get('addressProvince').setValue(null); this.form.get('addressProvinceSeqNo').setValidators(null); this.form.get('addressProvinceSeqNo').setValue(null); this.form.get('addressProvinceCountryId').setValue(null); this.form.get('addressProvinceProvId').setValue(null); - this.form.get('homePhoneNumber').setValidators([Validators.maxLength(20)]); - this.form.get('driversLicenceProvince').setValidators([Validators.maxLength(30)]); - this.form.get("driversLicenceProvinceSeqNo").setValidators(null); + this.form + .get('homePhoneNumber') + .setValidators([Validators.maxLength(20)]); + this.form + .get('driversLicenceProvince') + .setValidators([Validators.maxLength(30)]); + this.form.get('driversLicenceProvinceSeqNo').setValidators(null); if (ctryId === this.canada.ctryId || ctryId == this.usa.ctryId) { - this.form.get('addressProvinceSeqNo').addValidators([Validators.required]); + this.form + .get('addressProvinceSeqNo') + .addValidators([Validators.required]); this.form.get('postalCode').addValidators([Validators.required]); - this.form.get('homePhoneNumber').addValidators([FormControlValidators.phone]); + this.form + .get('homePhoneNumber') + .addValidators([FormControlValidators.phone]); } if (ctryId == this.canada.ctryId) { - this.form.get('postalCode').addValidators([Validators.minLength(6)]); + this.form.get('postalCode').addValidators([Validators.minLength(6)]); this.form.get('addressProvinceCountryId').setValue(ctryId); - this.form.get("addressProvinceSeqNo").setValue(this.bc.provSeqNo); - this.form.get("addressProvinceProvId").setValue(this.bc.provId); - this.form.get("addressProvince").setValue(this.bc.provAbbreviationCd); + this.form.get('addressProvinceSeqNo').setValue(this.bc.provSeqNo); + this.form.get('addressProvinceProvId').setValue(this.bc.provId); + this.form.get('addressProvince').setValue(this.bc.provAbbreviationCd); } this.form.get('postalCode').updateValueAndValidity(); this.form.get('addressProvince').updateValueAndValidity(); - this.form.get("addressProvinceCountryId").updateValueAndValidity(); - this.form.get("addressProvinceSeqNo").updateValueAndValidity(); - this.form.get("addressProvinceProvId").updateValueAndValidity(); + this.form.get('addressProvinceCountryId').updateValueAndValidity(); + this.form.get('addressProvinceSeqNo').updateValueAndValidity(); + this.form.get('addressProvinceProvId').updateValueAndValidity(); this.form.get('homePhoneNumber').updateValueAndValidity(); this.form.get('driversLicenceProvince').updateValueAndValidity(); - this.form.get("driversLicenceProvinceSeqNo").updateValueAndValidity(); + this.form.get('driversLicenceProvinceSeqNo').updateValueAndValidity(); }, 5); } onFullDescription1Keyup() { - this.filteredCount1Statutes = this.filterStatutes(this.form.get('violationTicket').get('violationTicketCount1').get('fullDescription').value); + this.filteredCount1Statutes = this.filterStatutes( + this.form + .get('violationTicket') + .get('violationTicketCount1') + .get('fullDescription').value + ); } onFullDescription2Keyup() { - this.filteredCount2Statutes = this.filterStatutes(this.form.get('violationTicket').get('violationTicketCount2').get('fullDescription').value); + this.filteredCount2Statutes = this.filterStatutes( + this.form + .get('violationTicket') + .get('violationTicketCount2') + .get('fullDescription').value + ); } onFullDescription3Keyup() { - this.filteredCount3Statutes = this.filterStatutes(this.form.get('violationTicket').get('violationTicketCount3').get('fullDescription').value); + this.filteredCount3Statutes = this.filterStatutes( + this.form + .get('violationTicket') + .get('violationTicketCount3') + .get('fullDescription').value + ); } // return a filtered list of statutes public filterStatutes(val: string): Statute[] { - if (!this.lookupsService.statutes || this.lookupsService.statutes.length == 0) return []; - return this.lookupsService.statutes?.filter(option => (option.__statuteString || "").toUpperCase().indexOf((val || "").toUpperCase()) >= 0); + if ( + !this.lookupsService.statutes || + this.lookupsService.statutes.length == 0 + ) + return []; + return this.lookupsService.statutes?.filter( + (option) => + (option.__statuteString || '') + .toUpperCase() + .indexOf((val || '').toUpperCase()) >= 0 + ); } // is the statute valid? on the form public isStatuteValid(countNo: number): boolean { - let countForm = this.form.get('violationTicket').get('violationTicketCount' + countNo.toString()); - if (countForm.get('fullDescription').value && !countForm.get('section').value && - countForm.get('fullDescription').value !== " ") + let countForm = this.form + .get('violationTicket') + .get('violationTicketCount' + countNo.toString()); + if ( + countForm.get('fullDescription').value && + !countForm.get('section').value && + countForm.get('fullDescription').value !== ' ' + ) return false; return true; } public resendEmailVerification() { - this.disputeService.resendEmailVerification(this.lastUpdatedDispute.disputeId) - .subscribe(email => { + this.disputeService + .resendEmailVerification(this.lastUpdatedDispute.disputeId) + .subscribe((email) => { const data: DialogOptions = { - titleKey: "Email Verification Resent", - icon: "email", - actionType: "green", + titleKey: 'Email Verification Resent', + icon: 'email', + actionType: 'green', messageKey: - "The email verification has been resent to the contact email address provided.\n\n" + this.lastUpdatedDispute.emailAddress, - actionTextKey: "Ok", - cancelHide: true + 'The email verification has been resent to the contact email address provided.\n\n' + + this.lastUpdatedDispute.emailAddress, + actionTextKey: 'Ok', + cancelHide: true, }; - this.dialog.open(ConfirmDialogComponent, { data }).afterClosed() - .subscribe((action: any) => { - }); - }) + this.dialog + .open(ConfirmDialogComponent, { data }) + .afterClosed() + .subscribe((action: any) => {}); + }); } public onExpandTicketImage(event: MouseEvent) { @@ -296,77 +412,138 @@ export class TicketInfoComponent implements OnInit { // violation ticket borders only for new status public applyOverErrThreshold(fieldName: string): boolean { if (this.lastUpdatedDispute.status != this.DispStatus.New) return false; - if (this.lastUpdatedDispute.violationTicket.ocrViolationTicket && this.lastUpdatedDispute.violationTicket.ocrViolationTicket.fields[fieldName]?.fieldConfidence <= 0.80) return false; + if ( + this.lastUpdatedDispute.violationTicket.ocrViolationTicket && + this.lastUpdatedDispute.violationTicket.ocrViolationTicket.fields[ + fieldName + ]?.fieldConfidence <= 0.8 + ) + return false; return true; } public applyUnderErrThreshold(fieldName: string): boolean { if (this.lastUpdatedDispute.status != this.DispStatus.New) return false; - if (this.lastUpdatedDispute.violationTicket.ocrViolationTicket && this.lastUpdatedDispute.violationTicket.ocrViolationTicket.fields[fieldName]?.fieldConfidence > 0.80) return false; + if ( + this.lastUpdatedDispute.violationTicket.ocrViolationTicket && + this.lastUpdatedDispute.violationTicket.ocrViolationTicket.fields[ + fieldName + ]?.fieldConfidence > 0.8 + ) + return false; return true; } // change validators on drivers licence number in violation ticket when changing province / state public onViolationTicketDLProvinceChange(provAbbreviationCd: string) { - setTimeout(() => { - if(provAbbreviationCd === null){ - this.form.get('violationTicket').get('driversLicenceCountry').setValue(null); - this.form.get('violationTicket').get('driversLicenceProvince').setValue(null); - this.form.get('violationTicket').get('disputantDriversLicenceNumber').setValidators([Validators.maxLength(20)]); - } else{ + if (provAbbreviationCd === null) { + this.form + .get('violationTicket') + .get('driversLicenceCountry') + .setValue(null); + this.form + .get('violationTicket') + .get('driversLicenceProvince') + .setValue(null); + this.form + .get('violationTicket') + .get('disputantDriversLicenceNumber') + .setValidators([Validators.maxLength(20)]); + } else { if (provAbbreviationCd == this.bc.provAbbreviationCd) { - this.form.get('violationTicket').get('disputantDriversLicenceNumber').setValidators([Validators.maxLength(9)]) - this.form.get('violationTicket').get('disputantDriversLicenceNumber').addValidators([Validators.minLength(7)]); + this.form + .get('violationTicket') + .get('disputantDriversLicenceNumber') + .setValidators([ + Validators.maxLength(9), + Validators.minLength(7), + Validators.pattern(/^\d+$/), + ]); } else { - this.form.get('violationTicket').get('disputantDriversLicenceNumber').setValidators([Validators.maxLength(20)]); + this.form + .get('violationTicket') + .get('disputantDriversLicenceNumber') + .setValidators([Validators.maxLength(20)]); } - let provFound = this.config.provincesAndStates.filter(x => x.provAbbreviationCd === provAbbreviationCd).shift(); + let provFound = this.config.provincesAndStates + .filter((x) => x.provAbbreviationCd === provAbbreviationCd) + .shift(); if (provFound) { - let ctryFound = this.config.countries.filter(x => x.ctryId === provFound.ctryId).shift(); - this.form.get('violationTicket').get('driversLicenceCountry').setValue(ctryFound.ctryLongNm); + let ctryFound = this.config.countries + .filter((x) => x.ctryId === provFound.ctryId) + .shift(); + this.form + .get('violationTicket') + .get('driversLicenceCountry') + .setValue(ctryFound.ctryLongNm); } - } - this.form.get('violationTicket').get('disputantDriversLicenceNumber').updateValueAndValidity(); - }, 5) + } + this.form + .get('violationTicket') + .get('disputantDriversLicenceNumber') + .markAsTouched(); + this.form + .get('violationTicket') + .get('disputantDriversLicenceNumber') + .markAsDirty(); + this.form + .get('violationTicket') + .get('disputantDriversLicenceNumber') + .updateValueAndValidity(); + }, 5); } public onAddressProvinceChange(provId: number) { setTimeout(() => { - let provFound = this.config.provincesAndStates.filter(x => x.provId === provId).shift(); - this.form.get("addressProvinceCountryId").setValue(provFound.ctryId); - this.form.get("addressProvinceSeqNo").setValue(provFound.provSeqNo); - this.form.get("addressProvinceProvId").setValue(provFound.provId); - this.form.get("addressProvince").setValue(provFound.provAbbreviationCd); - }, 0) + let provFound = this.config.provincesAndStates + .filter((x) => x.provId === provId) + .shift(); + this.form.get('addressProvinceCountryId').setValue(provFound.ctryId); + this.form.get('addressProvinceSeqNo').setValue(provFound.provSeqNo); + this.form.get('addressProvinceProvId').setValue(provFound.provId); + this.form.get('addressProvince').setValue(provFound.provAbbreviationCd); + }, 0); } // change validators on drivers licence number in notice of dispute when changing province / state public onNoticeOfDisputeDLProvinceChange(provId: number) { setTimeout(() => { - if(provId === null){ - this.form.get("driversLicenceProvince").setValue(null); - this.form.get("driversLicenceCountryId").setValue(null); - this.form.get("driversLicenceProvinceSeqNo").setValue(null); - this.form.get('driversLicenceNumber').setValidators([Validators.maxLength(20)]); - } else{ - let provFound = this.config.provincesAndStates.filter(x => x.provId === provId).shift(); - this.form.get("driversLicenceCountryId").setValue(provFound.ctryId); - this.form.get("driversLicenceProvinceSeqNo").setValue(provFound.provSeqNo); + if (provId === null) { + this.form.get('driversLicenceProvince').setValue(null); + this.form.get('driversLicenceCountryId').setValue(null); + this.form.get('driversLicenceProvinceSeqNo').setValue(null); + this.form + .get('driversLicenceNumber') + .setValidators([Validators.maxLength(20)]); + } else { + let provFound = this.config.provincesAndStates + .filter((x) => x.provId === provId) + .shift(); + this.form.get('driversLicenceCountryId').setValue(provFound.ctryId); + this.form + .get('driversLicenceProvinceSeqNo') + .setValue(provFound.provSeqNo); if (provId === this.bc.provId) { - this.form.get('driversLicenceNumber').setValidators([Validators.maxLength(9)]); - this.form.get('driversLicenceNumber').addValidators([Validators.minLength(7)]); + this.form + .get('driversLicenceNumber') + .setValidators([Validators.maxLength(9)]); + this.form + .get('driversLicenceNumber') + .addValidators([Validators.minLength(7)]); } else { - this.form.get('driversLicenceNumber').setValidators([Validators.maxLength(20)]); - } + this.form + .get('driversLicenceNumber') + .setValidators([Validators.maxLength(20)]); + } } this.form.get('driversLicenceNumber').updateValueAndValidity(); this.onFieldChange(); - }, 5) + }, 5); } onKeyPressNumbers(event: any, BCOnly: boolean) { - var charCode = (event.which) ? event.which : event.keyCode; + var charCode = event.which ? event.which : event.keyCode; // Only Numbers 0-9 if ((charCode < 48 || charCode > 57) && BCOnly) { event.preventDefault(); @@ -377,49 +554,86 @@ export class TicketInfoComponent implements OnInit { } public onSubmitViolationTicket(): void { - // We are only sending the violation Ticket fields so update a local copy of lastUpdatedDispute // with violation Ticket form fields only that were changed let putDispute = this.lastUpdatedDispute; - putDispute.violationTicket.ticketNumber = this.form.get('violationTicket').get('ticketNumber').value; - putDispute.violationTicket.disputantSurname = this.form.get('violationTicket').get('disputantSurname').value; - putDispute.violationTicket.disputantGivenNames = this.form.get('violationTicket').get('disputantGivenNames').value; - putDispute.violationTicket.disputantDriversLicenceNumber = this.form.get('violationTicket').get('disputantDriversLicenceNumber').value; - putDispute.violationTicket.driversLicenceProvince = this.form.get('violationTicket').get('driversLicenceProvince').value; - putDispute.violationTicket.driversLicenceCountry = this.form.get('violationTicket').get('driversLicenceCountry').value; - putDispute.violationTicket.courtLocation = this.form.get('violationTicket').get('courtLocation').value; + putDispute.violationTicket.ticketNumber = this.form + .get('violationTicket') + .get('ticketNumber').value; + putDispute.violationTicket.disputantSurname = this.form + .get('violationTicket') + .get('disputantSurname').value; + putDispute.violationTicket.disputantGivenNames = this.form + .get('violationTicket') + .get('disputantGivenNames').value; + putDispute.violationTicket.disputantDriversLicenceNumber = this.form + .get('violationTicket') + .get('disputantDriversLicenceNumber').value; + putDispute.violationTicket.driversLicenceProvince = this.form + .get('violationTicket') + .get('driversLicenceProvince').value; + putDispute.violationTicket.driversLicenceCountry = this.form + .get('violationTicket') + .get('driversLicenceCountry').value; + putDispute.violationTicket.courtLocation = this.form + .get('violationTicket') + .get('courtLocation').value; // reconstruct issued date as string from violation date and violation time format yyyy-mm-ddTHH:mm putDispute.violationTicket.issuedTs = this.form.get('violationTicket').get('violationDate').value + - "T" + - this.form.get('violationTicket').get('violationTime').value.substring(0, 2) - + ":" + - this.form.get('violationTicket').get('violationTime').value.substring(2, 4) + "Z"; - putDispute.issuedTs = this.form.get('violationTicket').get('violationDate').value + - "T" + - this.form.get('violationTicket').get('violationTime').value.substring(0, 2) - + ":" + - this.form.get('violationTicket').get('violationTime').value.substring(2, 4) + ":00Z";; + 'T' + + this.form + .get('violationTicket') + .get('violationTime') + .value.substring(0, 2) + + ':' + + this.form + .get('violationTicket') + .get('violationTime') + .value.substring(2, 4) + + 'Z'; + putDispute.issuedTs = + this.form.get('violationTicket').get('violationDate').value + + 'T' + + this.form + .get('violationTicket') + .get('violationTime') + .value.substring(0, 2) + + ':' + + this.form + .get('violationTicket') + .get('violationTime') + .value.substring(2, 4) + + ':00Z'; // Counts 1,2,3 - putDispute.violationTicket.violationTicketCounts = [] as ViolationTicketCount[]; + putDispute.violationTicket.violationTicketCounts = + [] as ViolationTicketCount[]; for (let i = 1; i <= 3; i++) { // stuff 3 violation ticket counts in putDispute - let violationTicketCount = this.form.get('violationTicket').get('violationTicketCount' + i.toString()).value as ViolationTicketCount; + let violationTicketCount = this.form + .get('violationTicket') + .get('violationTicketCount' + i.toString()) + .value as ViolationTicketCount; violationTicketCount.countNo = i; - violationTicketCount.ticketedAmount = this.form.get('violationTicket').get('violationTicketCount' + i.toString()).get('ticketedAmount').value; - putDispute.violationTicket.violationTicketCounts = [...putDispute.violationTicket.violationTicketCounts, violationTicketCount]; + violationTicketCount.ticketedAmount = this.form + .get('violationTicket') + .get('violationTicketCount' + i.toString()) + .get('ticketedAmount').value; + putDispute.violationTicket.violationTicketCounts = [ + ...putDispute.violationTicket.violationTicketCounts, + violationTicketCount, + ]; } this.logger.log('TicketInfoComponent::putDispute', putDispute); this.lastUpdatedDispute = putDispute; - if(this.validateClicked) { + if (this.validateClicked) { this.validate(putDispute); - } - else { + } else { this.putDispute(putDispute); } } @@ -430,23 +644,37 @@ export class TicketInfoComponent implements OnInit { let putDispute = this.lastUpdatedDispute; putDispute.disputantSurname = this.form.get('disputantSurname').value; putDispute.disputantGivenNames = this.form.get('disputantGivenNames').value; - putDispute.driversLicenceNumber = this.form.get('driversLicenceNumber').value; - putDispute.driversLicenceProvince = this.form.get('driversLicenceProvince').value; - putDispute.driversLicenceIssuedCountryId = this.form.get('driversLicenceCountryId').value; - putDispute.driversLicenceIssuedProvinceSeqNo = this.form.get('driversLicenceProvinceSeqNo').value; + putDispute.driversLicenceNumber = this.form.get( + 'driversLicenceNumber' + ).value; + putDispute.driversLicenceProvince = this.form.get( + 'driversLicenceProvince' + ).value; + putDispute.driversLicenceIssuedCountryId = this.form.get( + 'driversLicenceCountryId' + ).value; + putDispute.driversLicenceIssuedProvinceSeqNo = this.form.get( + 'driversLicenceProvinceSeqNo' + ).value; putDispute.homePhoneNumber = this.form.get('homePhoneNumber').value; putDispute.emailAddress = this.form.get('emailAddress').value; putDispute.address = this.form.get('address').value; putDispute.addressCity = this.form.get('addressCity').value; putDispute.addressProvince = this.form.get('addressProvince').value; - putDispute.addressProvinceCountryId = this.form.get('addressProvinceCountryId').value; - putDispute.addressProvinceSeqNo = this.form.get('addressProvinceSeqNo').value; + putDispute.addressProvinceCountryId = this.form.get( + 'addressProvinceCountryId' + ).value; + putDispute.addressProvinceSeqNo = this.form.get( + 'addressProvinceSeqNo' + ).value; putDispute.addressCountryId = this.form.get('addressCountryId').value; putDispute.postalCode = this.form.get('postalCode').value; putDispute.rejectedReason = this.form.get('rejectedReason').value; // set dispute courtagenid from violation ticket courthouse location - let courtFound = this.lookupsService.courthouseAgencies.filter(x => x.name === putDispute.violationTicket.courtLocation).shift(); + let courtFound = this.lookupsService.courthouseAgencies + .filter((x) => x.name === putDispute.violationTicket.courtLocation) + .shift(); putDispute.courtAgenId = courtFound?.id; this.logger.log('TicketInfoComponent::putDispute', putDispute); @@ -456,25 +684,38 @@ export class TicketInfoComponent implements OnInit { } // decompose string into subparagraph, section, subsection, paragraph - public unLegalParagraph(statuteLegalParagraphing: string): { subparagraph: string, section: string, subsection: string, paragraph: string } { - let allParts = statuteLegalParagraphing.split("("); - let subparagraph = ""; - let section = ""; - let subsection = ""; - let paragraph = ""; + public unLegalParagraph(statuteLegalParagraphing: string): { + subparagraph: string; + section: string; + subsection: string; + paragraph: string; + } { + let allParts = statuteLegalParagraphing.split('('); + let subparagraph = ''; + let section = ''; + let subsection = ''; + let paragraph = ''; // parts are section(section)(subsection)(paragraph)(subparagraph) if all are present // extract substrings but dont include final ')' of each part - if (allParts.length > 0) section = allParts[0].substring(0, allParts[0].length); - if (allParts.length > 1) subsection = allParts[1].substring(0, allParts[1].length - 1); - if (allParts.length > 2) paragraph = allParts[2].substring(0, allParts[2].length - 1); - if (allParts.length > 3) subparagraph = allParts[3].substring(0, allParts[3].length - 1); - - return { subparagraph: subparagraph, section: section, subsection: subsection, paragraph: paragraph }; + if (allParts.length > 0) + section = allParts[0].substring(0, allParts[0].length); + if (allParts.length > 1) + subsection = allParts[1].substring(0, allParts[1].length - 1); + if (allParts.length > 2) + paragraph = allParts[2].substring(0, allParts[2].length - 1); + if (allParts.length > 3) + subparagraph = allParts[3].substring(0, allParts[3].length - 1); + + return { + subparagraph: subparagraph, + section: section, + subsection: subsection, + paragraph: paragraph, + }; } public enableNoticeOfDisputeSave(): boolean { - // check for fields invalid in contact information only if (this.form.get('emailAddress').invalid) return false; if (this.form.get('homePhoneNumber').invalid) return false; @@ -520,22 +761,32 @@ export class TicketInfoComponent implements OnInit { } public handleCollapse(name: string) { - this.collapseObj[name] = !this.collapseObj[name] + this.collapseObj[name] = !this.collapseObj[name]; } // get legal paragraphing for a particular count - public getCountLegalParagraphing(countNumber: number, violationTicket: ViolationTicket): string { - let violationTicketCount = violationTicket.violationTicketCounts?.filter(x => x.countNo == countNumber)[0]; + public getCountLegalParagraphing( + countNumber: number, + violationTicket: ViolationTicket + ): string { + let violationTicketCount = violationTicket.violationTicketCounts?.filter( + (x) => x.countNo == countNumber + )[0]; if (violationTicketCount) { - let desc = (this.violationTicketService - .getLegalParagraphing(violationTicketCount) + (violationTicketCount.description ? " " + violationTicketCount.description : "")); + let desc = + this.violationTicketService.getLegalParagraphing(violationTicketCount) + + (violationTicketCount.description + ? ' ' + violationTicketCount.description + : ''); return desc; - } - else return ""; + } else return ''; } // put dispute by id - putDispute(dispute: Dispute, isSubmittingNoticeOfDispute: boolean = false): void { + putDispute( + dispute: Dispute, + isSubmittingNoticeOfDispute: boolean = false + ): void { this.logger.log('TicketInfoComponent::putDispute', dispute); // no need to pass back byte array with image @@ -543,58 +794,69 @@ export class TicketInfoComponent implements OnInit { tempDispute.violationTicket.violationTicketImage = null; const data: DialogOptions = { - titleKey: "Enter File History Comment", - messageKey: "Please describe the changes you made to the dispute.", - actionTextKey: "Save", - actionType: "primary", - cancelTextKey: "Go back", - icon: "error_outline" + titleKey: 'Enter File History Comment', + messageKey: 'Please describe the changes you made to the dispute.', + actionTextKey: 'Save', + actionType: 'primary', + cancelTextKey: 'Go back', + icon: 'error_outline', }; - this.dialog.open(ConfirmReasonDialogComponent, { data }).afterClosed() + this.dialog + .open(ConfirmReasonDialogComponent, { data }) + .afterClosed() .subscribe((action?: any) => { if (action?.output?.response) { - this.disputeService.putDispute(dispute.disputeId, action?.output?.reason, tempDispute).subscribe((response: Dispute) => { - this.logger.info( - 'TicketInfoComponent::putDispute response', - response - ); - this.isViolationTicketCountDeleted = false; - if (!isSubmittingNoticeOfDispute) { - // markAsUntouched form group - this.form.get('violationTicket').markAsUntouched(); - } else { - // markAsUntouched notice of dispute fields - this.form.get('disputantSurname').markAsUntouched(); - this.form.get('disputantGivenNames').markAsUntouched(); - this.form.get('driversLicenceNumber').markAsUntouched(); - this.form.get('driversLicenceProvince').markAsUntouched(); - this.form.get('driversLicenceCountryId').markAsUntouched(); - this.form.get('driversLicenceProvinceSeqNo').markAsUntouched(); - this.form.get('driversLicenceProvinceProvId').markAsUntouched(); - this.form.get('homePhoneNumber').markAsUntouched(); - this.form.get('emailAddress').markAsUntouched(); - this.form.get('address').markAsUntouched(); - this.form.get('addressCity').markAsUntouched(); - this.form.get('addressProvince').markAsUntouched(); - this.form.get('addressProvinceSeqNo').markAsUntouched(); - this.form.get('addressProvinceCountryId').markAsUntouched(); - this.form.get('addressProvinceProvId').markAsUntouched(); - this.form.get('addressCountryId').markAsUntouched(); - this.form.get('postalCode').markAsUntouched(); - this.form.get('rejectedReason').markAsUntouched(); - } - }); + this.disputeService + .putDispute(dispute.disputeId, action?.output?.reason, tempDispute) + .subscribe((response: Dispute) => { + this.logger.info( + 'TicketInfoComponent::putDispute response', + response + ); + this.isViolationTicketCountDeleted = false; + if (!isSubmittingNoticeOfDispute) { + // markAsUntouched form group + this.form.get('violationTicket').markAsUntouched(); + } else { + // markAsUntouched notice of dispute fields + this.form.get('disputantSurname').markAsUntouched(); + this.form.get('disputantGivenNames').markAsUntouched(); + this.form.get('driversLicenceNumber').markAsUntouched(); + this.form.get('driversLicenceProvince').markAsUntouched(); + this.form.get('driversLicenceCountryId').markAsUntouched(); + this.form.get('driversLicenceProvinceSeqNo').markAsUntouched(); + this.form.get('driversLicenceProvinceProvId').markAsUntouched(); + this.form.get('homePhoneNumber').markAsUntouched(); + this.form.get('emailAddress').markAsUntouched(); + this.form.get('address').markAsUntouched(); + this.form.get('addressCity').markAsUntouched(); + this.form.get('addressProvince').markAsUntouched(); + this.form.get('addressProvinceSeqNo').markAsUntouched(); + this.form.get('addressProvinceCountryId').markAsUntouched(); + this.form.get('addressProvinceProvId').markAsUntouched(); + this.form.get('addressCountryId').markAsUntouched(); + this.form.get('postalCode').markAsUntouched(); + this.form.get('rejectedReason').markAsUntouched(); + } + }); } }); } // use violationTicket Service setFieldsFromJSON(dispute: Dispute): Dispute { - if (dispute.violationTicket?.ocrViolationTicket && dispute.violationTicket?.ocrViolationTicket?.fields) { + if ( + dispute.violationTicket?.ocrViolationTicket && + dispute.violationTicket?.ocrViolationTicket?.fields + ) { var fields = dispute.violationTicket?.ocrViolationTicket.fields; - dispute.violationTicket = this.violationTicketService.setViolationTicketFromJSON(dispute.violationTicket.ocrViolationTicket, dispute.violationTicket); + dispute.violationTicket = + this.violationTicketService.setViolationTicketFromJSON( + dispute.violationTicket.ocrViolationTicket, + dispute.violationTicket + ); } return dispute; @@ -606,42 +868,45 @@ export class TicketInfoComponent implements OnInit { let tempDispute = dispute; tempDispute.violationTicket.violationTicketImage = null; - this.disputeService.validateDispute(this.lastUpdatedDispute.disputeId, tempDispute).subscribe({ - next: response => { - this.lastUpdatedDispute.status = this.DispStatus.Validated; - this.form.controls.violationTicket.disable(); - }, - error: err => { }, - complete: () => { } - }); + this.disputeService + .validateDispute(this.lastUpdatedDispute.disputeId, tempDispute) + .subscribe({ + next: (response) => { + this.lastUpdatedDispute.status = this.DispStatus.Validated; + this.form.controls.violationTicket.disable(); + }, + error: (err) => {}, + complete: () => {}, + }); } // dialog, if ok then send to api, on return update status, return to TRM home public approve(): void { const data: DialogOptions = { - titleKey: "Approve ticket resolution request?", + titleKey: 'Approve ticket resolution request?', messageKey: - "Once you approve this request, the information will be sent to ICBC. Are you sure you are ready to approve and submit this request to ARC?", - actionTextKey: "Approve and send request", - actionType: "green", - cancelTextKey: "Go back", - icon: "error_outline", + 'Once you approve this request, the information will be sent to ICBC. Are you sure you are ready to approve and submit this request to ARC?', + actionTextKey: 'Approve and send request', + actionType: 'green', + cancelTextKey: 'Go back', + icon: 'error_outline', }; - this.dialog.open(ConfirmDialogComponent, { data }).afterClosed() + this.dialog + .open(ConfirmDialogComponent, { data }) + .afterClosed() .subscribe((action: any) => { if (action) { - // submit dispute and return to TRM home - this.disputeService.submitDispute(this.lastUpdatedDispute.disputeId).subscribe( - { - next: response => { + this.disputeService + .submitDispute(this.lastUpdatedDispute.disputeId) + .subscribe({ + next: (response) => { this.lastUpdatedDispute.status = this.DispStatus.Processing; this.onBack(); }, - error: err => { }, - complete: () => { } - } - ); + error: (err) => {}, + complete: () => {}, + }); } }); } @@ -649,31 +914,38 @@ export class TicketInfoComponent implements OnInit { // dialog, if ok then send to api, on return update status, return to TRM home public reject(): void { const data: DialogOptions = { - titleKey: "Reject ticket resolution request?", + titleKey: 'Reject ticket resolution request?', messageKey: - "Please enter the reason this request is being rejected. This information will be sent to the user in email notification.", - actionTextKey: "Send rejection notification", - actionType: "warn", - cancelTextKey: "Go back", - icon: "error_outline", - message: "" + 'Please enter the reason this request is being rejected. This information will be sent to the user in email notification.', + actionTextKey: 'Send rejection notification', + actionType: 'warn', + cancelTextKey: 'Go back', + icon: 'error_outline', + message: '', }; - this.dialog.open(ConfirmReasonDialogComponent, { data }).afterClosed() + this.dialog + .open(ConfirmReasonDialogComponent, { data }) + .afterClosed() .subscribe((action?: any) => { if (action?.output?.response) { this.form.get('rejectedReason').setValue(action.output.reason); // update on form for appearances this.lastUpdatedDispute.rejectedReason = action.output.reason; // update to send back on put // udate the reason entered, reject dispute and return to TRM home - this.disputeService.rejectDispute(this.lastUpdatedDispute.disputeId, this.lastUpdatedDispute.rejectedReason).subscribe({ - next: response => { - this.onBack(); - this.lastUpdatedDispute.status = this.DispStatus.Rejected; - this.lastUpdatedDispute.rejectedReason = action.output.reason; - }, - error: err => { }, - complete: () => { } - }); + this.disputeService + .rejectDispute( + this.lastUpdatedDispute.disputeId, + this.lastUpdatedDispute.rejectedReason + ) + .subscribe({ + next: (response) => { + this.onBack(); + this.lastUpdatedDispute.status = this.DispStatus.Rejected; + this.lastUpdatedDispute.rejectedReason = action.output.reason; + }, + error: (err) => {}, + complete: () => {}, + }); } }); } @@ -681,16 +953,18 @@ export class TicketInfoComponent implements OnInit { // dialog, if ok then send to api, on return update status, return to TRM home public cancel(): void { const data: DialogOptions = { - titleKey: "Cancel ticket resolution request?", + titleKey: 'Cancel ticket resolution request?', messageKey: - "Please enter the reason this request is being cancelled. This information will be sent to the user in email notification.", - actionTextKey: "Send cancellation notification", - actionType: "warn", - cancelTextKey: "Go back", - icon: "error_outline", - message: "" + 'Please enter the reason this request is being cancelled. This information will be sent to the user in email notification.', + actionTextKey: 'Send cancellation notification', + actionType: 'warn', + cancelTextKey: 'Go back', + icon: 'error_outline', + message: '', }; - this.dialog.open(ConfirmReasonDialogComponent, { data }).afterClosed() + this.dialog + .open(ConfirmReasonDialogComponent, { data }) + .afterClosed() .subscribe((action?: any) => { if (action?.output?.response) { this.form.get('rejectedReason').setValue(action.output.reason); // update on form for appearances @@ -701,35 +975,52 @@ export class TicketInfoComponent implements OnInit { delete tempDispute.violationTicket.violationTicketImage; // udate the reason entered, cancel dispute and return to TRM home since this will be filtered out - this.disputeService.cancelDispute(this.lastUpdatedDispute.disputeId, action.output.reason).subscribe({ - next: response => { - this.lastUpdatedDispute.status = this.DispStatus.Cancelled; - this.lastUpdatedDispute.rejectedReason = action.output.reason; - this.onBack(); - }, - error: err => { }, - complete: () => { } - }); + this.disputeService + .cancelDispute( + this.lastUpdatedDispute.disputeId, + action.output.reason + ) + .subscribe({ + next: (response) => { + this.lastUpdatedDispute.status = this.DispStatus.Cancelled; + this.lastUpdatedDispute.rejectedReason = action.output.reason; + this.onBack(); + }, + error: (err) => {}, + complete: () => {}, + }); } }); } // count description changed in form onChangeCount(countNo: number, fullDescription: string) { - let countForm = this.form.get('violationTicket').get('violationTicketCount' + countNo.toString()); - let parts = fullDescription.split(" "); // act/code fullsection description + let countForm = this.form + .get('violationTicket') + .get('violationTicketCount' + countNo.toString()); + let parts = fullDescription.split(' '); // act/code fullsection description // lookup legal statute from part[0] which should be in legal paragraph form if (parts && parts.length > 1) { - let foundStatute = this.lookupsService.statutes?.find(x => StringUtils.nullSafeCompare(x.actCode, parts[0]) && StringUtils.nullSafeCompare(x.code, parts[1])); + let foundStatute = this.lookupsService.statutes?.find( + (x) => + StringUtils.nullSafeCompare(x.actCode, parts[0]) && + StringUtils.nullSafeCompare(x.code, parts[1]) + ); if (foundStatute) { countForm.get('actOrRegulationNameCode').setValue(foundStatute.actCode); countForm.get('section').setValue(foundStatute.sectionText); countForm.get('subsection').setValue(foundStatute.subsectionText); countForm.get('paragraph').setValue(foundStatute.paragraphText); countForm.get('subparagraph').setValue(foundStatute.subparagraphText); - countForm.get('description').setValue(foundStatute.shortDescriptionText); - countForm.get('fullDescription').setValue(`${foundStatute.actCode} ${foundStatute.code} ${foundStatute.shortDescriptionText}`); + countForm + .get('description') + .setValue(foundStatute.shortDescriptionText); + countForm + .get('fullDescription') + .setValue( + `${foundStatute.actCode} ${foundStatute.code} ${foundStatute.shortDescriptionText}` + ); } else { countForm.get('actOrRegulationNameCode').setValue(undefined); countForm.get('section').setValue(undefined); @@ -750,13 +1041,10 @@ export class TicketInfoComponent implements OnInit { this.initialDisputeValues = null; this.lastUpdatedDispute = null; - this.disputeService.getDispute(this.disputeInfo.disputeId) - .subscribe((response: Dispute) => { + this.disputeService.getDispute(this.disputeInfo.disputeId).subscribe( + (response: Dispute) => { this.retrieving = false; - this.logger.info( - 'TicketInfoComponent::getDispute response', - response - ); + this.logger.info('TicketInfoComponent::getDispute response', response); // If disputant surname is filled in, then this is not the first time this ticket has been opened, only call setFieldsFromJSON the first time if (response.violationTicket.disputantSurname) { @@ -766,63 +1054,144 @@ export class TicketInfoComponent implements OnInit { } // set court agency id if possible - let courtFound = this.lookupsService.courthouseAgencies.filter(x => x.name === this.initialDisputeValues.violationTicket.courtLocation); - if (courtFound?.length > 0) this.initialDisputeValues.courtAgenId = courtFound[0].id; + let courtFound = this.lookupsService.courthouseAgencies.filter( + (x) => + x.name === this.initialDisputeValues.violationTicket.courtLocation + ); + if (courtFound?.length > 0) + this.initialDisputeValues.courtAgenId = courtFound[0].id; - this.lastUpdatedDispute = JSON.parse(JSON.stringify(this.initialDisputeValues)); + this.lastUpdatedDispute = JSON.parse( + JSON.stringify(this.initialDisputeValues) + ); this.form.patchValue(this.initialDisputeValues); - this.form.get('driversLicenceProvinceSeqNo').setValue(this.initialDisputeValues.driversLicenceIssuedProvinceSeqNo); - this.form.get('driversLicenceCountryId').setValue(this.initialDisputeValues.driversLicenceIssuedCountryId); + this.form + .get('driversLicenceProvinceSeqNo') + .setValue( + this.initialDisputeValues.driversLicenceIssuedProvinceSeqNo + ); + this.form + .get('driversLicenceCountryId') + .setValue(this.initialDisputeValues.driversLicenceIssuedCountryId); // set provId for drivers Licence and address this field is only good client side as angular dropdown needs a single value key to behave well, doesnt like two part key of ctryid & seqno - let provFound = this.config.provincesAndStates.filter(x => x.ctryId === this.initialDisputeValues.driversLicenceIssuedCountryId && x.provSeqNo === this.initialDisputeValues.driversLicenceIssuedProvinceSeqNo).shift(); - if (provFound) this.form.get('driversLicenceProvinceProvId').setValue(provFound.provId); + let provFound = this.config.provincesAndStates + .filter( + (x) => + x.ctryId === + this.initialDisputeValues.driversLicenceIssuedCountryId && + x.provSeqNo === + this.initialDisputeValues.driversLicenceIssuedProvinceSeqNo + ) + .shift(); + if (provFound) + this.form + .get('driversLicenceProvinceProvId') + .setValue(provFound.provId); // set violation date and time using violation ticket issuedTs as source of truth - if (!this.initialDisputeValues.violationTicket.issuedTs) this.initialDisputeValues.violationTicket.issuedTs = this.initialDisputeValues.issuedTs; - let violationDate = this.initialDisputeValues.violationTicket.issuedTs?.split("T"); + if (!this.initialDisputeValues.violationTicket.issuedTs) + this.initialDisputeValues.violationTicket.issuedTs = + this.initialDisputeValues.issuedTs; + let violationDate = + this.initialDisputeValues.violationTicket.issuedTs?.split('T'); if (violationDate && violationDate.length > 1) { - this.form.get('violationTicket').get('issuedTs').setValue(this.initialDisputeValues.violationTicket.issuedTs); - this.form.get('violationTicket').get('violationDate').setValue(violationDate[0]); - this.form.get('violationTicket').get('violationTime').setValue(violationDate[1].split(":")[0] + violationDate[1].split(":")[1]); - this.initialDisputeValues.issuedTs = this.initialDisputeValues.violationTicket.issuedTs; - this.lastUpdatedDispute.issuedTs = this.initialDisputeValues.violationTicket.issuedTs; + this.form + .get('violationTicket') + .get('issuedTs') + .setValue(this.initialDisputeValues.violationTicket.issuedTs); + this.form + .get('violationTicket') + .get('violationDate') + .setValue(violationDate[0]); + this.form + .get('violationTicket') + .get('violationTime') + .setValue( + violationDate[1].split(':')[0] + violationDate[1].split(':')[1] + ); + this.initialDisputeValues.issuedTs = + this.initialDisputeValues.violationTicket.issuedTs; + this.lastUpdatedDispute.issuedTs = + this.initialDisputeValues.violationTicket.issuedTs; } // ticket image - if (this.initialDisputeValues?.violationTicket?.violationTicketImage?.mimeType) { - this.imageToShow = "data:" + this.initialDisputeValues.violationTicket.violationTicketImage.mimeType + ";base64," + this.initialDisputeValues.violationTicket.violationTicketImage.image; - } else if (this.initialDisputeValues?.violationTicket?.violationTicketImage?.image) { - this.imageToShow = 'data:image/png;base64,' + this.initialDisputeValues?.violationTicket?.violationTicketImage?.image; + if ( + this.initialDisputeValues?.violationTicket?.violationTicketImage + ?.mimeType + ) { + this.imageToShow = + 'data:' + + this.initialDisputeValues.violationTicket.violationTicketImage + .mimeType + + ';base64,' + + this.initialDisputeValues.violationTicket.violationTicketImage + .image; + } else if ( + this.initialDisputeValues?.violationTicket?.violationTicketImage + ?.image + ) { + this.imageToShow = + 'data:image/png;base64,' + + this.initialDisputeValues?.violationTicket?.violationTicketImage + ?.image; } // set disputant detected ocr issues - this.flagsForm.get('disputantOcrIssues').setValue(response.disputantOcrIssues); + this.flagsForm + .get('disputantOcrIssues') + .setValue(response.disputantOcrIssues); // set counts 1,2,3 of violation ticket this.assignViolationTicketCountForm(); - this.violationTicketService.getAllOCRMessages(this.lastUpdatedDispute.violationTicket.ocrViolationTicket); + this.violationTicketService.getAllOCRMessages( + this.lastUpdatedDispute.violationTicket.ocrViolationTicket + ); // set system flags for provincial court hearing location this.courtLocationFlag = { - heading: "Court Location", - key: "court_location", - fieldConfidence: this.lastUpdatedDispute.violationTicket.ocrViolationTicket?.fields["court_location"]?.fieldConfidence + heading: 'Court Location', + key: 'court_location', + fieldConfidence: + this.lastUpdatedDispute.violationTicket.ocrViolationTicket?.fields[ + 'court_location' + ]?.fieldConfidence, }; // update address field validators - provFound = this.config.provincesAndStates.filter(x => x.ctryId === this.initialDisputeValues.addressProvinceCountryId && x.provSeqNo === this.initialDisputeValues.addressProvinceSeqNo).shift(); - if (provFound) this.form.get('addressProvinceProvId').setValue(provFound.provId); - this.form.get('addressProvince').setValidators([Validators.maxLength(30)]); - this.form.get('homePhoneNumber').setValidators([Validators.maxLength(20)]); - this.form.get('driversLicenceProvince').setValidators([Validators.maxLength(30)]); - this.form.get("driversLicenceProvinceSeqNo").setValidators(null); - - if (this.form.get('addressCountryId').value === this.canada.ctryId || this.form.get('addressCountryId').value === this.usa.ctryId) { - this.form.get('addressProvinceSeqNo').addValidators([Validators.required]); + provFound = this.config.provincesAndStates + .filter( + (x) => + x.ctryId === this.initialDisputeValues.addressProvinceCountryId && + x.provSeqNo === this.initialDisputeValues.addressProvinceSeqNo + ) + .shift(); + if (provFound) + this.form.get('addressProvinceProvId').setValue(provFound.provId); + this.form + .get('addressProvince') + .setValidators([Validators.maxLength(30)]); + this.form + .get('homePhoneNumber') + .setValidators([Validators.maxLength(20)]); + this.form + .get('driversLicenceProvince') + .setValidators([Validators.maxLength(30)]); + this.form.get('driversLicenceProvinceSeqNo').setValidators(null); + + if ( + this.form.get('addressCountryId').value === this.canada.ctryId || + this.form.get('addressCountryId').value === this.usa.ctryId + ) { + this.form + .get('addressProvinceSeqNo') + .addValidators([Validators.required]); this.form.get('postalCode').addValidators([Validators.required]); - this.form.get('homePhoneNumber').addValidators([FormControlValidators.phone]); + this.form + .get('homePhoneNumber') + .addValidators([FormControlValidators.phone]); } if (this.form.get('addressCountryId').value == this.canada.ctryId) { @@ -830,11 +1199,11 @@ export class TicketInfoComponent implements OnInit { } this.form.get('postalCode').updateValueAndValidity(); this.form.get('addressProvince').updateValueAndValidity(); - this.form.get("addressProvinceSeqNo").updateValueAndValidity(); - this.form.get("addressProvinceProvId").updateValueAndValidity(); + this.form.get('addressProvinceSeqNo').updateValueAndValidity(); + this.form.get('addressProvinceProvId').updateValueAndValidity(); this.form.get('homePhoneNumber').updateValueAndValidity(); this.form.get('driversLicenceProvince').updateValueAndValidity(); - this.form.get("driversLicenceProvinceSeqNo").updateValueAndValidity(); + this.form.get('driversLicenceProvinceSeqNo').updateValueAndValidity(); if (this.lastUpdatedDispute.status !== this.DispStatus.New) { this.form.controls.violationTicket.disable(); @@ -842,52 +1211,73 @@ export class TicketInfoComponent implements OnInit { this.form.get('violationTicket').updateValueAndValidity(); // TCVP-2554 make a static variable to indicate if the TicketInformation section is editable or not. - this.isTicketInformationReadOnly = this.lastUpdatedDispute.status === this.DispStatus.Validated; - }, (error: any) => { + this.isTicketInformationReadOnly = + this.lastUpdatedDispute.status === this.DispStatus.Validated; + }, + (error: any) => { this.retrieving = false; if (error.status == 409) this.conflict = true; - }); + } + ); } - assignViolationTicketCountForm(){ - this.initialDisputeValues.violationTicket.violationTicketCounts.forEach(violationTicketCount => { - let countForm = this.form.get('violationTicket').get('violationTicketCount' + violationTicketCount.countNo.toString()); - countForm.markAsUntouched(); - countForm.get('ticketedAmount').setValue(violationTicketCount.ticketedAmount); - let fullDesc = this.getCountLegalParagraphing(violationTicketCount.countNo, this.initialDisputeValues.violationTicket); - countForm - .get('fullDescription') - .setValue(fullDesc); - countForm.get('description').setValue(violationTicketCount.description); - - // lookup legal statute - let actCode = violationTicketCount.actOrRegulationNameCode; - if (!actCode) { - actCode = violationTicketCount.isAct === ViolationTicketCountIsAct.Y ? "MVA" : "MVR"; - } - const sectionCode = this.getSectionText(violationTicketCount); - let foundStatute = this.lookupsService.statutes?.find(x => StringUtils.nullSafeCompare(x.actCode, actCode) && StringUtils.nullSafeCompare(x.code, sectionCode)); - if (foundStatute) { - countForm.get('actOrRegulationNameCode').setValue(foundStatute.actCode); - countForm.get('section').setValue(foundStatute.sectionText); - countForm.get('subsection').setValue(foundStatute.subsectionText); - countForm.get('paragraph').setValue(foundStatute.paragraphText); - countForm.get('subparagraph').setValue(foundStatute.subparagraphText); + assignViolationTicketCountForm() { + this.initialDisputeValues.violationTicket.violationTicketCounts.forEach( + (violationTicketCount) => { + let countForm = this.form + .get('violationTicket') + .get( + 'violationTicketCount' + violationTicketCount.countNo.toString() + ); + countForm.markAsUntouched(); + countForm + .get('ticketedAmount') + .setValue(violationTicketCount.ticketedAmount); + let fullDesc = this.getCountLegalParagraphing( + violationTicketCount.countNo, + this.initialDisputeValues.violationTicket + ); + countForm.get('fullDescription').setValue(fullDesc); + countForm.get('description').setValue(violationTicketCount.description); + + // lookup legal statute + let actCode = violationTicketCount.actOrRegulationNameCode; + if (!actCode) { + actCode = + violationTicketCount.isAct === ViolationTicketCountIsAct.Y + ? 'MVA' + : 'MVR'; + } + const sectionCode = this.getSectionText(violationTicketCount); + let foundStatute = this.lookupsService.statutes?.find( + (x) => + StringUtils.nullSafeCompare(x.actCode, actCode) && + StringUtils.nullSafeCompare(x.code, sectionCode) + ); + if (foundStatute) { + countForm + .get('actOrRegulationNameCode') + .setValue(foundStatute.actCode); + countForm.get('section').setValue(foundStatute.sectionText); + countForm.get('subsection').setValue(foundStatute.subsectionText); + countForm.get('paragraph').setValue(foundStatute.paragraphText); + countForm.get('subparagraph').setValue(foundStatute.subparagraphText); + } + countForm.updateValueAndValidity(); } - countForm.updateValueAndValidity(); - }); + ); } /** * Returns the full section text from a ViolationTicketCount, ie 44(3)(a)(iii) - * @param vtc - * @returns + * @param vtc + * @returns */ private getSectionText(vtc: ViolationTicketCount): string { - let sectionText = vtc.section ?? ""; - sectionText += vtc.subsection ? '(' + vtc.subsection + ')' : ""; - sectionText += vtc.paragraph ? '(' + vtc.paragraph + ')' : ""; - sectionText += vtc.subparagraph ? '(' + vtc.subparagraph + ')' : ""; + let sectionText = vtc.section ?? ''; + sectionText += vtc.subsection ? '(' + vtc.subsection + ')' : ''; + sectionText += vtc.paragraph ? '(' + vtc.paragraph + ')' : ''; + sectionText += vtc.subparagraph ? '(' + vtc.subparagraph + ')' : ''; return sectionText; } @@ -897,32 +1287,40 @@ export class TicketInfoComponent implements OnInit { onPrint() { var type = DcfTemplateType.DcfTemplate; - - this.disputeService.apiTicketValidationPrintGet(this.lastUpdatedDispute.disputeId, type).subscribe(result => { - if (result) { - var url = URL.createObjectURL(result); - window.open(url); - } else { - alert("File contents not found"); - } - }); + + this.disputeService + .apiTicketValidationPrintGet(this.lastUpdatedDispute.disputeId, type) + .subscribe((result) => { + if (result) { + var url = URL.createObjectURL(result); + window.open(url); + } else { + alert('File contents not found'); + } + }); } onDeleteViolationTicketCount(countNumber: number) { - let violationTicketCounts = this.lastUpdatedDispute.violationTicket.violationTicketCounts; - let count = violationTicketCounts.find(x => x.countNo == countNumber); + let violationTicketCounts = + this.lastUpdatedDispute.violationTicket.violationTicketCounts; + let count = violationTicketCounts.find((x) => x.countNo == countNumber); const data: DialogOptions = { - titleKey: "Delete Violation Ticket Count?", - messageKey: "Are you sure you want to delete this violation ticket count?", - actionTextKey: "Delete", - actionType: "warn", - cancelTextKey: "Cancel", - icon: "delete" + titleKey: 'Delete Violation Ticket Count?', + messageKey: + 'Are you sure you want to delete this violation ticket count?', + actionTextKey: 'Delete', + actionType: 'warn', + cancelTextKey: 'Cancel', + icon: 'delete', }; - this.dialog.open(ConfirmDialogComponent, { data }).afterClosed() + this.dialog + .open(ConfirmDialogComponent, { data }) + .afterClosed() .subscribe((action: any) => { if (action) { - var countForm = this.form.get('violationTicket').get('violationTicketCount' + countNumber.toString()); + var countForm = this.form + .get('violationTicket') + .get('violationTicketCount' + countNumber.toString()); countForm.reset(); countForm.get('ticketedAmount').reset(); countForm.get('fullDescription').reset(); @@ -941,8 +1339,13 @@ export class TicketInfoComponent implements OnInit { count.subsection = null; count.paragraph = null; count.subparagraph = null; - this.lastUpdatedDispute.violationTicket.violationTicketCounts = violationTicketCounts.filter(x => x.countNo != countNumber); - this.lastUpdatedDispute.violationTicket.violationTicketCounts.splice(countNumber - 1, 0, count); + this.lastUpdatedDispute.violationTicket.violationTicketCounts = + violationTicketCounts.filter((x) => x.countNo != countNumber); + this.lastUpdatedDispute.violationTicket.violationTicketCounts.splice( + countNumber - 1, + 0, + count + ); this.isViolationTicketCountDeleted = true; this.lastUpdatedDispute = { ...this.lastUpdatedDispute }; } @@ -951,8 +1354,11 @@ export class TicketInfoComponent implements OnInit { onCancelChanges() { this.assignViolationTicketCountForm(); - this.lastUpdatedDispute.violationTicket.violationTicketCounts = - JSON.parse(JSON.stringify(this.initialDisputeValues.violationTicket.violationTicketCounts)); + this.lastUpdatedDispute.violationTicket.violationTicketCounts = JSON.parse( + JSON.stringify( + this.initialDisputeValues.violationTicket.violationTicketCounts + ) + ); this.lastUpdatedDispute = { ...this.lastUpdatedDispute }; this.validateClicked = false; this.isViolationTicketCountDeleted = false; diff --git a/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-inbox/update-request-inbox.component.html b/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-inbox/update-request-inbox.component.html index bb2ac2ecd..37453b0ea 100644 --- a/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-inbox/update-request-inbox.component.html +++ b/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-inbox/update-request-inbox.component.html @@ -1,5 +1,5 @@ + [defaultStatusFilter]="defaultStatusFilter" (onFilterChanged)="onApplyFilter($event)">
    diff --git a/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-inbox/update-request-inbox.component.ts b/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-inbox/update-request-inbox.component.ts index 899070a58..a3d51518d 100644 --- a/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-inbox/update-request-inbox.component.ts +++ b/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-inbox/update-request-inbox.component.ts @@ -6,7 +6,7 @@ import { Dispute, DisputeStatus } from 'app/api'; import { LoggerService } from '@core/services/logger.service'; import { AuthService, KeycloakProfile } from 'app/services/auth.service'; import { DateUtil } from '@shared/utils/date-util'; -import { TableFilter, TableFilterKeys } from '@shared/models/table-filter-options.model'; +import { TableFilter, TableFilterKeys, TableFilterStatus, TableFilterStatusOptions, UpdateRequestTableStatusDefault } from '@shared/models/table-filter-options.model'; import { TableFilterService } from 'app/services/table-filter.service'; @Component({ @@ -20,7 +20,8 @@ export class UpdateRequestInboxComponent implements OnInit, AfterViewInit { dataSource = new MatTableDataSource(); tableFilterKeys: TableFilterKeys[] = ["dateSubmittedFrom", "dateSubmittedTo", "disputantSurname", "status", "ticketNumber"]; - statusFilterOptions = [DisputeStatus.New, DisputeStatus.Processing, DisputeStatus.Validated, DisputeStatus.Rejected, DisputeStatus.Cancelled, DisputeStatus.Concluded]; + statusFilterOptions = TableFilterStatusOptions; + defaultStatusFilter = UpdateRequestTableStatusDefault; displayedColumns: string[] = [ 'submittedTs', 'ticketNumber', @@ -52,7 +53,7 @@ export class UpdateRequestInboxComponent implements OnInit, AfterViewInit { this.userProfile = userProfile; } }) - + // when authentication token available, get data this.getAllDisputesWithPendingUpdates(); } @@ -92,6 +93,11 @@ export class UpdateRequestInboxComponent implements OnInit, AfterViewInit { else if ("dateSubmittedTo" === field) { return !value || !DateUtil.isValid(value) || DateUtil.isDateOnOrBefore(record.submittedTs, value); } + else if ("status" === field) { + var status = record[field]; + var statusFilters = (value as unknown) as TableFilterStatus; + return statusFilters.mapping.includes(status); + } else if (record[field]) { return record[field].toLocaleLowerCase().indexOf(value.trim().toLocaleLowerCase()) != -1; } diff --git a/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-info/update-request-info.component.html b/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-info/update-request-info.component.html index 5ddd7574c..1b6132056 100644 --- a/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-info/update-request-info.component.html +++ b/src/frontend/staff-portal/src/app/components/staff-workbench/update-request-info/update-request-info.component.html @@ -1,8 +1,8 @@
    - - arrow_back Back to Inbox - +
    diff --git a/src/frontend/staff-portal/src/app/components/staff-workbench/upload/upload.component.ts b/src/frontend/staff-portal/src/app/components/staff-workbench/upload/upload.component.ts index 8bd4f7aae..a886b2a2f 100644 --- a/src/frontend/staff-portal/src/app/components/staff-workbench/upload/upload.component.ts +++ b/src/frontend/staff-portal/src/app/components/staff-workbench/upload/upload.component.ts @@ -6,6 +6,7 @@ import { DocumentService } from 'app/api/api/document.service'; import { FileMetadata } from 'app/api/model/fileMetadata.model'; import { Dispute } from 'app/services/dispute.service'; import { JJDisputeService } from 'app/services/jj-dispute.service'; +import { ChangeDetectorRef } from '@angular/core'; @Component({ selector: 'app-upload', @@ -26,6 +27,7 @@ export class UploadComponent { private dialog: MatDialog, private documentService: DocumentService, private jjDisputeService: JJDisputeService, + private cdr: ChangeDetectorRef, ) { } @@ -63,10 +65,33 @@ export class UploadComponent { onUpload(files: FileList) { if (files.length <= 0) return; - this.documentService.apiDocumentPost(this.disputeInfo.noticeOfDisputeGuid, this.fileTypeToUpload, files[0], - null).subscribe(fileId => { - let item: FileMetadata = { fileId: fileId, fileName: files[0].name, virusScanStatus: "waiting for virus scan..." }; - this.disputeInfo.fileData.push(item); - }); + + // Initially, set the status to "waiting for virus scan..." + let item: FileMetadata = { + fileId: '', + fileName: files[0].name, + virusScanStatus: "waiting for virus scan..." + }; + + // Add the item to the fileData array + this.disputeInfo.fileData.push(item); + + // Manually trigger change detection to ensure the UI is refreshed + this.cdr.detectChanges(); + + // Now upload the file + this.documentService.apiDocumentPost(this.disputeInfo.noticeOfDisputeGuid, this.fileTypeToUpload, files[0], null) + .subscribe(fileId => { + // Once the file is uploaded, update the status and fileId + item.fileId = fileId; + item.virusScanStatus = ""; // or any other status + + // Manually trigger change detection again to update the view + this.cdr.detectChanges(); + }, error => { + // If the upload fails, set an error message + item.virusScanStatus = "upload failed"; + this.cdr.detectChanges(); // Ensure the component updates in case of error + }); } } diff --git a/src/frontend/staff-portal/src/app/components/table-filters/table-filters.component.html b/src/frontend/staff-portal/src/app/components/table-filters/table-filters.component.html index ddefa7b81..e85aae1dc 100644 --- a/src/frontend/staff-portal/src/app/components/table-filters/table-filters.component.html +++ b/src/frontend/staff-portal/src/app/components/table-filters/table-filters.component.html @@ -90,8 +90,7 @@ - {{ statusFilterDefaultText }} - {{item}} + {{item.label}}
    diff --git a/src/frontend/staff-portal/src/app/components/table-filters/table-filters.component.ts b/src/frontend/staff-portal/src/app/components/table-filters/table-filters.component.ts index dd851015b..46a4b0718 100644 --- a/src/frontend/staff-portal/src/app/components/table-filters/table-filters.component.ts +++ b/src/frontend/staff-portal/src/app/components/table-filters/table-filters.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { TableFilter, TableFilterConfigs, TableFilterKeys } from '@shared/models/table-filter-options.model'; +import { TableFilter, TableFilterConfigs, TableFilterKeys, TableFilterStatus, TableFilterStatusDefault } from '@shared/models/table-filter-options.model'; import { DisputeStatus } from 'app/api'; import { LookupsService } from 'app/services/lookups.service'; import { TableFilterService } from 'app/services/table-filter.service'; @@ -14,7 +14,7 @@ export class TableFiltersComponent implements OnInit { @Input() tabIndex: number; @Input() tableFilterKeys: TableFilterKeys[] = []; @Input() statusFilterOptions: DisputeStatus[] = []; - @Input() statusFilterDefaultText: string = "-- select --"; + @Input() defaultStatusFilter: TableFilterStatus = TableFilterStatusDefault; @Input() courthouseTeamNames: string[] = []; @Output() onFilterChanged: EventEmitter = new EventEmitter(); @@ -42,17 +42,17 @@ export class TableFiltersComponent implements OnInit { this.tableFilterConfigs[key] = true; }) this.dataFilters = this.tableFilterService.tableFilters[this.tabIndex]; - this.dataFilters.status = this.dataFilters.status ?? ""; + this.dataFilters.status = this.dataFilters.status ?? this.defaultStatusFilter; } resetSearchFilters() { // Will update search filters in UI this.dataFilters = new TableFilter(); + this.dataFilters.status = this.defaultStatusFilter; // Will re-execute the filter function, but will block UI rendering // Put this call in a Timeout to keep UI responsive. setTimeout(() => { this.tableFilterService.tableFilters[this.tabIndex] = this.dataFilters; - this.dataFilters.status = this.dataFilters.status ?? ""; this.onFilterChanged.emit(this.dataFilters); }, 100); } diff --git a/src/frontend/staff-portal/src/app/services/dispute.service.ts b/src/frontend/staff-portal/src/app/services/dispute.service.ts index e86f28fb3..4d7db5800 100644 --- a/src/frontend/staff-portal/src/app/services/dispute.service.ts +++ b/src/frontend/staff-portal/src/app/services/dispute.service.ts @@ -5,10 +5,11 @@ import { DisputeService as DisputeApiService, Dispute as DisputeBase, DisputeWit import { Observable, BehaviorSubject } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { EventEmitter, Injectable } from '@angular/core'; -import { TableFilter } from '@shared/models/table-filter-options.model'; +import { TableFilter, TableFilterStatus } from '@shared/models/table-filter-options.model'; import { HttpClient, HttpContext, HttpHeaders, HttpResponse } from '@angular/common/http'; import { AuthService } from './auth.service'; + export interface IDisputeService { disputes$: Observable; disputes: Dispute[]; @@ -122,32 +123,33 @@ export class DisputeService implements IDisputeService { */ public getDisputes(sortBy: Array, sortDirection: Array, pageNumber: number, filters?: TableFilter): Observable { - return this.disputeApiService.apiDisputeDisputesGet(filters.status ? undefined : [ExcludeStatus.Cancelled, - ExcludeStatus.Processing, ExcludeStatus.Rejected, ExcludeStatus.Concluded], filters.ticketNumber, filters.disputantSurname, - filters.status ? [filters.status] : [DisputeStatus.New, DisputeStatus.Validated], filters.dateSubmittedFrom, - filters.dateSubmittedTo, undefined, sortBy, sortDirection, undefined, pageNumber, 25) - .pipe( - map((response: PagedDisputeListItemCollection) => { - this.logger.info('DisputeService::getDisputes', response); - this._disputes.next(response.items); - response.items.forEach(dispute => { - dispute = this.joinDisputantGivenNames(dispute); - dispute = this.joinContactGivenNames(dispute); - dispute = this.joinLawyerNames(dispute); - dispute = this.joinAddressLines(dispute); - }); - return response; - }), - catchError((error: any) => { - this.toastService.openErrorToast(this.configService.dispute_error); - this.logger.error( - 'DisputeService::getDisputes error has occurred: ', - error - ); - throw error; - }) - ); + var disputeStatuses = filters.status ? filters.status.mapping : [DisputeStatus.New, DisputeStatus.Validated, DisputeStatus.Processing, DisputeStatus.Rejected, DisputeStatus.Cancelled, DisputeStatus.Concluded]; + + return this.disputeApiService.apiDisputeDisputesGet(undefined, filters.ticketNumber, filters.disputantSurname, + disputeStatuses, filters.dateSubmittedFrom, filters.dateSubmittedTo, undefined, sortBy, sortDirection, undefined, pageNumber, 25) + .pipe( + map((response: PagedDisputeListItemCollection) => { + this.logger.info('DisputeService::getDisputes', response); + this._disputes.next(response.items); + response.items.forEach(dispute => { + dispute = this.joinDisputantGivenNames(dispute); + dispute = this.joinContactGivenNames(dispute); + dispute = this.joinLawyerNames(dispute); + dispute = this.joinAddressLines(dispute); + }); + + return response; + }), + catchError((error: any) => { + this.toastService.openErrorToast(this.configService.dispute_error); + this.logger.error( + 'DisputeService::getDisputes error has occurred: ', + error + ); + throw error; + }) + ); } public get disputantUpdateRequests$(): Observable { diff --git a/src/frontend/staff-portal/src/app/services/table-filter.service.ts b/src/frontend/staff-portal/src/app/services/table-filter.service.ts index 24e8db733..f950785f2 100644 --- a/src/frontend/staff-portal/src/app/services/table-filter.service.ts +++ b/src/frontend/staff-portal/src/app/services/table-filter.service.ts @@ -1,13 +1,14 @@ import { Injectable } from '@angular/core'; -import { TableFilter } from '@shared/models/table-filter-options.model'; +import { TableFilter, UpdateRequestTableStatusDefault} from '@shared/models/table-filter-options.model'; @Injectable({ providedIn: 'root', }) export class TableFilterService { // Temp - tableFilters: TableFilter[] = new Array(4).fill(new TableFilter()); + tableFilters: TableFilter[] = Array.from({ length: 4 }, () => new TableFilter()); currentPage: number[] = new Array(4).fill(1); - constructor( - ) { + constructor() { + //default status for Update Request inbox set to 'New' + this.tableFilters[2].status = UpdateRequestTableStatusDefault; } } \ No newline at end of file diff --git a/src/frontend/staff-portal/src/app/shared/models/table-filter-options.model.ts b/src/frontend/staff-portal/src/app/shared/models/table-filter-options.model.ts index 0a278f8c0..6fb143bdf 100644 --- a/src/frontend/staff-portal/src/app/shared/models/table-filter-options.model.ts +++ b/src/frontend/staff-portal/src/app/shared/models/table-filter-options.model.ts @@ -1,5 +1,44 @@ import { Agency, DisputeStatus } from "app/api"; + + /** + * This is an intermediate layer of "options" to allow us to create special cases like "New & Validated", but still only send a final list of DisputeStatuses we want to the API + */ +export class TableFilterStatus { + label: string; + mapping: DisputeStatus[]; +} + +export const TableFilterStatusOptions = [{ + label: 'ALL', + mapping: [DisputeStatus.New, DisputeStatus.Validated, DisputeStatus.Processing, DisputeStatus.Rejected, DisputeStatus.Cancelled, DisputeStatus.Concluded] + },{ + label: 'NEW', + mapping: [DisputeStatus.New] + },{ + label: 'VALIDATED', + mapping: [DisputeStatus.Validated] + },{ + label: 'NEW & VALIDATED', + mapping: [DisputeStatus.New, DisputeStatus.Validated] + },{ + label: 'PROCESSING', + mapping: [DisputeStatus.Processing] + },{ + label: 'REJECTED', + mapping: [DisputeStatus.Rejected] + },{ + label: 'CANCELLED', + mapping: [DisputeStatus.Cancelled] + },{ + label: 'CONCLUDED', + mapping: [DisputeStatus.Concluded] + }, +]; + +export const TableFilterStatusDefault: TableFilterStatus = TableFilterStatusOptions[0]; +export const UpdateRequestTableStatusDefault: TableFilterStatus = TableFilterStatusOptions[1]; + export class TableFilter { dateSubmittedFrom?: string; dateSubmittedTo?: string; @@ -10,7 +49,7 @@ export class TableFilter { surname?: string; team?: string; courthouseLocation?: Agency[]; - status?: DisputeStatus | ''; + status?: TableFilterStatus = TableFilterStatusDefault; } export type TableFilterKeys = keyof TableFilter; export type TableFilterConfigs = { diff --git a/src/frontend/staff-portal/src/scss/vendors/_ngx-progress.scss b/src/frontend/staff-portal/src/scss/vendors/_ngx-progress.scss index 3b1707720..00fd963ed 100644 --- a/src/frontend/staff-portal/src/scss/vendors/_ngx-progress.scss +++ b/src/frontend/staff-portal/src/scss/vendors/_ngx-progress.scss @@ -21,12 +21,46 @@ // Change the height of the progress bar // to match the height of the top border // of the container - height: 3px !important; + height: 6px !important; } .ng-bar { // Actual bar that translates when the progress increments - background: theme-palette(yellow) !important; + background: repeating-linear-gradient( + 45deg, + lighten(theme-palette(yellow), 0%), + lighten(theme-palette(yellow), 0%) 10px, + theme-palette(yellow) 10px, + theme-palette(yellow) 20px + ) !important; + position: relative; + overflow: hidden; + + &::after { + content: ''; + position: absolute; + top: 0; + left: -50%; + width: 150%; + height: 150%; + background: repeating-linear-gradient( + 45deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.5) 10px, + rgba(255, 255, 255, 0) 20px + ); + box-shadow: 0 0 10px 5px rgba(255, 255, 255, 0.5); + animation: light-sweep 2s infinite; + } +} + +@keyframes light-sweep { + 0% { + left: +50%; + } + 100% { + left: 100%; + } } // .ng-spinner {