Skip to content

Commit

Permalink
fix: attendance overview shows total attendance summary and calender …
Browse files Browse the repository at this point in the history
…also shows previous months

fixes: #1149, #1146

Co-authored-by: Sebastian Leidig <sebastian.leidig@gmail.com>
  • Loading branch information
TheSlimvReal and sleidig authored Apr 7, 2022
1 parent 0904c9d commit 408754b
Show file tree
Hide file tree
Showing 21 changed files with 842 additions and 269 deletions.
14 changes: 6 additions & 8 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials"
]
}
// See {@link https://github.com/storybookjs/storybook/issues/17004#issuecomment-993210351}
{ name: "@storybook/addon-essentials", options: { docs: false } },
],
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
<app-attendance-calendar
[records]="displayedEvents"
[highlightForChild]="forChild"
[activity]="activity"
>
</app-attendance-calendar>
<div *ngIf="loading">
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>

<div fxLayout="row wrap" *ngIf="!loading">
<app-attendance-calendar
[records]="combinedAttendance?.events || []"
[highlightForChild]="forChild"
[activity]="activity"
fxFlex
></app-attendance-calendar>
<app-attendance-summary
[attendance]="combinedAttendance"
[forChild]="forChild"
[columns]="columns"
style="margin-top: 1em;"
fxFlex
></app-attendance-summary>
</div>

<app-entity-subrecord
[records]="records"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { AttendanceLogicalStatus } from "../model/attendance-status";
import { AttendanceModule } from "../attendance.module";
import { MatNativeDateModule } from "@angular/material/core";
import { MockSessionModule } from "../../../core/session/mock-session.module";
import moment from "moment";

describe("ActivityAttendanceSectionComponent", () => {
let component: ActivityAttendanceSectionComponent;
Expand Down Expand Up @@ -104,4 +105,48 @@ describe("ActivityAttendanceSectionComponent", () => {
component.updateDisplayedRecords(true);
expect(component.records).toEqual(component.allRecords);
});

it("should combine all activity attendances to have an all-time overview", async () => {
const oldestEvent = EventNote.create(
moment().subtract(2, "months").toDate()
);
const someEvent1 = EventNote.create(
moment().subtract(1, "months").toDate()
);
const someEvent2 = EventNote.create(
moment().subtract(1, "months").toDate()
);
const latestEvent = EventNote.create(new Date());
const oldestAttendance = ActivityAttendance.create(oldestEvent.date, [
oldestEvent,
]);
oldestAttendance.periodTo = oldestEvent.date;
const middleAttendance = ActivityAttendance.create(someEvent1.date, [
someEvent1,
someEvent2,
]);
middleAttendance.periodTo = someEvent2.date;
const latestAttendance = ActivityAttendance.create(latestEvent.date, [
latestEvent,
]);
latestAttendance.periodTo = latestEvent.date;
mockAttendanceService.getActivityAttendances.and.resolveTo([
oldestAttendance,
middleAttendance,
latestAttendance,
]);

await component.init();

expect(component.combinedAttendance.periodFrom).toBe(oldestEvent.date);
expect(component.combinedAttendance.periodTo).toBe(latestEvent.date);
expect(component.combinedAttendance.events).toEqual(
jasmine.arrayWithExactContents([
oldestEvent,
someEvent1,
someEvent2,
latestEvent,
])
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { AttendanceDetailsComponent } from "../attendance-details/attendance-det
import { AttendanceService } from "../attendance.service";
import { PercentPipe } from "@angular/common";
import { ActivityAttendance } from "../model/activity-attendance";
import { Note } from "../../notes/model/note";
import moment from "moment";
import { FormFieldConfig } from "../../../core/entity-components/entity-form/entity-form/FormConfig";
import { FormDialogService } from "../../../core/form-dialog/form-dialog.service";
Expand All @@ -22,9 +21,10 @@ export class ActivityAttendanceSectionComponent
@Input() activity: RecurringActivity;
@Input() forChild?: string;

loading: boolean = true;
records: ActivityAttendance[] = [];
allRecords: ActivityAttendance[] = [];
displayedEvents: Note[] = [];
combinedAttendance: ActivityAttendance;

columns: FormFieldConfig[] = [
{
Expand All @@ -40,7 +40,7 @@ export class ActivityAttendanceSectionComponent
additional: (e: ActivityAttendance) =>
this.forChild
? e.countEventsPresent(this.forChild)
: e.countEventsPresentAverage(true),
: e.countTotalPresent(),
},
{
id: "totalEvents",
Expand Down Expand Up @@ -83,6 +83,7 @@ export class ActivityAttendanceSectionComponent
}

async init(loadAll: boolean = false) {
this.loading = true;
if (loadAll) {
this.allRecords = await this.attendanceService.getActivityAttendances(
this.activity
Expand All @@ -94,6 +95,32 @@ export class ActivityAttendanceSectionComponent
);
}
this.updateDisplayedRecords(false);
this.createCombinedAttendance();
this.loading = false;
}

private createCombinedAttendance() {
this.combinedAttendance = new ActivityAttendance();
this.combinedAttendance.activity = this.activity;
this.allRecords.forEach((record) => {
this.combinedAttendance.events.push(...record.events);
if (
!this.combinedAttendance.periodFrom ||
moment(record.periodFrom).isBefore(
this.combinedAttendance.periodFrom,
"day"
)
) {
this.combinedAttendance.periodFrom = record.periodFrom;
}

if (
!this.combinedAttendance.periodTo ||
moment(record.periodTo).isAfter(this.combinedAttendance.periodTo, "day")
) {
this.combinedAttendance.periodTo = record.periodTo;
}
});
}

updateDisplayedRecords(includeRecordsWithoutParticipation: boolean) {
Expand All @@ -112,7 +139,6 @@ export class ActivityAttendanceSectionComponent
this.records.sort(
(a, b) => b.periodFrom.getTime() - a.periodFrom.getTime()
);
this.displayedEvents = this.records[0].events;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,64 @@ import {
import { AttendanceLogicalStatus } from "../model/attendance-status";
import { StorybookBaseModule } from "../../../utils/storybook-base.module";
import { MockSessionModule } from "../../../core/session/mock-session.module";
import { AttendanceService } from "../attendance.service";
import { ChildrenService } from "../../children/children.service";
import { of } from "rxjs";
import { Child } from "../../children/model/child";
import moment from "moment";

const demoActivity = RecurringActivity.create("Coaching Batch C");
const attendanceRecords = [
ActivityAttendance.create(new Date("2020-01-01"), [
ActivityAttendance.create(
moment().subtract(1, "month").startOf("month").toDate(),
[
generateEventWithAttendance([
["1", AttendanceLogicalStatus.ABSENT],
["2", AttendanceLogicalStatus.ABSENT],
]),
generateEventWithAttendance([
["1", AttendanceLogicalStatus.PRESENT],
["2", AttendanceLogicalStatus.ABSENT],
]),
]
),

ActivityAttendance.create(moment().startOf("month").toDate(), [
generateEventWithAttendance(
[
["1", AttendanceLogicalStatus.PRESENT],
["2", AttendanceLogicalStatus.PRESENT],
["3", AttendanceLogicalStatus.ABSENT],
],
new Date("2020-01-01")
moment().subtract(5, "days").toDate()
),
generateEventWithAttendance(
[
["1", AttendanceLogicalStatus.PRESENT],
["2", AttendanceLogicalStatus.ABSENT],
],
new Date("2020-01-02")
moment().subtract(4, "days").toDate()
),
generateEventWithAttendance(
[
["1", AttendanceLogicalStatus.ABSENT],
["2", AttendanceLogicalStatus.ABSENT],
],
new Date("2020-01-03")
moment().subtract(3, "days").toDate()
),
generateEventWithAttendance(
[
["1", AttendanceLogicalStatus.PRESENT],
["2", AttendanceLogicalStatus.ABSENT],
],
new Date("2020-01-04")
moment().subtract(2, "days").toDate()
),
]),

ActivityAttendance.create(new Date("2020-02-01"), [
generateEventWithAttendance([
["1", AttendanceLogicalStatus.ABSENT],
["2", AttendanceLogicalStatus.ABSENT],
]),
generateEventWithAttendance([
["1", AttendanceLogicalStatus.PRESENT],
["2", AttendanceLogicalStatus.ABSENT],
]),
]),
];
attendanceRecords.forEach((a) => (a.activity = demoActivity));
attendanceRecords.forEach((a) => {
a.activity = demoActivity;
a.periodTo = moment(a.periodFrom).endOf("month").toDate();
});

export default {
title: "Attendance/Sections/ActivityAttendanceSection",
Expand All @@ -68,6 +79,18 @@ export default {
StorybookBaseModule,
MockSessionModule.withState(),
],
providers: [
{
provide: AttendanceService,
useValue: {
getActivityAttendances: () => Promise.resolve(attendanceRecords),
},
},
{
provide: ChildrenService,
useValue: { getChild: () => of(Child.create("John Doe")) },
},
],
}),
],
} as Meta;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,15 @@ <h1>
</div>
</div>

<div fxLayout="row wrap" fxLayoutGap="20px">
<div fxLayout="row wrap" fxLayoutGap="20px" *ngIf="focusedChild">
<div fxFlex>
<mat-form-field>
<input
matInput
type="number"
min="0"
i18n-placeholder="
days present|How many days a child or a class was present
"
i18n-placeholder="days present|How many days a child was present"
placeholder="days present"
[value]="
focusedChild
? entity.countEventsPresent(focusedChild)
: entity.countEventsPresentAverage(true)
"
[value]="entity.countEventsPresent(focusedChild)"
readonly
/>
</mat-form-field>
Expand All @@ -66,16 +59,9 @@ <h1>
<input
matInput
type="number"
min="0"
i18n-placeholder="
days absent|How many days a child or a class was absent
"
i18n-placeholder="days absent|How many days a child was absent"
placeholder="days absent"
[value]="
focusedChild
? entity.countEventsAbsent(focusedChild)
: entity.countEventsAbsentAverage(true)
"
[value]="entity.countEventsAbsent(focusedChild)"
readonly
/>
</mat-form-field>
Expand All @@ -86,20 +72,50 @@ <h1>
<input
matInput
type="number"
min="0"
i18n-placeholder="
days absent|How many days the presence or absence of a child is
unknown
"
i18n-placeholder="days absent|How many days the presence or absence of a child is unknown"
placeholder="unknown status"
[value]="
focusedChild
? entity.countEventsWithStatusForChild(
UnknownStatus,
focusedChild
)
: entity.countEventsWithUnknownStatus()
"
[value]="entity.countEventsWithUnknownStatus(focusedChild)"
readonly
/>
</mat-form-field>
</div>
</div>

<div fxLayout="row wrap" fxLayoutGap="20px" *ngIf="!focusedChild">
<div fxFlex>
<mat-form-field>
<input
matInput
type="number"
i18n-placeholder="Total present|How many children were present"
placeholder="Total present"
[value]="entity.countTotalPresent()"
readonly
/>
</mat-form-field>
</div>

<div fxFlex>
<mat-form-field>
<input
matInput
type="number"
i18n-placeholder="Total absent|How many children were absent"
placeholder="Total absent"
[value]="entity.countTotalAbsent()"
readonly
/>
</mat-form-field>
</div>

<div fxFlex>
<mat-form-field>
<input
matInput
type="number"
i18n-placeholder="Total unknown|How many children have an unknown presence or absence status"
placeholder="Total unknown"
[value]="entity.countEventsWithUnknownStatus()"
readonly
/>
</mat-form-field>
Expand Down
Loading

0 comments on commit 408754b

Please sign in to comment.