Skip to content

Commit

Permalink
Merge pull request #367 from fecgov/feature/262-overlapping-coverage-…
Browse files Browse the repository at this point in the history
…dates

Feature/262 overlapping coverage dates
  • Loading branch information
toddlees authored Jul 5, 2022
2 parents 426f165 + 3acc0ae commit 2e73a34
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 15 deletions.
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

0 comments on commit 2e73a34

Please sign in to comment.