+
@@ -44,6 +44,9 @@
+
+ {{ s.sundayAttendanceCount ?? '—' }}
+
📎
@@ -51,15 +54,12 @@
-
-
-
-
-
No sessions yet — pick a date above to start.
尚無紀錄 — 選擇上方日期開始
+
+ 點一列檢視 · 右鍵修改主日人數 / Click a row to view · right-click to edit attendance
@@ -306,4 +306,25 @@
+
+
+
+
+
+
+
+
+ 總數 Total: {{ attTotal }}
+
+
+
+
+
diff --git a/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.scss b/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.scss
index dafebfe..eed978a 100644
--- a/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.scss
+++ b/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.scss
@@ -233,3 +233,13 @@
@media (prefers-reduced-motion: reduce) {
.rise { animation: none; opacity: 1; transform: none; }
}
+
+.clickable-rows {
+ .k-grid-table tr { cursor: pointer; }
+}
+
+.att-total {
+ margin-top: 0.75rem;
+ font-weight: 600;
+ text-align: right;
+}
diff --git a/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.ts b/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.ts
index 82266a4..797e95e 100644
--- a/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.ts
+++ b/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.ts
@@ -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. */