diff --git a/src/app/child-dev-project/schools/activities-overview/activities-overview.component.html b/src/app/child-dev-project/schools/activities-overview/activities-overview.component.html
new file mode 100644
index 0000000000..6d80f2895a
--- /dev/null
+++ b/src/app/child-dev-project/schools/activities-overview/activities-overview.component.html
@@ -0,0 +1,7 @@
+
diff --git a/src/app/child-dev-project/schools/activities-overview/activities-overview.component.scss b/src/app/child-dev-project/schools/activities-overview/activities-overview.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/app/child-dev-project/schools/activities-overview/activities-overview.component.spec.ts b/src/app/child-dev-project/schools/activities-overview/activities-overview.component.spec.ts
new file mode 100644
index 0000000000..252b79e2f3
--- /dev/null
+++ b/src/app/child-dev-project/schools/activities-overview/activities-overview.component.spec.ts
@@ -0,0 +1,74 @@
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { RecurringActivity } from "app/child-dev-project/attendance/model/recurring-activity";
+import { EntityMapperService } from "app/core/entity/entity-mapper.service";
+import {
+ mockEntityMapper,
+ MockEntityMapperService,
+} from "app/core/entity/mock-entity-mapper-service";
+import { UpdatedEntity } from "app/core/entity/model/entity-update";
+import { Subject } from "rxjs";
+import { School } from "../model/school";
+
+import { ActivitiesOverviewComponent } from "./activities-overview.component";
+
+describe("ActivitiesOverviewComponent", () => {
+ let component: ActivitiesOverviewComponent;
+ let fixture: ComponentFixture;
+
+ let entityMapper: MockEntityMapperService;
+
+ beforeEach(async () => {
+ entityMapper = mockEntityMapper();
+ await TestBed.configureTestingModule({
+ declarations: [ActivitiesOverviewComponent],
+ providers: [{ provide: EntityMapperService, useValue: entityMapper }],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ActivitiesOverviewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it("should create", () => {
+ expect(component).toBeTruthy();
+ });
+
+ it("should fetch all and only recurring activities having the selected school as a linkedGroup", async () => {
+ const school1 = new School("school1");
+ const activity1 = new RecurringActivity();
+ activity1.linkedGroups = ["school1"];
+ const activity2 = new RecurringActivity();
+ activity2.linkedGroups = ["school1", "school2"];
+ const activity3 = new RecurringActivity();
+ activity3.linkedGroups = ["school3"];
+ entityMapper.addAll([activity1, activity2, activity3]);
+
+ await component.onInitFromDynamicConfig({ entity: school1 });
+ expect(component.records).toEqual([activity1, activity2]);
+ });
+
+ it("should create a new recurring activity having the current school as a linkedGroup", () => {
+ component.entity = new School("school1");
+ const newRecurringActivity = component.generateNewRecordFactory();
+ expect(newRecurringActivity().linkedGroups).toEqual(["school1"]);
+ });
+
+ it("should remove the recurring activity from the table view if the current school is removed as a group of this recurring activity", async () => {
+ const school1 = new School("school1");
+ const activity1 = new RecurringActivity();
+ activity1.linkedGroups = ["school1"];
+ const activity2 = new RecurringActivity();
+ activity1.linkedGroups = ["school1", "school2", "school3"];
+ entityMapper.addAll([activity1, activity2]);
+ const subject = new Subject>();
+ spyOn(entityMapper, "receiveUpdates").and.returnValue(subject);
+ await component.onInitFromDynamicConfig({ entity: school1 });
+
+ activity2.linkedGroups = ["school2", "school3"];
+ subject.next({ entity: activity2, type: "update" });
+
+ expect(component.records).toEqual([activity1]);
+ });
+});
diff --git a/src/app/child-dev-project/schools/activities-overview/activities-overview.component.ts b/src/app/child-dev-project/schools/activities-overview/activities-overview.component.ts
new file mode 100644
index 0000000000..5f11e79e02
--- /dev/null
+++ b/src/app/child-dev-project/schools/activities-overview/activities-overview.component.ts
@@ -0,0 +1,58 @@
+import { Component } from "@angular/core";
+import { RecurringActivity } from "app/child-dev-project/attendance/model/recurring-activity";
+import { FormFieldConfig } from "app/core/entity-components/entity-form/entity-form/FormConfig";
+import { EntityListConfig } from "app/core/entity-components/entity-list/EntityListConfig";
+import { EntityMapperService } from "app/core/entity/entity-mapper.service";
+import { Entity } from "app/core/entity/model/entity";
+import { DynamicComponent } from "app/core/view/dynamic-components/dynamic-component.decorator";
+import { OnInitDynamicComponent } from "app/core/view/dynamic-components/on-init-dynamic-component.interface";
+
+@DynamicComponent("ActivitiesOverview")
+@Component({
+ selector: "app-activities-overview",
+ templateUrl: "./activities-overview.component.html",
+ styleUrls: ["./activities-overview.component.scss"],
+})
+export class ActivitiesOverviewComponent implements OnInitDynamicComponent {
+ columns: FormFieldConfig[] = [
+ { id: "title" },
+ { id: "type" },
+ { id: "assignedTo" },
+ { id: "linkedGroups" },
+ ];
+
+ entity: Entity;
+ records: RecurringActivity[] = [];
+ listConfig: EntityListConfig;
+ activityConstructor = RecurringActivity;
+
+ constructor(private entityMapper: EntityMapperService) {}
+
+ async onInitFromDynamicConfig(config: any) {
+ if (config?.config?.columns) {
+ this.columns = config.config.columns;
+ }
+
+ this.entity = config.entity;
+ this.records = (
+ await this.entityMapper.loadType(RecurringActivity)
+ ).filter((activity) => activity.linkedGroups.includes(this.entity.getId()));
+ this.entityMapper
+ .receiveUpdates(RecurringActivity)
+ .subscribe((updateEntity) => {
+ if (updateEntity.type === "update") {
+ this.records = this.records.filter((activity) =>
+ activity.linkedGroups.includes(this.entity.getId())
+ );
+ }
+ });
+ }
+
+ generateNewRecordFactory(): () => RecurringActivity {
+ return () => {
+ const newRecurringActivity = new RecurringActivity();
+ newRecurringActivity.linkedGroups.push(this.entity.getId());
+ return newRecurringActivity;
+ };
+ }
+}
diff --git a/src/app/child-dev-project/schools/schools.module.ts b/src/app/child-dev-project/schools/schools.module.ts
index da9ee0559e..49c2fee77d 100644
--- a/src/app/child-dev-project/schools/schools.module.ts
+++ b/src/app/child-dev-project/schools/schools.module.ts
@@ -27,6 +27,7 @@ import { EntityListModule } from "../../core/entity-components/entity-list/entit
import { EntitySubrecordModule } from "../../core/entity-components/entity-subrecord/entity-subrecord.module";
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { ViewModule } from "../../core/view/view.module";
+import { ActivitiesOverviewComponent } from "./activities-overview/activities-overview.component";
@NgModule({
imports: [
@@ -70,10 +71,18 @@ import { ViewModule } from "../../core/view/view.module";
FontAwesomeModule,
ViewModule,
],
- declarations: [SchoolBlockComponent, ChildrenOverviewComponent],
+ declarations: [
+ SchoolBlockComponent,
+ ChildrenOverviewComponent,
+ ActivitiesOverviewComponent,
+ ],
exports: [SchoolBlockComponent],
providers: [DatePipe],
})
export class SchoolsModule {
- static dynamicComponents = [ChildrenOverviewComponent, SchoolBlockComponent];
+ static dynamicComponents = [
+ ChildrenOverviewComponent,
+ SchoolBlockComponent,
+ ActivitiesOverviewComponent,
+ ];
}
diff --git a/src/app/core/config/config-fix.ts b/src/app/core/config/config-fix.ts
index 84bbf8db6b..a49f6e7004 100644
--- a/src/app/core/config/config-fix.ts
+++ b/src/app/core/config/config-fix.ts
@@ -424,6 +424,15 @@ export const defaultJsonConfig = {
"component": "ChildrenOverview",
}
]
+ },
+ {
+ "title": $localize`:Panel title:Activities`,
+ "components": [
+ {
+ "title": "",
+ "component": "ActivitiesOverview",
+ }
+ ]
}
],
"icon": "university"
diff --git a/src/app/core/registry/dynamic-registry.ts b/src/app/core/registry/dynamic-registry.ts
index cca4683825..528b912589 100644
--- a/src/app/core/registry/dynamic-registry.ts
+++ b/src/app/core/registry/dynamic-registry.ts
@@ -31,8 +31,9 @@ export abstract class Registry extends Map {
public get(key: string): T {
if (!this.has(key)) {
throw Error(
- `${this.constructor.name}: Requested item ${key} is not registered`
+ `${this.constructor.name}: Requested item ${key} is not registered. See dynamic-registry.ts for more details.`
);
+ // To register a component, add @DynamicComponent("COMPONENTNAME") to the components .ts-file and implement the onInitFromDynamicConfig method, e.g. onInitFromDynamicConfig(config: any) {}
}
return super.get(key);
}