feat(giving): show Sunday attendance per session and add edit action

This commit is contained in:
Chris Chen
2026-06-24 11:40:44 -07:00
parent b0e2e112fc
commit e768f53ccc
3 changed files with 104 additions and 9 deletions
@@ -1,14 +1,16 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Observable, Subject, from, of, map, switchMap, takeUntil } from 'rxjs';
import { buildProofPdf } from '../../services/proof-pdf.builder';
import { GridModule } from '@progress/kendo-angular-grid';
import { GridModule, CellClickEvent } from '@progress/kendo-angular-grid';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { ButtonsModule } from '@progress/kendo-angular-buttons';
import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
import { DateInputsModule } from '@progress/kendo-angular-dateinputs';
import { DialogsModule } from '@progress/kendo-angular-dialog';
import { ContextMenuModule, ContextMenuComponent, ContextMenuSelectEvent } from '@progress/kendo-angular-menu';
import { MealAttendanceApiService } from '../../../meal-attendance/services/meal-attendance-api.service';
import { OfferingSessionApiService } from '../../services/offering-session-api.service';
import { OfferingEntrySignalrService } from '../../services/offering-entry-signalr.service';
import { GivingCategoryApiService } from '../../services/giving-category-api.service';
@@ -30,7 +32,7 @@ type PageMode = 'landing' | 'workspace' | 'view';
standalone: true,
imports: [
CommonModule, FormsModule, GridModule, InputsModule, ButtonsModule,
DropDownsModule, DateInputsModule, DialogsModule, MemberQuickAddDialogComponent,
DropDownsModule, DateInputsModule, DialogsModule, ContextMenuModule, MemberQuickAddDialogComponent,
],
templateUrl: './offering-session-page.component.html',
styleUrls: ['./offering-session-page.component.scss'],
@@ -74,11 +76,24 @@ export class OfferingSessionPageComponent implements OnInit, OnDestroy {
viewSession: OfferingSessionDto | null = null;
confirmReopenOpen = false;
// Right-click actions on a Recent Sessions row.
@ViewChild('sessionMenu') sessionMenu!: ContextMenuComponent;
readonly sessionMenuItems = [{ text: 'View / 檢視' }, { text: '修改主日人數' }];
private contextSession: OfferingSessionListItemDto | null = null;
// Edit Sunday attendance dialog.
attDialogOpen = false;
attSaving = false;
private attDate: string | null = null; // yyyy-MM-dd of the session being edited
attForm = { adult: 0, youth: 0, kid: 0 };
get attTotal(): number { return this.attForm.adult + this.attForm.youth + this.attForm.kid; }
constructor(
private api: OfferingSessionApiService,
private categoryApi: GivingCategoryApiService,
private memberApi: MemberApiService,
private signalr: OfferingEntrySignalrService,
private mealAttendanceApi: MealAttendanceApiService,
) {}
ngOnInit(): void {
@@ -162,6 +177,55 @@ export class OfferingSessionPageComponent implements OnInit, OnDestroy {
this.api.getPaged(1, 20).subscribe(r => this.sessions = r.items);
}
// Left-click anywhere on a row opens it; right-click opens the actions menu.
onSessionCellClick(event: CellClickEvent): void {
if (event.type === 'contextmenu') {
event.originalEvent.preventDefault();
this.contextSession = event.dataItem;
this.sessionMenu.show({ left: event.originalEvent.pageX, top: event.originalEvent.pageY });
} else {
this.openView(event.dataItem);
}
}
onSessionMenuSelect(event: ContextMenuSelectEvent): void {
const session = this.contextSession;
if (!session) return;
if (event.item.text === 'View / 檢視') this.openView(session);
else if (event.item.text === '修改主日人數') this.openAttendanceEdit(session);
}
// Open the attendance editor, prefilling the three age groups from the existing row (zeros if none).
openAttendanceEdit(session: OfferingSessionListItemDto): void {
this.attDate = session.sessionDate;
this.attForm = { adult: 0, youth: 0, kid: 0 };
this.attSaving = false;
this.attDialogOpen = true;
this.mealAttendanceApi.getRange(session.sessionDate, session.sessionDate).subscribe(rows => {
const row = rows[0];
if (row) this.attForm = { adult: row.adult, youth: row.youth, kid: row.kid };
});
}
saveAttendance(): void {
if (!this.attDate) return;
const date = this.attDate;
this.attSaving = true;
this.mealAttendanceApi.setCounts(date, this.attForm).subscribe({
next: counts => {
const total = counts.adult + counts.youth + counts.kid;
const row = this.sessions.find(s => s.sessionDate === date);
if (row) row.sundayAttendanceCount = total;
this.attDialogOpen = false;
this.attSaving = false;
},
error: (err: { error?: { message?: string } }) => {
this.attSaving = false;
alert(err?.error?.message ?? 'Save failed.');
},
});
}
// ── Flow: landing → workspace / view ──────────────────────────────────────
/** Free date chosen on the landing screen — begin a brand-new session. */