diff --git a/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.html b/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.html index 289052eca4..727ac3e31f 100644 --- a/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.html +++ b/src/app/child-dev-project/children/educational-material/educational-material-component/educational-material.component.html @@ -5,9 +5,17 @@ > -
- Total: {{ summary }} +
+ + + + Total: {{ summary }}
+
+
+ + + Average: {{ avgSummary }} + + +
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 2e8e639b56..a789f671ba 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 @@ -6,8 +6,10 @@ import { MockedTestingModule } from "../../../../utils/mocked-testing.module"; import { EducationalMaterial } from "../model/educational-material"; import { ConfigurableEnumValue } from "../../../../core/basic-datatypes/configurable-enum/configurable-enum.interface"; import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service"; -import { Subject } from "rxjs"; +import { of, Subject } from "rxjs"; import { UpdatedEntity } from "../../../../core/entity/model/entity-update"; +import { ActivatedRoute } from "@angular/router"; +import { RouteData } from "app/core/config/dynamic-routing/view-config.interface"; describe("EducationalMaterialComponent", () => { let component: EducationalMaterialComponent; @@ -26,7 +28,33 @@ describe("EducationalMaterialComponent", () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [EducationalMaterialComponent, MockedTestingModule.withState()], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: of({ + config: { + entity: "Child", + panels: [ + { + title: "Educational Materials", + summary: [ + { + Total: "Total", + }, + { + Average: "Average", + }, + ], + }, + ], + }, + }), + }, + }, + ], }).compileComponents(); + const entityMapper = TestBed.inject(EntityMapperService); spyOn(entityMapper, "receiveUpdates").and.returnValue(updates); })); @@ -95,6 +123,36 @@ describe("EducationalMaterialComponent", () => { const newRecord = component.newRecordFactory(); expect(newRecord.child).toBe(child.getId()); }); + it("produces an empty summary when there are no records", () => { + component.records = []; + component.updateSummary(); + expect(component.summary).toHaveSize(0); + }); + + it("should set summaryTitle based on panel title", () => { + setRecordsAndGenerateSummary([{ Total: "Total" }, { Average: "Average" }]) + + const routeData: RouteData = { + config: { + panels: [ + { + title: "Educational Materials", + summary: [ + { + Total: "Total", + }, + { + Average: "Average", + }, + ], + }, + ], + }, + }; + + component.getSummaryList(); + expect(component.summaryTitle).toEqual([{ Total: "Total" }, { Average: "Average" }]); + }); it("should update the summary when entity updates are received", async () => { const update1 = EducationalMaterial.create({ 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 feba170e3e..953a012890 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,4 +1,5 @@ import { Component, Input, OnInit } from "@angular/core"; +import { NgFor, NgIf, } from "@angular/common"; import { EducationalMaterial } from "../model/educational-material"; import { Child } from "../../model/child"; import { FormFieldConfig } from "../../../../core/common-components/entity-form/entity-form/FormConfig"; @@ -8,6 +9,9 @@ import { applyUpdate } from "../../../../core/entity/model/entity-update"; import { filter } from "rxjs/operators"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; import { EntitySubrecordComponent } from "../../../../core/common-components/entity-subrecord/entity-subrecord/entity-subrecord.component"; +import { EntityListConfig } from "app/core/entity-list/EntityListConfig"; +import { ActivatedRoute } from "@angular/router"; +import { RouteData } from "app/core/config/dynamic-routing/view-config.interface"; /** * Displays educational materials of a child, such as a pencil, rulers, e.t.c @@ -18,13 +22,16 @@ import { EntitySubrecordComponent } from "../../../../core/common-components/ent @Component({ selector: "app-educational-material", templateUrl: "./educational-material.component.html", - imports: [EntitySubrecordComponent], + imports: [EntitySubrecordComponent,NgIf,NgFor], standalone: true, }) export class EducationalMaterialComponent implements OnInit { @Input() entity: Child; records: EducationalMaterial[] = []; summary = ""; + avgSummary = ""; + summaryTitle: { [key: string]: string }[] + listConfig: EntityListConfig; @Input() config: { columns: FormFieldConfig[] } = { columns: [ @@ -35,7 +42,8 @@ export class EducationalMaterialComponent implements OnInit { ], }; - constructor(private entityMapper: EntityMapperService) { + constructor(private entityMapper: EntityMapperService, + private route: ActivatedRoute ) { this.entityMapper .receiveUpdates(EducationalMaterial) .pipe( @@ -65,6 +73,7 @@ export class EducationalMaterialComponent implements OnInit { (mat) => mat.child === this.entity.getId(), ); this.updateSummary(); + } newRecordFactory = () => { @@ -83,15 +92,49 @@ export class EducationalMaterialComponent implements OnInit { * human-readable format */ updateSummary() { - const summary = new Map(); - this.records.forEach((m) => { - const previousValue = summary.has(m.materialType.label) - ? summary.get(m.materialType.label) - : 0; - summary.set(m.materialType.label, previousValue + m.materialAmount); - }); - this.summary = [...summary] - .map(([key, value]) => key + ": " + value) - .join(", "); + const summary = new Map(); + const average = new Map(); + + // Initialize summary and average maps in a single loop + for (const m of this.records) { + if (m.materialType) { + const label = m.materialType.label; + const amount = m.materialAmount; + + if (!summary.has(label)) { + summary.set(label, { count: 0, sum: 0 }); + } + + const labelData = summary.get(label); + labelData.count++; + labelData.sum += amount; + } + } + + // Calculate averages and build summary strings + const summaryArray: string[] = []; + const avgSummaryArray: string[] = []; + + for (const [label, labelData] of summary.entries()) { + const avg = labelData.sum / labelData.count; + average.set(label, avg); + + summaryArray.push(`${label}: ${labelData.sum}`); + avgSummaryArray.push(`${label}: ${avg}`); + } + + this.summary = summaryArray.join(", "); + this.avgSummary = avgSummaryArray.join(", "); + this.getSummaryList(); + } + + + getSummaryList(){ + this.route.data.subscribe( + (data: RouteData) => (this.listConfig = data.config), + ); + this.summaryTitle = this.listConfig['panels'] + .find((panel: { title: string })=> panel.title === "Educational Materials").summary; + } } diff --git a/src/app/core/config/config-fix.ts b/src/app/core/config/config-fix.ts index b179821a6d..0e03c98188 100644 --- a/src/app/core/config/config-fix.ts +++ b/src/app/core/config/config-fix.ts @@ -714,6 +714,15 @@ export const defaultJsonConfig = { "title": "", "component": "EducationalMaterial" } + ], + "summary": [ + { + "Total": $localize `:Total item Count:Total` + }, + + { + "Average": $localize `:Average Per Item:Average` + } ] }, {