Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/262 overlapping coverage dates #367

Merged
merged 4 commits into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions front-end/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { TwoFactorLoginComponent } from './login/two-factor-login/two-factor-log
import { ConfirmTwoFactorComponent } from './login/confirm-two-factor/confirm-two-factor.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HttpErrorInterceptor } from './shared/interceptors/http-error.interceptor';
import { FecDatePipe } from './shared/pipes/fec-date.pipe';

// Save ngrx store to localStorage dynamically
function localStorageSyncReducer(reducer: ActionReducer<AppState>): ActionReducer<AppState> {
Expand Down Expand Up @@ -96,6 +97,7 @@ const metaReducers: Array<MetaReducer<AppState, Action>> = [localStorageSyncRedu
ConfirmationService,
MessageService,
{ provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true },
FecDatePipe,
],
bootstrap: [AppComponent],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ <h3>Covering period</h3>
name="coverage_from_date"
formControlName="coverage_from_date"
></p-calendar>
<app-error-messages fieldName="coverage_from_date" [formSubmitted]="formSubmitted"></app-error-messages>
<app-error-messages [form]="form" fieldName="coverage_from_date" [formSubmitted]="formSubmitted"></app-error-messages>
</div>
</div>
<div class="col-6">
Expand All @@ -132,7 +132,7 @@ <h3>Covering period</h3>
name="coverage_through_date"
formControlName="coverage_through_date"
></p-calendar>
<app-error-messages fieldName="coverage_through_date" [formSubmitted]="formSubmitted"></app-error-messages>
<app-error-messages [form]="form" fieldName="coverage_through_date" [formSubmitted]="formSubmitted"></app-error-messages>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { RadioButtonModule } from 'primeng/radiobutton';
import { SelectButtonModule } from 'primeng/selectbutton';
import { CreateF3XStep1Component, F3xReportTypeCategories } from './create-f3x-step1.component';
import { selectUserLoginData } from 'app/store/login.selectors';
import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe';

describe('CreateF3XStep1Component', () => {
let component: CreateF3XStep1Component;
Expand Down Expand Up @@ -50,6 +51,7 @@ describe('CreateF3XStep1Component', () => {
F3xSummaryService,
FormBuilder,
MessageService,
FecDatePipe,
provideMockStore({
initialState: { fecfile_online_userLoginData: userLoginData },
selectors: [{ selector: selectUserLoginData, value: userLoginData }],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { Store } from '@ngrx/store';
import {
electionReportCodes,
F3xCoverageDates,
F3xFormTypes,
F3xReportCode,
F3xReportCodes,
F3xSummary,
monthlyElectionYearReportCodes,
monthlyNonElectionYearReportCodes,
quarterlyElectionYearReportCodes,
quarterlyNonElectionYearReportCodes,
quarterlyNonElectionYearReportCodes
} from 'app/shared/models/f3x-summary.model';
import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe';
import { F3xSummaryService } from 'app/shared/services/f3x-summary.service';
import { ValidateService } from 'app/shared/services/validate.service';
import { LabelList, LabelUtils, PrimeOptions, StatesCodeLabels } from 'app/shared/utils/label.utils';
import { selectCommitteeAccount } from 'app/store/committee-account.selectors';
import { ValidateService } from 'app/shared/services/validate.service';
import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X';
import { Subject, takeUntil } from 'rxjs';
import { Store } from '@ngrx/store';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { environment } from 'environments/environment';
import { ActivatedRoute, Router } from '@angular/router';
import { F3xSummaryService } from 'app/shared/services/f3x-summary.service';
import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X';
import { MessageService } from 'primeng/api';
import { Subject, takeUntil } from 'rxjs';

@Component({
selector: 'app-create-f3x-step1',
Expand Down Expand Up @@ -77,16 +79,18 @@ export class CreateF3XStep1Component implements OnInit, OnDestroy {
];

readonly F3xReportTypeCategories = F3xReportTypeCategories;
private f3xCoverageDatesList: F3xCoverageDates[] | undefined;

constructor(
private store: Store,
private validateService: ValidateService,
private fecDatePipe: FecDatePipe,
private fb: FormBuilder,
private f3xSummaryService: F3xSummaryService,
private messageService: MessageService,
private activatedRoute: ActivatedRoute,
protected router: Router
) {}
) { }

ngOnInit(): void {
const report: F3xSummary = this.activatedRoute.snapshot.data['report'];
Expand Down Expand Up @@ -123,11 +127,59 @@ export class CreateF3XStep1Component implements OnInit, OnDestroy {
});
this.stateOptions = LabelUtils.getPrimeOptions(StatesCodeLabels);

this.f3xSummaryService.getF3xCoverageDates().subscribe((dates) => {
this.f3xCoverageDatesList = dates;
});
this.form.controls['coverage_from_date'].addValidators(
this.buildCoverageDatesValidator('coverage_from_date'));
this.form.controls['coverage_through_date'].addValidators(
this.buildCoverageDatesValidator('coverage_through_date'));

// Initialize validation tracking of current JSON schema and form data
this.validateService.formValidatorSchema = f3xSchema;
this.validateService.formValidatorForm = this.form;
}

buildCoverageDatesValidator(valueFormControlName: string,): ValidatorFn {
return (): ValidationErrors | null => {
let result: ValidationErrors | null = null;
const formValue: Date = this.form?.get(valueFormControlName)?.value
if (this.f3xCoverageDatesList && formValue) {
const retval = this.f3xCoverageDatesList.find((f3xCoverageDate) => {
return (f3xCoverageDate &&
f3xCoverageDate.coverage_from_date &&
f3xCoverageDate.coverage_through_date &&
(formValue >= f3xCoverageDate.coverage_from_date &&
formValue <= f3xCoverageDate.coverage_through_date)
)
});
result = this.getCoverageDatesValidator(retval);
}
return result;
};
}

getCoverageDatesValidator(f3xCoverageDates?: F3xCoverageDates) {
let retval: ValidationErrors | null = null;
if (f3xCoverageDates) {
const f3xReportCodeLabel = this.f3xReportCodeCreationLabels.find(
label => label[0] === f3xCoverageDates.report_code);
const reportCodeLabel = f3xReportCodeLabel ? f3xReportCodeLabel[1] ||
f3xCoverageDates.report_code?.valueOf : 'invalid name';
const coverageFromDate = this.fecDatePipe.transform(
f3xCoverageDates.coverage_from_date);
const coverageThroughDate = this.fecDatePipe.transform(
f3xCoverageDates.coverage_through_date);
retval = {};
retval['invaliddate'] = {
msg: `You have entered coverage dates that overlap ` +
`the coverage dates of the following report: ${reportCodeLabel} ` +
` ${coverageFromDate} - ${coverageThroughDate}`
};
}
return retval;
}

ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.complete();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
<small class="p-error" *ngIf="control?.hasError('required')">{{ requiredErrorMessage }}</small>
<small class="p-error" *ngIf="control?.hasError('minlength')">{{ minLengthErrorMessage }}</small>
<small class="p-error" *ngIf="control?.hasError('maxlength')">{{ maxLengthErrorMessage }}</small>
<small class="p-error" *ngIf="control?.hasError('invaliddate')">{{ invalidDateErrorMessage }}</small>
<small
class="p-error"
*ngIf="!control?.hasError('minlength') && !control?.hasError('maxlength') && control?.hasError('pattern')"
*ngIf="!control?.hasError('minlength') && !control?.hasError('maxlength') && !control?.hasError('invaliddate') && control?.hasError('pattern')"
>{{ patternErrorMessage }}</small
>
</ng-container>
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ export class ErrorMessagesComponent implements OnInit {
return `This field cannot contain more than ${this.control?.errors?.['maxlength']?.requiredLength} alphanumeric characters.`;
}

private _invalidDateErrorMessage = '';
@Input() set invalidDateErrorMessage(value: string) {
this._invalidDateErrorMessage = value;
}
get invalidDateErrorMessage(): string {
if (this._invalidDateErrorMessage) {
return this._invalidDateErrorMessage;
}
return this.control?.errors?.['invaliddate']?.msg;
}

control: FormGroup | null = null;

ngOnInit(): void {
Expand Down
14 changes: 12 additions & 2 deletions front-end/src/app/shared/models/f3x-summary.model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BaseModel } from './base.model';
import { Report } from '../interfaces/report.interface';
import { plainToClass, Transform } from 'class-transformer';
import { Report } from '../interfaces/report.interface';
import { LabelList } from '../utils/label.utils';
import { BaseModel } from './base.model';

export enum F3xFormTypes {
F3XN = 'F3XN',
Expand Down Expand Up @@ -146,6 +146,16 @@ export const electionReportCodes: F3xReportCode[] = [
F3xReportCodes.TwelveS,
];

export class F3xCoverageDates {
@Transform(BaseModel.dateTransform) coverage_from_date: Date | null = null;
@Transform(BaseModel.dateTransform) coverage_through_date: Date | null = null;
report_code: F3xReportCodes | null = null;
// prettier-ignore
static fromJSON(json: any): F3xCoverageDates { // eslint-disable-line @typescript-eslint/no-explicit-any
return plainToClass(F3xCoverageDates, json);
}
}

export class F3xSummary extends BaseModel implements Report {
id: number | null = null;

Expand Down
9 changes: 8 additions & 1 deletion front-end/src/app/shared/services/f3x-summary.service.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { F3xCoverageDates, F3xSummary } from '../models/f3x-summary.model';
import { ApiService } from './api.service';
import { F3xSummary } from '../models/f3x-summary.model';

@Injectable({
providedIn: 'root',
})
export class F3xSummaryService {
constructor(private apiService: ApiService) {}

public getF3xCoverageDates(): Observable<F3xCoverageDates[]> {
return this.apiService
.get<F3xCoverageDates[]>(`/f3x-summaries/coverage_dates`)
.pipe(map((response) => response.map(fx3CoverageDate =>
F3xCoverageDates.fromJSON(fx3CoverageDate))));
}

public get(id: number): Observable<F3xSummary> {
return this.apiService
.get<F3xSummary>(`/f3x-summaries/${id}`)
Expand Down