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/355 read only reports #455

Merged
merged 32 commits into from
Aug 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
733c838
Starts on providing the guards necessary to block editing a submitted…
Elaine-Krauss-TCG Aug 10, 2022
4b99626
Implements a working guard
Elaine-Krauss-TCG Aug 11, 2022
99b7060
Merge branch 'develop' into feature/355-read-only-reports
Elaine-Krauss-TCG Aug 11, 2022
54dc5ce
Begins utilizing the new guard. Guard is not behaving as expected
Elaine-Krauss-TCG Aug 11, 2022
dec6709
WIP
Elaine-Krauss-TCG Aug 11, 2022
c69c908
More WIP
Elaine-Krauss-TCG Aug 11, 2022
ec2b6f7
Fixes the layout so that it properly uses the ReportIsEditableService
Elaine-Krauss-TCG Aug 11, 2022
68a76e3
WIP...
Elaine-Krauss-TCG Aug 11, 2022
869e33a
Updates some console.logs for clarity
Elaine-Krauss-TCG Aug 11, 2022
f818e96
The guard and service finally work
Elaine-Krauss-TCG Aug 11, 2022
32235c7
Removes a console.log()
Elaine-Krauss-TCG Aug 11, 2022
74d2bad
Per ticket requirements, the report menu no longer shows the Enter A …
Elaine-Krauss-TCG Aug 11, 2022
465a649
Starts on unit tests
Elaine-Krauss-TCG Aug 11, 2022
95da7ff
Merge branch 'feature/379-view-filing-id' into feature/355-read-only-…
Elaine-Krauss-TCG Aug 11, 2022
7f1c9cc
Restructuring
Elaine-Krauss-TCG Aug 11, 2022
0fc4b82
Removes unused imports
Elaine-Krauss-TCG Aug 11, 2022
66dc5ae
Adds a unit test for the report-is-editable service
Elaine-Krauss-TCG Aug 12, 2022
6def6f0
Removes an unused variable
Elaine-Krauss-TCG Aug 12, 2022
45202bb
Removes an unused variable
Elaine-Krauss-TCG Aug 12, 2022
bb83a82
Refreshes the active report after submitting it
Elaine-Krauss-TCG Aug 12, 2022
c6feab8
WIP
Elaine-Krauss-TCG Aug 12, 2022
0caa1b2
Implements Todd's feedback and makes it so that the menu doesn't subs…
Elaine-Krauss-TCG Aug 12, 2022
846ebfd
Removes unused import
Elaine-Krauss-TCG Aug 12, 2022
fa1c29c
Adds unit tests for the guard
Elaine-Krauss-TCG Aug 12, 2022
a0c91db
Renames report-is-editable.guards -> .guard
Elaine-Krauss-TCG Aug 12, 2022
248a52d
Renames import
Elaine-Krauss-TCG Aug 12, 2022
6f7d130
Resolved merge conflicts
mjtravers Aug 13, 2022
e8ea85c
Added UploadSubmission to report interface
mjtravers Aug 13, 2022
595cc00
Added set active report to guard and resolver
mjtravers Aug 13, 2022
0f55e03
Replaced ActivatedRoute to get report with call to ngrx store
mjtravers Aug 14, 2022
4eef8cd
Fixed issues with COH saving to store and routing
mjtravers Aug 14, 2022
1927875
Updated unit tests
mjtravers Aug 14, 2022
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: 1 addition & 1 deletion front-end/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const routes: Routes = [
path: '',
component: LoginComponent,
pathMatch: 'full',
canActivate:[LoginGuard],
canActivate: [LoginGuard],
},
{ path: 'twoFactLogin', component: TwoFactorLoginComponent },
{ path: 'confirm-2f', component: ConfirmTwoFactorComponent },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { provideMockStore } from '@ngrx/store/testing';
import { NavigationEnd, Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
Expand Down Expand Up @@ -37,6 +38,7 @@ describe('MenuReportComponent', () => {
SharedModule,
PanelMenuModule,
BrowserAnimationsModule,
HttpClientTestingModule,
RouterTestingModule.withRoutes([{ path: 'transactions/report/999/list', redirectTo: '' }]),
],
}).compileComponents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Report, CashOnHand } from '../../../shared/interfaces/report.interface'
import { ReportCodeLabelList } from '../../../shared/utils/reportCodeLabels.utils';
import { f3xReportCodeDetailedLabels, LabelList } from '../../../shared/utils/label.utils';
import { F3xFormTypeLabels } from '../../../shared/models/f3x-summary.model';
import { ReportService } from '../../../shared/services/report.service';

@Component({
selector: 'app-menu-report',
Expand All @@ -24,6 +25,7 @@ export class MenuReportComponent implements OnInit, OnDestroy {
reportCodeLabelList$: Observable<ReportCodeLabelList> = new Observable<ReportCodeLabelList>();
f3xFormTypeLabels: LabelList = F3xFormTypeLabels;
f3xReportCodeDetailedLabels: LabelList = f3xReportCodeDetailedLabels;
reportIsEditableFlag = false;
cashOnHand: CashOnHand = {
report_id: null,
value: null,
Expand All @@ -47,17 +49,19 @@ export class MenuReportComponent implements OnInit, OnDestroy {
/^\/reports\/f3x\/submit\/status\/\d+/, // Submit your report group
];

constructor(private router: Router, private store: Store) {}
constructor(private router: Router, private store: Store, private reportService: ReportService) {}

ngOnInit(): void {
this.reportCodeLabelList$ = this.store.select<ReportCodeLabelList>(selectReportCodeLabelList);
this.reportCodeLabelList$ = this.store
.select<ReportCodeLabelList>(selectReportCodeLabelList)
.pipe(takeUntil(this.destroy$));

// Update the active report whenever a new one is pushed to the ngrx store.
this.store
.select(selectActiveReport)
.pipe(takeUntil(this.destroy$))
.subscribe((report: Report | null) => {
this.activeReport = report;
this.reportIsEditableFlag = this.reportService.isEditable(report);
});

this.store
Expand Down Expand Up @@ -88,6 +92,7 @@ export class MenuReportComponent implements OnInit, OnDestroy {
{
label: 'ENTER A TRANSACTION',
expanded: false,
visible: this.reportIsEditableFlag,
items: [
{
label: 'Cash on hand',
Expand All @@ -101,6 +106,7 @@ export class MenuReportComponent implements OnInit, OnDestroy {
{
label: 'Add a receipt',
routerLink: [`/transactions/report/${this.currentReportId}/create`],
visible: this.reportIsEditableFlag,
},
{ label: 'Add a disbursements', styleClass: 'menu-item-disabled' },
{ label: 'Add loans and debts', styleClass: 'menu-item-disabled' },
Expand All @@ -123,16 +129,31 @@ export class MenuReportComponent implements OnInit, OnDestroy {
label: 'View print preview',
routerLink: [`/reports/f3x/web-print/${this.currentReportId}`],
},
{ label: 'Add a report level memo', routerLink: [`/reports/f3x/memo/${this.currentReportId}`] },
{
label: 'Add a report level memo',
routerLink: [`/reports/f3x/memo/${this.currentReportId}`],
visible: this.reportIsEditableFlag,
},
],
},
{
label: 'SUBMIT YOUR REPORT',
expanded: false,
items: [
{ label: 'Confirm information', routerLink: [`/reports/f3x/submit/step1/${this.currentReportId}`] },
{ label: 'Submit report', routerLink: [`/reports/f3x/submit/step2/${this.currentReportId}`] },
{ label: 'Report status', routerLink: [`/reports/f3x/submit/status/${this.currentReportId}`] },
{
label: 'Confirm information',
routerLink: [`/reports/f3x/submit/step1/${this.currentReportId}`],
visible: this.reportIsEditableFlag,
},
{
label: 'Submit report',
routerLink: [`/reports/f3x/submit/step2/${this.currentReportId}`],
visible: this.reportIsEditableFlag,
},
{
label: 'Report status',
routerLink: [`/reports/f3x/submit/status/${this.currentReportId}`],
},
],
},
];
Expand Down
9 changes: 8 additions & 1 deletion front-end/src/app/layout/sidebar/sidebar.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { provideMockStore } from '@ngrx/store/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { UserLoginData } from 'app/shared/models/user.model';
Expand All @@ -22,7 +23,13 @@ describe('SidebarComponent', () => {
};

await TestBed.configureTestingModule({
imports: [BrowserAnimationsModule, MenuModule, PanelMenuModule, RouterTestingModule.withRoutes([])],
imports: [
BrowserAnimationsModule,
MenuModule,
PanelMenuModule,
HttpClientTestingModule,
RouterTestingModule.withRoutes([]),
],
declarations: [SidebarComponent, MenuReportComponent],
providers: [
provideMockStore({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Subject, takeUntil } from 'rxjs';
import { Store } from '@ngrx/store';
import { setCashOnHandAction } from 'app/store/cash-on-hand.actions';
import { MessageService } from 'primeng/api';
import { ValidateService } from 'app/shared/services/validate.service';
import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X';
import { F3xSummary } from 'app/shared/models/f3x-summary.model';
import { F3xSummaryService } from 'app/shared/services/f3x-summary.service';
import { selectActiveReport } from 'app/store/active-report.selectors';

@Component({
selector: 'app-cash-on-hand',
templateUrl: './cash-on-hand.component.html',
})
export class CashOnHandComponent implements OnInit {
export class CashOnHandComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<boolean>();
formProperties: string[] = ['L6a_cash_on_hand_jan_1_ytd', 'cash_on_hand_date'];
report: F3xSummary | undefined;
formSubmitted = false;
form: FormGroup = this.fb.group(this.validateService.getFormGroupFields(this.formProperties));

constructor(
public router: Router,
private activatedRoute: ActivatedRoute,
private f3xSummaryService: F3xSummaryService,
private validateService: ValidateService,
private fb: FormBuilder,
Expand All @@ -30,7 +32,10 @@ export class CashOnHandComponent implements OnInit {
) {}

ngOnInit(): void {
this.report = this.activatedRoute.snapshot.data['report'];
this.store
.select(selectActiveReport)
.pipe(takeUntil(this.destroy$))
.subscribe((report) => (this.report = report as F3xSummary));

// Initialize validation tracking of current JSON schema and form data
this.validateService.formValidatorSchema = f3xSchema;
Expand All @@ -42,6 +47,11 @@ export class CashOnHandComponent implements OnInit {
this.form.patchValue({ ...this.report });
}

ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.complete();
}

public save(): void {
this.formSubmitted = true;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { AbstractControl, FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { Router, ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store';
import {
electionReportCodes,
Expand All @@ -19,11 +19,13 @@ import { F3xSummaryService } from 'app/shared/services/f3x-summary.service';
import { ValidateService } from 'app/shared/services/validate.service';
import { f3xReportCodeDetailedLabels, LabelUtils, PrimeOptions, StatesCodeLabels } from 'app/shared/utils/label.utils';
import { selectCommitteeAccount } from 'app/store/committee-account.selectors';
import { selectActiveReport } from 'app/store/active-report.selectors';
import { environment } from 'environments/environment';
import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X';
import { MessageService } from 'primeng/api';
import { Subject, takeUntil } from 'rxjs';
import { LabelList } from '../../../shared/utils/label.utils';
import { ReportService } from 'app/shared/services/report.service';

@Component({
selector: 'app-create-f3x-step1',
Expand Down Expand Up @@ -59,12 +61,22 @@ export class CreateF3XStep1Component implements OnInit, OnDestroy {
private fb: FormBuilder,
private f3xSummaryService: F3xSummaryService,
private messageService: MessageService,
protected router: Router,
private activatedRoute: ActivatedRoute,
protected router: Router
private reportService: ReportService
) {}

ngOnInit(): void {
const report: F3xSummary = this.activatedRoute.snapshot.data['report'];
const reportId = this.activatedRoute.snapshot.data['reportId'];
this.store
.select(selectActiveReport)
.pipe(takeUntil(this.destroy$))
.subscribe((report) => {
if (reportId && report) {
this.form.patchValue(report);
}
});

this.store
.select(selectCommitteeAccount)
.pipe(takeUntil(this.destroy$))
Expand Down Expand Up @@ -92,9 +104,6 @@ export class CreateF3XStep1Component implements OnInit, OnDestroy {
report_code: this.getReportCodes()[0],
});
});
if (report) {
this.form.patchValue(report);
}
});
this.stateOptions = LabelUtils.getPrimeOptions(StatesCodeLabels);

Expand All @@ -111,85 +120,75 @@ export class CreateF3XStep1Component implements OnInit, OnDestroy {
}

/**
* Checks if a field's date is within another report's dates or if
* Checks if a field's date is within another report's dates or if
* another report's dates fall within the form's "from" and "through" dates
*
*
* @param fieldDate {Date} the date of the field being checked
* @param fromDate {Date} the form's "from" date
* @param throughDate {Date} the form's "through" date
* @param targetDate {F3xCoverageDates} the object whose date is being checked for an overlap
* @returns true if there is an overlap in dates
*/
checkForDateOverlap(
fieldDate: Date,
fromDate: Date,
throughDate: Date,
targetDate: F3xCoverageDates
): boolean {
return (
targetDate &&
checkForDateOverlap(fieldDate: Date, fromDate: Date, throughDate: Date, targetDate: F3xCoverageDates): boolean {
return (targetDate &&
targetDate.coverage_from_date &&
targetDate.coverage_through_date &&
(
//The form's date is between another report's from/through date
( fieldDate >= targetDate.coverage_from_date &&
fieldDate <= targetDate.coverage_through_date ) ||
//The form's date is between another report's from/through date
((fieldDate >= targetDate.coverage_from_date && fieldDate <= targetDate.coverage_through_date) ||
//Another report's dates are inside the form's dates
( fromDate <= targetDate.coverage_from_date &&
(fromDate <= targetDate.coverage_from_date &&
throughDate >= targetDate.coverage_from_date &&
fromDate <= targetDate.coverage_through_date &&
throughDate >= targetDate.coverage_through_date)
)
) as boolean;
fromDate <= targetDate.coverage_through_date &&
throughDate >= targetDate.coverage_through_date))) as boolean;
}

buildCoverageDatesValidator(): ValidatorFn {
/**
* This is being used as a group validator, so it will always be called with a FormGroup
* for the parameter, but addValidators() only takes a method that returns a ValidatorFn,
* and a ValidatorFn must have parameters of AbstractControl or AbstractControl | FormGroup
*
*
* Additionally, a unit test is present to make sure that the constructed validator function
* does not explode if passed an AbstractControl parameter.
*/
return (toValidate: AbstractControl | FormGroup): null => {
const group = toValidate as FormGroup;
if (group.controls){
if (group.controls) {
const fromDate = group.controls['coverage_from_date'];
const throughDate = group.controls['coverage_through_date'];
if (this.f3xCoverageDatesList) {
for (const formValue of [fromDate, throughDate]){
for (const formValue of [fromDate, throughDate]) {
const overlap = this.f3xCoverageDatesList.find((f3xCoverageDate) => {
return this.checkForDateOverlap(formValue.value, fromDate.value, throughDate.value, f3xCoverageDate);
});
if (overlap){
if (overlap) {
this.setCoverageOverlapError(formValue, overlap);
} else {
const errors = formValue.errors;
if (errors){
delete errors["invaliddate"];
if (errors) {
delete errors['invaliddate'];
formValue.setErrors(errors);
}
}
}
}
}
return null;
}
};
}

setCoverageOverlapError(formValue: AbstractControl, overlap: F3xCoverageDates){
setCoverageOverlapError(formValue: AbstractControl, overlap: F3xCoverageDates) {
const f3xReportCodeLabel = f3xReportCodeDetailedLabels.find((label) => label[0] === overlap.report_code);
const reportCodeLabel = f3xReportCodeLabel
? f3xReportCodeLabel[1] || overlap.report_code?.valueOf
: 'invalid name';
const reportCodeLabel = f3xReportCodeLabel ? f3xReportCodeLabel[1] || overlap.report_code?.valueOf : 'invalid name';
const overlapFromDate = this.fecDatePipe.transform(overlap.coverage_from_date);
const overlapThroughDate = this.fecDatePipe.transform(overlap.coverage_through_date);
const errors = formValue.errors ? formValue.errors : {};
errors["invaliddate"] = {msg:`You have entered coverage dates that overlap ` +
errors['invaliddate'] = {
msg:
`You have entered coverage dates that overlap ` +
`the coverage dates of the following report: ${reportCodeLabel} ` +
` ${overlapFromDate} - ${overlapThroughDate}`,
}
};
formValue.setErrors(errors);
}

Expand Down Expand Up @@ -238,6 +237,9 @@ export class CreateF3XStep1Component implements OnInit, OnDestroy {

this.f3xSummaryService.create(summary, this.formProperties).subscribe((report: F3xSummary) => {
if (jump === 'continue') {
// Save report to Cash On Hand in the store if necessary by pulling the reports table data.
this.reportService.getTableData().subscribe();

this.router.navigateByUrl(`/reports/f3x/create/step2/${report.id}`);
} else {
this.router.navigateByUrl('/reports');
Expand Down
Loading