Skip to content

Commit

Permalink
fix: improved usability of children overview in schools section
Browse files Browse the repository at this point in the history
fixes #982
  • Loading branch information
kirtijadhav authored Sep 25, 2021
1 parent 3aa384f commit 590c145
Show file tree
Hide file tree
Showing 13 changed files with 448 additions and 580 deletions.
2 changes: 2 additions & 0 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { Note } from "./child-dev-project/notes/model/note";
import { EventNote } from "./child-dev-project/attendance/model/event-note";
import { waitForChangeTo } from "./core/session/session-states/session-utils";
import { environment } from "../environments/environment";
import { ChildSchoolRelation } from "./child-dev-project/children/model/childSchoolRelation";

@Component({
selector: "app-root",
Expand Down Expand Up @@ -81,6 +82,7 @@ export class AppComponent implements OnInit {
this.entityConfigService.addConfigAttributes(HistoricalEntityData);
this.entityConfigService.addConfigAttributes(Note);
this.entityConfigService.addConfigAttributes(EventNote);
this.entityConfigService.addConfigAttributes(ChildSchoolRelation);
});

// If loading the config earlier (in a module constructor or through APP_INITIALIZER) a runtime error occurs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ export class ChildSchoolRelation extends Entity {
@DatabaseField({
dataType: "date-only",
label: $localize`:Label for the start date of a relation:From`,
description: $localize`:Description of the start date of a relation:The date a child joins a school`,
})
start: Date;
@DatabaseField({
dataType: "date-only",
label: $localize`:Label for the end date of a relation:To`,
description: $localize`:Description of the end date of a relation:The date of a child leaving the school`,
})
end: Date;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
<button
mat-stroked-button
color="accent"
(click)="addChildClick()"
>
<mat-icon
class="button-icon"
aria-label="add child"
fontIcon="fa-plus-circle"
></mat-icon>
<span fxHide.lt-md="true" i18n="Add New Button"> Add {{ addButtonLabel }}</span>
</button>
<app-entity-subrecord
[records]="children"
[records]="records"
[columns]="columns"
[showEntity]="routeToChild.bind(this)"
[editable]="false"
[newRecordFactory]="generateNewRecordFactory()"
></app-entity-subrecord>

Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,21 @@ import { ChildrenOverviewComponent } from "./children-overview.component";
import { SchoolsModule } from "../schools.module";
import { School } from "../model/school";
import { Child } from "../../children/model/child";
import { SchoolsService } from "../schools.service";
import { RouterTestingModule } from "@angular/router/testing";
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { Router } from "@angular/router";
import { MockSessionModule } from "../../../core/session/mock-session.module";
import { MatDialog } from "@angular/material/dialog";
import { EntityFormComponent } from "../../../core/entity-components/entity-form/entity-form/entity-form.component";
import { EntityFormService } from "../../../core/entity-components/entity-form/entity-form.service";
import { AlertService } from "../../../core/alerts/alert.service";
import { EventEmitter } from "@angular/core";
import { PanelConfig } from "../../../core/entity-components/entity-details/EntityDetailsConfig";
import { ChildSchoolRelation } from "../../children/model/childSchoolRelation";
import { ChildrenService } from "../../children/children.service";

describe("ChildrenOverviewComponent", () => {
let component: ChildrenOverviewComponent;
let fixture: ComponentFixture<ChildrenOverviewComponent>;
let mockSchoolsService: jasmine.SpyObj<SchoolsService>;
let mockChildrenService: jasmine.SpyObj<ChildrenService>;

beforeEach(
waitForAsync(() => {
mockSchoolsService = jasmine.createSpyObj(["getChildrenForSchool"]);
mockChildrenService = jasmine.createSpyObj(["queryRelationsOf"]);

TestBed.configureTestingModule({
declarations: [],
Expand All @@ -39,35 +33,39 @@ describe("ChildrenOverviewComponent", () => {
NoopAnimationsModule,
MockSessionModule.withState(),
],
providers: [{ provide: SchoolsService, useValue: mockSchoolsService }],
providers: [
{ provide: ChildrenService, useValue: mockChildrenService },
],
}).compileComponents();
})
);

beforeEach(() => {
fixture = TestBed.createComponent(ChildrenOverviewComponent);
component = fixture.componentInstance;
component.entity = new School();
fixture.detectChanges();
});

it("should create", () => {
expect(component).toBeTruthy();
});

it("should load the children for a school", fakeAsync(() => {
it("should load the relations for a school", fakeAsync(() => {
const school = new School("s1");
const child1 = new Child("c1");
const child2 = new Child("c2");
const relation1 = new ChildSchoolRelation("r1");
const relation2 = new ChildSchoolRelation("r2");
const config = { entity: school };
mockSchoolsService.getChildrenForSchool.and.resolveTo([child1, child2]);
mockChildrenService.queryRelationsOf.and.resolveTo([relation1, relation2]);

component.onInitFromDynamicConfig(config);
tick();

expect(mockSchoolsService.getChildrenForSchool).toHaveBeenCalledWith(
expect(mockChildrenService.queryRelationsOf).toHaveBeenCalledWith(
"school",
school.getId()
);
tick();
expect(component.children).toEqual([child1, child2]);
expect(component.records).toEqual([relation1, relation2]);
}));

it("should route to a child when clicked", () => {
Expand All @@ -83,89 +81,12 @@ describe("ChildrenOverviewComponent", () => {
]);
});

it("should open a dialog when clicking add new child with correct relation", () => {
component.entity = new School();
const dialog = TestBed.inject(MatDialog);
const entityFormService = TestBed.inject(EntityFormService);
const alertService = TestBed.inject(AlertService);
const dialogComponent = new EntityFormComponent(
entityFormService,
alertService
);
spyOn(dialog, "open").and.returnValues({
componentInstance: dialogComponent,
} as any);
it("should create a relation with the school ID already been set", () => {
component.entity = new School("testID");

component.addChildClick();
const newRelation = component.generateNewRecordFactory()();

expect(dialog.open).toHaveBeenCalled();
const relation = dialogComponent.entity as ChildSchoolRelation;
expect(relation.schoolId).toBe(component.entity.getId());
expect(newRelation).toBeInstanceOf(ChildSchoolRelation);
expect(newRelation.schoolId).toBe("testID");
});

it("should add a newly added child to the list", fakeAsync(() => {
component.entity = new School();
const child = new Child();
const dialog = TestBed.inject(MatDialog);
const dialogComponent = {
onSave: new EventEmitter(),
onCancel: new EventEmitter(),
};
spyOn(dialog, "open").and.returnValues({
componentInstance: dialogComponent,
close: () => {},
} as any);
mockSchoolsService.getChildrenForSchool.and.resolveTo([child]);

component.addChildClick();
dialogComponent.onSave.emit(undefined);
tick();

expect(component.children).toContain(child);
}));

it("should close the dialog when cancel is clicked", fakeAsync(() => {
component.entity = new School();
const dialog = TestBed.inject(MatDialog);
const dialogComponent = {
onSave: new EventEmitter(),
onCancel: new EventEmitter(),
};
const closeSpy = jasmine.createSpy();
spyOn(dialog, "open").and.returnValues({
componentInstance: dialogComponent,
close: closeSpy,
} as any);

component.addChildClick();
dialogComponent.onCancel.emit(undefined);
tick();

expect(closeSpy).toHaveBeenCalled();
}));

it("should assign the popup columns from the config", fakeAsync(() => {
const dialog = TestBed.inject(MatDialog);
const dialogComponent = {
columns: [],
onSave: new EventEmitter(),
onCancel: new EventEmitter(),
};
spyOn(dialog, "open").and.returnValues({
componentInstance: dialogComponent,
close: () => {},
} as any);
const popupColumns = ["start", "end", "class"];
const config: PanelConfig = {
entity: new Child(),
config: { popupColumns: popupColumns },
};

component.onInitFromDynamicConfig(config);
tick();
component.addChildClick();
tick();

expect(dialogComponent.columns).toEqual(popupColumns.map((col) => [col]));
}));
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { Component } from "@angular/core";
import { OnInitDynamicComponent } from "../../../core/view/dynamic-components/on-init-dynamic-component.interface";
import { SchoolsService } from "../schools.service";
import { Child } from "../../children/model/child";
import { PanelConfig } from "../../../core/entity-components/entity-details/EntityDetailsConfig";
import { FormFieldConfig } from "../../../core/entity-components/entity-form/entity-form/FormConfig";
import { Router } from "@angular/router";
import { MatDialog } from "@angular/material/dialog";
import { EntityFormComponent } from "../../../core/entity-components/entity-form/entity-form/entity-form.component";
import { ChildSchoolRelation } from "../../children/model/childSchoolRelation";
import { Entity } from "../../../core/entity/model/entity";
import { ChildrenService } from "../../children/children.service";

/**
* This component creates a table containing all children currently attending this school.
Expand All @@ -19,47 +17,29 @@ import { Entity } from "../../../core/entity/model/entity";
styleUrls: ["./children-overview.component.scss"],
})
export class ChildrenOverviewComponent implements OnInitDynamicComponent {
readonly addButtonLabel = ChildSchoolRelation.schema.get("childId").label;

columns: FormFieldConfig[] = [
{ id: "projectNumber" },
{ id: "name" },
{
id: "schoolClass",
label: $localize`:The school-class of a child:Class`,
view: "DisplayText",
},
{
id: "age",
label: $localize`:The age of a child:Age`,
view: "DisplayText",
},
];

private popupColumns: (string | FormFieldConfig)[] = [
"childId",
"start",
"end",
{ id: "childId" },
{ id: "schoolClass" },
{ id: "start" },
{ id: "end" },
{ id: "result" },
];

children: Child[] = [];
entity: Entity;
records: ChildSchoolRelation[] = [];

constructor(
private schoolsService: SchoolsService,
private router: Router,
private dialog: MatDialog
private childrenService: ChildrenService,
private router: Router
) {}

async onInitFromDynamicConfig(config: PanelConfig) {
if (config?.config?.columns) {
this.columns = config.config.columns;
}
if (config?.config?.popupColumns?.length > 0) {
this.popupColumns = config.config.popupColumns;
}
this.entity = config.entity;
this.children = await this.schoolsService.getChildrenForSchool(
this.records = await this.childrenService.queryRelationsOf(
"school",
this.entity.getId()
);
}
Expand All @@ -68,23 +48,11 @@ export class ChildrenOverviewComponent implements OnInitDynamicComponent {
this.router.navigate([`/${child.getType().toLowerCase()}`, child.getId()]);
}

addChildClick() {
const dialogRef = this.dialog.open(EntityFormComponent, {
width: "80%",
maxHeight: "90vh",
});

dialogRef.componentInstance.columns = this.popupColumns.map((col) => [col]);
const newRelation = new ChildSchoolRelation();
newRelation.schoolId = this.entity.getId();
dialogRef.componentInstance.entity = newRelation;
dialogRef.componentInstance.editing = true;
dialogRef.componentInstance.onSave.subscribe(async () => {
dialogRef.close();
this.children = await this.schoolsService.getChildrenForSchool(
this.entity.getId()
);
});
dialogRef.componentInstance.onCancel.subscribe(() => dialogRef.close());
generateNewRecordFactory(): () => ChildSchoolRelation {
return () => {
const newRelation = new ChildSchoolRelation();
newRelation.schoolId = this.entity.getId();
return newRelation;
};
}
}
3 changes: 1 addition & 2 deletions src/app/child-dev-project/schools/schools.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { MatPaginatorModule } from "@angular/material/paginator";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatSelectModule } from "@angular/material/select";
import { RouterModule } from "@angular/router";
import { SchoolsService } from "./schools.service";
import { MatTooltipModule } from "@angular/material/tooltip";
import { Angulartics2Module } from "angulartics2";
import { ChildrenOverviewComponent } from "./children-overview/children-overview.component";
Expand Down Expand Up @@ -77,7 +76,7 @@ import { EntitySubrecordModule } from "../../core/entity-components/entity-subre
ChildrenOverviewComponent,
],
exports: [SchoolBlockComponent],
providers: [SchoolsService, DatePipe],
providers: [DatePipe],
entryComponents: [SchoolBlockComponent],
})
export class SchoolsModule {}
38 changes: 0 additions & 38 deletions src/app/child-dev-project/schools/schools.service.ts

This file was deleted.

Loading

0 comments on commit 590c145

Please sign in to comment.