diff --git a/src/app/child-dev-project/children/children.service.ts b/src/app/child-dev-project/children/children.service.ts index 6773f200ad..0325555dbd 100644 --- a/src/app/child-dev-project/children/children.service.ts +++ b/src/app/child-dev-project/children/children.service.ts @@ -3,7 +3,6 @@ import { from, Observable, Subject } from "rxjs"; import { Child } from "./model/child"; import { EntityMapperService } from "../../core/entity/entity-mapper.service"; import { Note } from "../notes/model/note"; -import { EducationalMaterial } from "./educational-material/model/educational-material"; import { Aser } from "./aser/model/aser"; import { ChildSchoolRelation } from "./model/childSchoolRelation"; import { HealthCheck } from "./health-checkup/model/health-check"; @@ -240,13 +239,6 @@ export class ChildrenService { return this.dbIndexing.createIndex(designDoc); } - async getEducationalMaterialsOfChild( - childId: string - ): Promise { - const allMaterials = await this.entityMapper.loadType(EducationalMaterial); - return allMaterials.filter((mat) => mat.child === childId); - } - /** * * @param childId should be set in the specific components and is passed by the URL as a parameter diff --git a/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.spec.ts b/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.spec.ts index 3ad24cc18c..dd3514fa16 100644 --- a/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.spec.ts +++ b/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.spec.ts @@ -1,17 +1,19 @@ import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { EducationalMaterialComponent } from "./educational-material.component"; -import { ChildrenService } from "../../children.service"; import { Child } from "../../model/child"; import { ChildrenModule } from "../../children.module"; import { MockedTestingModule } from "../../../../utils/mocked-testing.module"; import { EducationalMaterial } from "../model/educational-material"; import { ConfigurableEnumValue } from "../../../../core/configurable-enum/configurable-enum.interface"; +import { EntityMapperService } from "../../../../core/entity/entity-mapper.service"; +import { Subject } from "rxjs"; +import { UpdatedEntity } from "../../../../core/entity/model/entity-update"; describe("EducationalMaterialComponent", () => { let component: EducationalMaterialComponent; let fixture: ComponentFixture; - let mockChildrenService: jasmine.SpyObj; + const updates = new Subject>(); const child = new Child("22"); const PENCIL: ConfigurableEnumValue = { id: "pencil", @@ -24,15 +26,11 @@ describe("EducationalMaterialComponent", () => { beforeEach( waitForAsync(() => { - mockChildrenService = jasmine.createSpyObj([ - "getEducationalMaterialsOfChild", - ]); TestBed.configureTestingModule({ imports: [ChildrenModule, MockedTestingModule.withState()], - providers: [ - { provide: ChildrenService, useValue: mockChildrenService }, - ], }).compileComponents(); + const entityMapper = TestBed.inject(EntityMapperService); + spyOn(entityMapper, "receiveUpdates").and.returnValue(updates); }) ); @@ -84,10 +82,10 @@ describe("EducationalMaterialComponent", () => { it("loads all education data associated with a child and updates the summary", async () => { const educationalData = [ - { materialType: PENCIL, materialAmount: 1 }, - { materialType: RULER, materialAmount: 2 }, + { materialType: PENCIL, materialAmount: 1, child: child.getId() }, + { materialType: RULER, materialAmount: 2, child: child.getId() }, ].map(EducationalMaterial.create); - mockChildrenService.getEducationalMaterialsOfChild.and.resolveTo( + spyOn(TestBed.inject(EntityMapperService), "loadType").and.resolveTo( educationalData ); await component.loadData("22"); @@ -96,8 +94,33 @@ describe("EducationalMaterialComponent", () => { }); it("associates a new record with the current child", () => { - component.child = child; const newRecord = component.newRecordFactory(); expect(newRecord.child).toBe(child.getId()); }); + + it("should update the summary when entity updates are received", async () => { + const update1 = EducationalMaterial.create({ + child: child.getId(), + materialType: PENCIL, + materialAmount: 1, + }); + updates.next({ entity: update1, type: "new" }); + + expect(component.records).toEqual([update1]); + expect(component.summary).toBe(`${PENCIL.label}: 1`); + + const update2 = update1.copy() as EducationalMaterial; + update2.materialAmount = 2; + updates.next({ entity: update2, type: "update" }); + + expect(component.records).toEqual([update2]); + expect(component.summary).toBe(`${PENCIL.label}: 2`); + + const unrelatedUpdate = update1.copy() as EducationalMaterial; + unrelatedUpdate.child = "differentChild"; + updates.next({ entity: unrelatedUpdate, type: "new" }); + // No change + expect(component.records).toEqual([update2]); + expect(component.summary).toBe(`${PENCIL.label}: 2`); + }); }); diff --git a/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.ts b/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.ts index a5b35d02e2..34bbd67d90 100644 --- a/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.ts +++ b/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.ts @@ -1,17 +1,21 @@ import { Component, Input, OnChanges, SimpleChanges } from "@angular/core"; import { EducationalMaterial } from "../model/educational-material"; -import { ChildrenService } from "../../children.service"; import { Child } from "../../model/child"; import { OnInitDynamicComponent } from "../../../../core/view/dynamic-components/on-init-dynamic-component.interface"; import { PanelConfig } from "../../../../core/entity-components/entity-details/EntityDetailsConfig"; import { FormFieldConfig } from "../../../../core/entity-components/entity-form/entity-form/FormConfig"; import { DynamicComponent } from "../../../../core/view/dynamic-components/dynamic-component.decorator"; +import { EntityMapperService } from "../../../../core/entity/entity-mapper.service"; +import { applyUpdate } from "../../../../core/entity/model/entity-update"; +import { filter } from "rxjs/operators"; +import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; /** * Displays educational materials of a child, such as a pencil, rulers, e.t.c * as well as a summary */ @DynamicComponent("EducationalMaterial") +@UntilDestroy() @Component({ selector: "app-educational-material", templateUrl: "./educational-material.component.html", @@ -29,7 +33,21 @@ export class EducationalMaterialComponent { id: "description", visibleFrom: "md" }, ]; - constructor(private childrenService: ChildrenService) {} + constructor(private entityMapper: EntityMapperService) { + this.entityMapper + .receiveUpdates(EducationalMaterial) + .pipe( + untilDestroyed(this), + filter( + ({ entity, type }) => + type === "remove" || entity.child === this.child.getId() + ) + ) + .subscribe((update) => { + this.records = applyUpdate(this.records, update); + this.updateSummary(); + }); + } async ngOnChanges(changes: SimpleChanges): Promise { if (changes.hasOwnProperty("child")) { @@ -51,9 +69,8 @@ export class EducationalMaterialComponent * @param id The id of the child to load the data for */ async loadData(id: string) { - this.records = await this.childrenService.getEducationalMaterialsOfChild( - id - ); + const allMaterials = await this.entityMapper.loadType(EducationalMaterial); + this.records = allMaterials.filter((mat) => mat.child === id); this.updateSummary(); } diff --git a/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.spec.ts b/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.spec.ts index 059f922859..c07daa3d1d 100644 --- a/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.spec.ts +++ b/src/app/child-dev-project/children/health-checkup/health-checkup-component/health-checkup.component.spec.ts @@ -17,11 +17,9 @@ describe("HealthCheckupComponent", () => { waitForAsync(() => { mockChildrenService = jasmine.createSpyObj([ "getChild", - "getEducationalMaterialsOfChild", "getHealthChecksOfChild", ]); mockChildrenService.getChild.and.returnValue(of(child)); - mockChildrenService.getEducationalMaterialsOfChild.and.resolveTo([]); mockChildrenService.getHealthChecksOfChild.and.returnValue(of([])); TestBed.configureTestingModule({ diff --git a/src/app/core/entity/model/entity-update.ts b/src/app/core/entity/model/entity-update.ts index dff20e24af..e4ae7b1570 100644 --- a/src/app/core/entity/model/entity-update.ts +++ b/src/app/core/entity/model/entity-update.ts @@ -33,7 +33,7 @@ export interface UpdatedEntity { export function applyUpdate( entities: T[], next: UpdatedEntity -) { +): T[] { if (!next || !next.entity) { return entities; }