diff --git a/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.html b/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.html index 6c4d7bd..dcfcd55 100644 --- a/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.html +++ b/APP/src/app/features/giving/pages/offering-session-page/offering-session-page.component.html @@ -36,7 +36,7 @@ 最近的奉獻紀錄 - + @@ -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. */