Description
Displays detailed attendance data for a calculated attendance time period (ActivityAttendance).
This is often displayed as a dialog for drilling down into details.
Example
|
entity
|
Default value : this.data.attendance
|
|
|
|
forChild
|
Default value : this.data.forChild
|
|
|
Methods
|
showEventDetails
|
showEventDetails(event: Entity)
|
|
|
Parameters :
| Name |
Type |
Optional |
| event |
Entity
|
No
|
|
|
Readonly
dataSource
|
Type : unknown
|
Default value : new InMemoryDataSource()
|
|
|
|
eventEntityType
|
Type : unknown
|
Default value : computed(() =>
this.entity()?.events[0]?.entity?.getConstructor(),
)
|
|
|
|
eventsColumns
|
Type : FormFieldConfig[]
|
Default value : [
{ id: "date" },
{ id: "subject", label: $localize`Event` },
{
id: "getAttendance",
label: $localize`:How a child attended, e.g. too late, in time, excused, e.t.c:Attended`,
viewComponent: "ReadonlyFunction",
additional: (event: Entity) => {
const eventWithAttendance = this.entity().events.find(
(e) => e.entity === event,
);
if (this.forChild()) {
const item = eventWithAttendance?.getAttendanceForParticipant(
this.forChild(),
);
return item?.status?.label || "-";
} else {
const avg = eventWithAttendance?.getAttendanceStats().average;
if (!Number.isFinite(avg)) {
return $localize`N/A`;
}
return `${(avg * 100).toFixed(0)}%`;
}
},
},
]
|
|
|
import {
ChangeDetectionStrategy,
Component,
computed,
effect,
inject,
input,
} from "@angular/core";
import { ActivityAttendance } from "../../model/activity-attendance";
import { Entity } from "#src/app/core/entity/model/entity";
import { FormFieldConfig } from "#src/app/core/common-components/entity-form/FormConfig";
import { FormDialogService } from "#src/app/core/form-dialog/form-dialog.service";
import { DialogCloseComponent } from "#src/app/core/common-components/dialog-close/dialog-close.component";
import { MAT_DIALOG_DATA, MatDialogModule } from "@angular/material/dialog";
import { MatFormFieldModule } from "@angular/material/form-field";
import { PercentPipe } from "@angular/common";
import { CustomDatePipe } from "#src/app/core/basic-datatypes/date/custom-date.pipe";
import { FormsModule } from "@angular/forms";
import { MatInputModule } from "@angular/material/input";
import { AttendanceCalendarComponent } from "../attendance-calendar/attendance-calendar.component";
import { EntitiesTableComponent } from "#src/app/core/common-components/entities-table/entities-table.component";
import { InMemoryDataSource } from "#src/app/core/common-components/entities-table/data-source/in-memory-data-source";
/**
* Displays detailed attendance data for a calculated attendance time period (`ActivityAttendance`).
* This is often displayed as a dialog for drilling down into details.
*/
@Component({
selector: "app-attendance-details",
templateUrl: "./attendance-details.component.html",
styleUrls: ["./attendance-details.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
DialogCloseComponent,
MatDialogModule,
MatFormFieldModule,
PercentPipe,
CustomDatePipe,
FormsModule,
MatInputModule,
EntitiesTableComponent,
AttendanceCalendarComponent,
],
})
export class AttendanceDetailsComponent {
private formDialog = inject(FormDialogService);
private data = inject<{
forChild: string;
attendance: ActivityAttendance;
}>(MAT_DIALOG_DATA);
readonly dataSource = new InMemoryDataSource();
entity = input(this.data.attendance);
forChild = input(this.data.forChild);
eventEntityType = computed(() =>
this.entity()?.events[0]?.entity?.getConstructor(),
);
eventsColumns: FormFieldConfig[] = [
{ id: "date" },
{ id: "subject", label: $localize`Event` },
{
id: "getAttendance",
label: $localize`:How a child attended, e.g. too late, in time, excused, e.t.c:Attended`,
viewComponent: "ReadonlyFunction",
additional: (event: Entity) => {
const eventWithAttendance = this.entity().events.find(
(e) => e.entity === event,
);
if (this.forChild()) {
const item = eventWithAttendance?.getAttendanceForParticipant(
this.forChild(),
);
return item?.status?.label || "-";
} else {
const avg = eventWithAttendance?.getAttendanceStats().average;
if (!Number.isFinite(avg)) {
return $localize`N/A`;
}
return `${(avg * 100).toFixed(0)}%`;
}
},
},
];
constructor() {
effect(() => {
const events = this.entity()?.events.map((e) => e.entity);
this.dataSource.allRecords.set(events);
});
}
showEventDetails(event: Entity) {
this.formDialog.openView(event);
}
}
<h1 mat-dialog-title>
<app-dialog-close mat-dialog-close></app-dialog-close>
{{ entity()?.activity?.toString() }}:
{{ entity().periodFrom | customDate: "shortDate" }} -
{{ entity().periodTo | customDate: "shortDate" }}
</h1>
<div mat-dialog-content>
<div class="summary w-{{ entity().getWarningLevel() }}">
<span
i18n="
Attendance|Attendance of a child (in percent) or the average of the
event (in percent)
"
>Attendance:</span
>
@let pct =
forChild()
? entity()?.getAttendancePercentage(forChild())
: entity()?.getAttendancePercentageAverage();
@if (pct !== undefined) {
{{ pct | percent: "1.0-0" }}
} @else {
-
}
</div>
@if (forChild()) {
<div class="flex-row flex-wrap gap-regular">
<mat-form-field>
<mat-label i18n="days present|How many days a child was present"
>Days Present</mat-label
>
<input
matInput
type="number"
[value]="entity().countEventsPresent(forChild())"
readonly
/>
</mat-form-field>
<mat-form-field>
<mat-label i18n="days absent|How many days a child was absent"
>Days Absent</mat-label
>
<input
matInput
type="number"
[value]="entity().countEventsAbsent(forChild())"
readonly
/>
</mat-form-field>
<mat-form-field>
<mat-label
i18n="
unknown status|How many days the presence or absence of a child is
unknown
"
>Unknown Status</mat-label
>
<input
matInput
type="number"
[value]="entity().countEventsWithUnknownStatus(forChild())"
readonly
/>
</mat-form-field>
</div>
} @else {
<div class="flex-row flex-wrap gap-regular">
<mat-form-field>
<mat-label i18n="Total present|How many children were present"
>Total Present</mat-label
>
<input
matInput
type="number"
[value]="entity().countTotalPresent()"
readonly
/>
</mat-form-field>
<mat-form-field>
<mat-label i18n="Total absent|How many children were absent"
>Total Absent</mat-label
>
<input
matInput
type="number"
[value]="entity().countTotalAbsent()"
readonly
/>
</mat-form-field>
<mat-form-field>
<mat-label
i18n="
Total unknown|How many children have an unknown presence or absence
status
"
>Total Unknown</mat-label
>
<input
matInput
type="number"
[value]="entity().countEventsWithUnknownStatus()"
readonly
/>
</mat-form-field>
</div>
}
@if (!!eventEntityType()) {
<app-entities-table
[entityType]="eventEntityType()"
[recordsDataSource]="dataSource"
[customColumns]="eventsColumns"
clickMode="none"
(entityClick)="showEventDetails($event)"
[editable]="false"
>
</app-entities-table>
<app-attendance-calendar
[records]="entity().events"
[highlightForChild]="forChild()"
[activity]="entity().activity"
></app-attendance-calendar>
}
</div>
@use "variables/sizes";
.summary {
font-weight: bold;
margin-bottom: sizes.$regular;
}
Legend
Html element with directive