src/app/features/attendance/add-day-attendance/activity-card/activity-card.component.ts

Description

Simple representation of an event in the context of setting up or selecting an event for a roll call.

Example

Metadata

Relationships

Depends on

Index

Properties
Inputs

Inputs

event
Type : EventWithAttendance
Required :  true

The event to be displayed.

recurring
Type : boolean

Whether the event or activity is displayed in the style of events generated from a generic recurring activity.

If not explicitly defined, it is inferred from the entity's relatesTo reference to a RecurringActivity.

Properties

Readonly attendance
Type : unknown
Default value : computed<AttendanceItem[]>( () => this.event().attendanceItems, )

The attendance items for the current event.

Readonly extraField
Type : unknown
Default value : computed(() => this.event().extraField)

An optional extra entity field to display on the card (e.g. "category"). The value's label property is shown if available, otherwise the raw value.

Readonly extraFieldValue
Type : unknown
Default value : computed<string | undefined>(() => { const field = this.extraField(); if (!field) { return undefined; } const val = this.event().entity[field]; return val?.label ?? val; })

Resolved display value of the extra field, if configured.

Readonly isRecurring
Type : unknown
Default value : computed(() => { const explicit = this.recurring(); if (explicit !== undefined) { return explicit; } return this.event().isActivityEvent; })

Whether this event is linked to a recurring activity.

Readonly warningLevel
Type : unknown
Default value : computed<"ok" | "warning" | "urgent">(() => { const att = this.attendance(); const hasUnknown = att.some((a) => !a.status?.id); if (!hasUnknown) { return "ok"; } return this.isRecurring() ? "warning" : "urgent"; })
import {
  ChangeDetectionStrategy,
  Component,
  computed,
  input,
} from "@angular/core";
import { AttendanceItem } from "../../model/attendance-item";
import { EventWithAttendance } from "../../model/event-with-attendance";
import { MatCardModule } from "@angular/material/card";
import { BorderHighlightDirective } from "#src/app/core/common-components/border-highlight/border-highlight.directive";
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { CustomDatePipe } from "#src/app/core/basic-datatypes/date/custom-date.pipe";

/**
 * Simple representation of an event
 * in the context of setting up or selecting an event for a roll call.
 */
@Component({
  selector: "app-activity-card",
  templateUrl: "./activity-card.component.html",
  styleUrls: ["./activity-card.component.scss"],
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    MatCardModule,
    BorderHighlightDirective,
    FontAwesomeModule,
    CustomDatePipe,
  ],
})
export class ActivityCardComponent {
  /**
   * The event to be displayed.
   */
  readonly event = input.required<EventWithAttendance>();

  /**
   * An optional extra entity field to display on the card (e.g. "category").
   * The value's `label` property is shown if available, otherwise the raw value.
   */
  readonly extraField = computed(() => this.event().extraField);

  /**
   * Whether the event or activity is displayed in the style of events generated from a generic recurring activity.
   *
   * If not explicitly defined, it is inferred from the entity's `relatesTo` reference to a RecurringActivity.
   */
  readonly recurring = input<boolean>();

  /** The attendance items for the current event. */
  readonly attendance = computed<AttendanceItem[]>(
    () => this.event().attendanceItems,
  );

  /** Whether this event is linked to a recurring activity. */
  readonly isRecurring = computed(() => {
    const explicit = this.recurring();
    if (explicit !== undefined) {
      return explicit;
    }
    return this.event().isActivityEvent;
  });

  /** Resolved display value of the extra field, if configured. */
  readonly extraFieldValue = computed<string | undefined>(() => {
    const field = this.extraField();
    if (!field) {
      return undefined;
    }
    const val = this.event().entity[field];
    return val?.label ?? val;
  });

  readonly warningLevel = computed<"ok" | "warning" | "urgent">(() => {
    const att = this.attendance();
    const hasUnknown = att.some((a) => !a.status?.id);
    if (!hasUnknown) {
      return "ok";
    }
    return this.isRecurring() ? "warning" : "urgent";
  });
}
<mat-card
  appBorderHighlight
  [borderClass]="'border-' + warningLevel()"
  class="main-card"
>
  <mat-card-header>
    <mat-card-title>{{ event().entity.toString() }}</mat-card-title>
  </mat-card-header>
  <mat-card-content class="text-secondary">
    <fa-icon icon="child"></fa-icon>
    {{ attendance().length }}
    <span i18n>participants</span>

    @if (!isRecurring() && event().date) {
      <span class="sep">/</span>
      <fa-icon icon="calendar"></fa-icon>
      {{ event().date | customDate: "shortDate" }}
    }

    @if (extraFieldValue()) {
      <span class="sep">/</span>{{ extraFieldValue() }}
    }
  </mat-card-content>
</mat-card>

./activity-card.component.scss

@use "variables/colors";

.border-ok {
  border-color: colors.$success !important;
}

.border-warning {
  border-color: colors.$warn !important;
}

.border-urgent {
  border-color: colors.$error !important;
}

.main-card {
  max-width: 500px;
  height: 100%;
  box-sizing: border-box;
}

.card-title {
  font-weight: bold;
}

.sep {
  font-weight: 900;
  color: colors.$disabled;

  /* Add a whitespace before the separator */
  &::before {
    content: " ";
  }

  /* Add a whitespace before the separator */
  &::after {
    content: " ";
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""