src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.component.ts

Metadata

Relationships

Used by

No results matching.

Index

Properties
Methods
Inputs

Constructor

constructor()

Inputs

entities
Type : EntityPropertyMap

An object holding the names of entities and properties where they have a DateOfBirth attribute. E.g. (which is also the default)

Example :
"entities": { "Child": "dateOfBirth" }
explanation
Type : string
subtitle
Type : string
Default value : $localize`:dashboard widget subtitle:Upcoming Birthdays`
threshold
Type : number
Default value : 31

Birthdays that are less or equal than "threshold" days away are shown. Default 31

Methods

Static getRequiredEntities
getRequiredEntities(config: BirthdayDashboardConfig)
Parameters :
Name Type Optional
config BirthdayDashboardConfig No
Returns : any

Properties

entries
Type : unknown
Default value : signal<EntityWithBirthday[] | undefined>(undefined)
import {
  ChangeDetectionStrategy,
  Component,
  effect,
  inject,
  input,
  signal,
} from "@angular/core";
import { debounceTime, merge } from "rxjs";
import { DynamicComponent } from "#src/app/core/config/dynamic-components/dynamic-component.decorator";
import { MatTableModule } from "@angular/material/table";
import { EntityMapperService } from "#src/app/core/entity/entity-mapper/entity-mapper.service";
import { DatePipe } from "@angular/common";
import { EntityBlockComponent } from "#src/app/core/basic-datatypes/entity/entity-block/entity-block.component";
import { DashboardListWidgetComponent } from "#src/app/core/dashboard/dashboard-list-widget/dashboard-list-widget.component";
import {
  BirthdayDashboardIndexService,
  EntityPropertyMap,
  EntityWithBirthday,
} from "./birthday-dashboard-index.service";
import { Logging } from "#src/app/core/logging/logging.service";

interface BirthdayDashboardConfig {
  entities: EntityPropertyMap;
  threshold: number;
}

@DynamicComponent("BirthdayDashboard")
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "app-birthday-dashboard",
  templateUrl: "./birthday-dashboard.component.html",
  styleUrls: ["./birthday-dashboard.component.scss"],
  imports: [
    MatTableModule,
    EntityBlockComponent,
    DatePipe,
    DashboardListWidgetComponent,
  ],
})
export class BirthdayDashboardComponent {
  private readonly birthdayIndex = inject(BirthdayDashboardIndexService);
  private readonly entityMapper = inject(EntityMapperService);
  entries = signal<EntityWithBirthday[] | undefined>(undefined);

  static getRequiredEntities(config: BirthdayDashboardConfig) {
    return config?.entities ? Object.keys(config.entities) : "Child";
  }

  /**
   * An object holding the names of entities and properties where they have a `DateOfBirth` attribute.
   * E.g. (which is also the default)
   * ```json
   * "entities": { "Child": "dateOfBirth" }
   * ```
   */
  entities = input<EntityPropertyMap>({ ["Child"]: "dateOfBirth" });

  /**
   * Birthdays that are less or equal than "threshold" days away are shown.
   * Default 31
   */
  threshold = input(31);

  subtitle = input<string>(
    $localize`:dashboard widget subtitle:Upcoming Birthdays`,
  );
  explanation = input<string>();

  constructor() {
    effect((onCleanup) => {
      const entityConfig = this.entities();
      const threshold = this.threshold();
      let isCurrent = true;

      const indexBuilt = this.birthdayIndex.buildBirthdayIndex(entityConfig);

      const reload = () =>
        indexBuilt
          .then(() =>
            this.birthdayIndex.queryBirthdayIndex(entityConfig, threshold),
          )
          .catch((err) => {
            Logging.error("Failed to load upcoming birthdays", err);
            return [];
          })
          .then((res) => {
            if (isCurrent) {
              this.entries.set(res);
            }
          });

      // initial load - covers the case where matching entities already exist on mount.
      reload();

      // Re-query (cheap/incremental) whenever a relevant entity type changes, so the
      // widget picks up entities added/edited after the initial query (e.g. while the
      // index was still empty during sync/demo-data generation), and stays live for
      // ongoing changes. Debounced because e.g. demo-data generation saves many
      // entities in a burst - without this, that's one redundant query per save.
      const subscription = merge(
        ...Object.keys(entityConfig).map((type) =>
          this.entityMapper.receiveUpdates(type),
        ),
      )
        .pipe(debounceTime(500))
        .subscribe(() => reload());

      onCleanup(() => {
        isCurrent = false;
        subscription.unsubscribe();
      });
    });
  }
}
<app-dashboard-list-widget
  icon="birthday-cake"
  theme="child"
  [subtitle]="subtitle()"
  [explanation]="explanation()"
  [entries]="entries()"
>
  <div class="table-wrapper">
    <table mat-table>
      <!-- Table header only for assistive technologies like screen readers -->
      <tr class="visually-hidden">
        <th scope="col" i18n="Column header for the name of a participant">
          Name
        </th>
        <th scope="col" i18n="Column header for the date of anniversary">
          Date of "anniversary"
        </th>
        <th
          scope="col"
          i18n="Column header for the age at the next anniversary date"
        >
          Age after next date
        </th>
      </tr>
      <ng-container matColumnDef="entity">
        <td *matCellDef="let entity">
          <app-entity-block [entity]="entity.entity"></app-entity-block>
        </td>
      </ng-container>
      <ng-container matColumnDef="dateOfBirth">
        <td *matCellDef="let entity">
          {{ entity.birthday | date: "E, dd. MMM" }}
        </td>
      </ng-container>
      <ng-container matColumnDef="newAge">
        <td *matCellDef="let entity" i18n>{{ entity.newAge }} yrs</td>
      </ng-container>
      <tr
        mat-row
        *matRowDef="let row; columns: ['entity', 'dateOfBirth', 'newAge']"
      ></tr>
    </table>
  </div>
</app-dashboard-list-widget>

./birthday-dashboard.component.scss

@use "../../../../core/dashboard/dashboard-widget-base";
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""