Skip to content

Commit

Permalink
feat(schools): school list show a column stating the total number of …
Browse files Browse the repository at this point in the history
…participants
  • Loading branch information
tomwwinter committed Jan 5, 2024
1 parent 2f07b74 commit 65c65ca
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<p>
{{ participantRelationsCount() }}
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";

import { DisplayParticipantsCountComponent } from "./display-participants-count.component";
import { ChildrenService } from "../../children/children.service";
import { School } from "../model/school";
import { ChildSchoolRelation } from "../../children/model/childSchoolRelation";

describe("DisplayParticipantsCountComponent", () => {
let component: DisplayParticipantsCountComponent;
let fixture: ComponentFixture<DisplayParticipantsCountComponent>;

let mockChildrenService: jasmine.SpyObj<ChildrenService>;

const childSchoolRelations: ChildSchoolRelation[] = [
new ChildSchoolRelation("r-1"),
new ChildSchoolRelation("r-2"),
new ChildSchoolRelation("r-3"),
];

beforeEach(async () => {
mockChildrenService = jasmine.createSpyObj(["queryActiveRelationsOf"]);
mockChildrenService.queryActiveRelationsOf.and.resolveTo(
childSchoolRelations,
);

await TestBed.configureTestingModule({
imports: [DisplayParticipantsCountComponent],
providers: [{ provide: ChildrenService, useValue: mockChildrenService }],
}).compileComponents();

fixture = TestBed.createComponent(DisplayParticipantsCountComponent);
component = fixture.componentInstance;
component.entity = new School("s-1");
fixture.detectChanges();
});

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

it("should count correct number of active students for school", async () => {
expect(component.participantRelationsCount()).toBeNull();
await component.ngOnChanges();
expect(component.participantRelationsCount()).toBeDefined();
expect(component.participantRelationsCount()).toBe(3);
});

it("should handle empty response from ChildrenService", async () => {
mockChildrenService.queryActiveRelationsOf.and.resolveTo([]);
expect(component.participantRelationsCount()).toBeNull();
await component.ngOnChanges();
expect(component.participantRelationsCount()).toBeDefined();
expect(component.participantRelationsCount()).toBe(0);
});

it("should handle error response from ChildrenService", async () => {
mockChildrenService.queryActiveRelationsOf.and.rejectWith(new Error());
expect(component.participantRelationsCount()).toBeNull();
await component.ngOnChanges();
expect(component.participantRelationsCount()).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
ChangeDetectorRef,
Component,
OnChanges,
signal,
Signal,
WritableSignal,
} from "@angular/core";
import { CommonModule } from "@angular/common";
import { ChildrenService } from "../../children/children.service";
import { ViewDirective } from "../../../core/entity/default-datatype/view.directive";
import { DynamicComponent } from "../../../core/config/dynamic-components/dynamic-component.decorator";
import { ChildSchoolRelation } from "../../children/model/childSchoolRelation";

@DynamicComponent("DisplayParticipantsCount")
@Component({
selector: "app-display-participants-count",
standalone: true,
imports: [CommonModule],
templateUrl: "./display-participants-count.component.html",
})
export class DisplayParticipantsCountComponent
extends ViewDirective<any>
implements OnChanges
{
participantRelationsCount: WritableSignal<number> = signal(null);

constructor(private _childrenService: ChildrenService) {
super();
}

override async ngOnChanges(): Promise<void> {
super.ngOnChanges();

return this._childrenService
.queryActiveRelationsOf("school", this.entity.getId())
.then((relations: ChildSchoolRelation[]) => {
this.participantRelationsCount.set(relations.length);
})
.catch((reason) => {
console.log(
"Could not calculate participantRelationsCount, error response from ChildrenService.",
reason,
);
});
}
}
7 changes: 7 additions & 0 deletions src/app/child-dev-project/schools/schools-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,11 @@ export const schoolsComponents: ComponentTuple[] = [
(c) => c.SchoolBlockComponent,
),
],
[
"DisplayParticipantsCount",
() =>
import(
"./display-participants-count/display-participants-count.component"
).then((c) => c.DisplayParticipantsCountComponent),
],
];
1 change: 1 addition & 0 deletions src/app/core/config/config-fix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ export const defaultJsonConfig = {
"entity": "School",
"columns": [
"name",
{ id: "DisplayParticipantsCount", viewComponent: "DisplayParticipantsCount", label: $localize`Children` },
"privateSchool",
"language"
],
Expand Down
6 changes: 6 additions & 0 deletions src/app/core/entity/default-datatype/view.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export abstract class ViewDirective<T, C = any> implements OnChanges {
/** indicating that the value is not in its original state, so that components can explain this to the user */
isPartiallyAnonymized: boolean;

/**
* Attention:
* When content is loaded async in your child component, you need to manually trigger the change detection
* See: https://angularindepth.com/posts/1054/here-is-what-you-need-to-know-about-dynamic-components-in-angular#ngonchanges
*
*/
ngOnChanges() {
this.isPartiallyAnonymized =
this.entity?.anonymized &&
Expand Down
2 changes: 1 addition & 1 deletion src/app/core/entity/model/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export type EntityConstructor<T extends Entity = Entity> = (new (
*
* Entity classes do not deal with database actions, use {@link EntityMapperService} with its find/save/delete functions.
*
* Do not use the Entity class directly. Instead implement your own Entity types, writing classes that extend "Entity".
* Do not use the Entity class directly. Instead, implement your own Entity types, writing classes that extend "Entity".
* A How-To Guide on how to implement your own types is available:
* - [How to Create a new Entity Type]{@link /additional-documentation/how-to-guides/create-a-new-entity-type.html}
*/
Expand Down

0 comments on commit 65c65ca

Please sign in to comment.