Enhance offering session
This commit is contained in:
+87
-25
@@ -7,6 +7,7 @@ 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 { OfferingSessionApiService } from '../../services/offering-session-api.service';
|
||||
import { GivingCategoryApiService } from '../../services/giving-category-api.service';
|
||||
import { MemberApiService } from '../../../members/services/member-api.service';
|
||||
@@ -20,17 +21,21 @@ import { PAYMENT_METHOD_OPTIONS } from '../../../../shared/i18n/option-lists';
|
||||
|
||||
interface MemberOption { id: number; displayName: string; }
|
||||
|
||||
type PageMode = 'landing' | 'workspace' | 'view';
|
||||
|
||||
@Component({
|
||||
selector: 'app-offering-session-page',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule, FormsModule, GridModule, InputsModule, ButtonsModule,
|
||||
DropDownsModule, DateInputsModule, MemberQuickAddDialogComponent,
|
||||
DropDownsModule, DateInputsModule, DialogsModule, MemberQuickAddDialogComponent,
|
||||
],
|
||||
templateUrl: './offering-session-page.component.html',
|
||||
styleUrls: ['./offering-session-page.component.scss'],
|
||||
})
|
||||
export class OfferingSessionPageComponent implements OnInit {
|
||||
mode: PageMode = 'landing';
|
||||
|
||||
sessionDate: Date = new Date();
|
||||
dateConflict = false;
|
||||
categories: GivingCategoryDto[] = [];
|
||||
@@ -54,6 +59,10 @@ export class OfferingSessionPageComponent implements OnInit {
|
||||
sessions: OfferingSessionListItemDto[] = [];
|
||||
editingSessionId: number | null = null;
|
||||
|
||||
// Read-only session shown in `view` mode, and the confirm dialog for reopening it.
|
||||
viewSession: OfferingSessionDto | null = null;
|
||||
confirmReopenOpen = false;
|
||||
|
||||
constructor(
|
||||
private api: OfferingSessionApiService,
|
||||
private categoryApi: GivingCategoryApiService,
|
||||
@@ -80,23 +89,77 @@ export class OfferingSessionPageComponent implements OnInit {
|
||||
this.api.getPaged(1, 20).subscribe(r => this.sessions = r.items);
|
||||
}
|
||||
|
||||
reopenAndEdit(s: OfferingSessionListItemDto): void {
|
||||
if (s.status !== 'Submitted') return;
|
||||
this.api.reopen(s.id).subscribe({
|
||||
next: () => this.api.getById(s.id).subscribe(dto => this.loadIntoBuffer(dto)),
|
||||
error: (err: { error?: { message?: string } }) => alert(err?.error?.message ?? 'Reopen failed.'),
|
||||
// ── Flow: landing → workspace / view ──────────────────────────────────────
|
||||
|
||||
/** Free date chosen on the landing screen — begin a brand-new session. */
|
||||
startEntry(): void {
|
||||
if (this.dateConflict) return;
|
||||
this.editingSessionId = null;
|
||||
this.editingIndex = null;
|
||||
this.viewSession = null;
|
||||
this.buffer = [];
|
||||
this.cashTotal = 0; this.checkTotal = 0; this.notes = null;
|
||||
this.resetEntry();
|
||||
this.mode = 'workspace';
|
||||
}
|
||||
|
||||
/** Existing date chosen on the landing screen — resolve it to its session and view it. */
|
||||
openViewByDate(): void {
|
||||
const iso = this.toIso(this.sessionDate);
|
||||
this.api.getPaged(1, 1, iso, iso).subscribe({
|
||||
next: r => {
|
||||
const item = r.items[0];
|
||||
if (!item) { this.checkDate(); return; } // race: it vanished, refresh the flag
|
||||
this.openView(item);
|
||||
},
|
||||
error: (err: { error?: { message?: string } }) => alert(err?.error?.message ?? 'Load failed.'),
|
||||
});
|
||||
}
|
||||
|
||||
// Already a Draft (e.g. a session reopened then left) — load it straight back in, no reopen.
|
||||
continueEditDraft(s: OfferingSessionListItemDto): void {
|
||||
if (s.status !== 'Draft') return;
|
||||
/** Open a session read-only (from a Recent Sessions row or a resolved date). */
|
||||
openView(s: OfferingSessionListItemDto): void {
|
||||
this.api.getById(s.id).subscribe({
|
||||
next: dto => this.loadIntoBuffer(dto),
|
||||
next: dto => { this.viewSession = dto; this.mode = 'view'; },
|
||||
error: (err: { error?: { message?: string } }) => alert(err?.error?.message ?? 'Load failed.'),
|
||||
});
|
||||
}
|
||||
|
||||
/** Edit button on the read-only view. Draft edits directly; Submitted confirms a reopen first. */
|
||||
editFromView(): void {
|
||||
const dto = this.viewSession;
|
||||
if (!dto) return;
|
||||
if (dto.status === 'Draft') {
|
||||
this.loadIntoBuffer(dto);
|
||||
this.mode = 'workspace';
|
||||
} else if (dto.status === 'Submitted') {
|
||||
this.confirmReopenOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Confirmed reopen of a Submitted session (status → Draft) then load it for editing. */
|
||||
confirmReopen(): void {
|
||||
const dto = this.viewSession;
|
||||
if (!dto) { this.confirmReopenOpen = false; return; }
|
||||
this.api.reopen(dto.id).subscribe({
|
||||
next: () => this.api.getById(dto.id).subscribe(fresh => {
|
||||
this.loadIntoBuffer(fresh);
|
||||
this.confirmReopenOpen = false;
|
||||
this.mode = 'workspace';
|
||||
}),
|
||||
error: (err: { error?: { message?: string } }) => {
|
||||
this.confirmReopenOpen = false;
|
||||
alert(err?.error?.message ?? 'Reopen failed.');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** Leave workspace/view and return to the date-first landing screen. */
|
||||
backToLanding(): void {
|
||||
this.resetSession();
|
||||
this.mode = 'landing';
|
||||
this.loadSessions();
|
||||
}
|
||||
|
||||
private loadIntoBuffer(dto: OfferingSessionDto): void {
|
||||
this.editingSessionId = dto.id;
|
||||
this.sessionDate = new Date(dto.sessionDate + 'T00:00:00');
|
||||
@@ -114,16 +177,6 @@ export class OfferingSessionPageComponent implements OnInit {
|
||||
this.resetEntry();
|
||||
}
|
||||
|
||||
cancelEdit(): void {
|
||||
this.editingSessionId = null;
|
||||
this.editingIndex = null;
|
||||
this.buffer = []; this.cashTotal = 0; this.checkTotal = 0; this.notes = null;
|
||||
this.sessionDate = new Date();
|
||||
this.checkDate();
|
||||
// The reopened session is now a server-side Draft — refresh so its "Continue editing" appears.
|
||||
this.loadSessions();
|
||||
}
|
||||
|
||||
onMemberFilter(term: string): void {
|
||||
if (!term) { this.memberResults = []; return; }
|
||||
this.memberApi.getPaged({ search: term, pageSize: 10 }).subscribe(r =>
|
||||
@@ -208,11 +261,8 @@ export class OfferingSessionPageComponent implements OnInit {
|
||||
next: () => {
|
||||
this.submitting = false;
|
||||
alert(this.editingSessionId != null ? 'Offering session updated.' : 'Offering session submitted.');
|
||||
this.editingSessionId = null;
|
||||
this.editingIndex = null;
|
||||
this.buffer = []; this.cashTotal = 0; this.checkTotal = 0; this.notes = null;
|
||||
this.sessionDate = new Date();
|
||||
this.checkDate();
|
||||
this.resetSession();
|
||||
this.mode = 'landing';
|
||||
this.loadSessions();
|
||||
},
|
||||
error: (err: { error?: { message?: string } }) => {
|
||||
@@ -222,6 +272,18 @@ export class OfferingSessionPageComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
/** Clear the whole working session back to a fresh state (today, empty buffer). */
|
||||
private resetSession(): void {
|
||||
this.editingSessionId = null;
|
||||
this.editingIndex = null;
|
||||
this.viewSession = null;
|
||||
this.buffer = [];
|
||||
this.cashTotal = 0; this.checkTotal = 0; this.notes = null;
|
||||
this.sessionDate = new Date();
|
||||
this.resetEntry();
|
||||
this.checkDate();
|
||||
}
|
||||
|
||||
private resetEntry(): void {
|
||||
this.editingIndex = null;
|
||||
this.selectedMemberId = null; this.selectedMemberName = null; this.memberResults = [];
|
||||
|
||||
Reference in New Issue
Block a user