diff --git a/APP/src/app/features/expense/pages/monthly-statement-page/monthly-statement-page.component.html b/APP/src/app/features/expense/pages/monthly-statement-page/monthly-statement-page.component.html
new file mode 100644
index 0000000..acaae81
--- /dev/null
+++ b/APP/src/app/features/expense/pages/monthly-statement-page/monthly-statement-page.component.html
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dataItem.difference | currency:'USD':'symbol':'1.2-2' }}
+
+
+
+
+
+
+
+ {{ dataItem.isFinalized ? 'Yes' : 'No' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Computed Summary
+
Total Giving
+
{{ preview.totalGiving | currency:'USD':'symbol':'1.2-2' }}
+
Total Expenses
+
{{ preview.totalExpenses | currency:'USD':'symbol':'1.2-2' }}
+
Calc. Closing
+
{{ preview.calculatedClosingBalance | currency:'USD':'symbol':'1.2-2' }}
+
Difference
+
+ {{ preview.difference | currency:'USD':'symbol':'1.2-2' }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/APP/src/app/features/expense/pages/monthly-statement-page/monthly-statement-page.component.scss b/APP/src/app/features/expense/pages/monthly-statement-page/monthly-statement-page.component.scss
new file mode 100644
index 0000000..6dd9a28
--- /dev/null
+++ b/APP/src/app/features/expense/pages/monthly-statement-page/monthly-statement-page.component.scss
@@ -0,0 +1,25 @@
+// Tailwind handles most layout; minimal local overrides only.
+
+.badge {
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ line-height: 1.4;
+}
+
+.badge-yes {
+ background: #dcfce7;
+ color: #166534;
+}
+
+.badge-no {
+ background: #f1f5f9;
+ color: #475569;
+}
+
+// Ensure Tailwind text-red-600 works even with preflight off
+.text-red-600 {
+ color: #dc2626;
+}
diff --git a/APP/src/app/features/expense/pages/monthly-statement-page/monthly-statement-page.component.ts b/APP/src/app/features/expense/pages/monthly-statement-page/monthly-statement-page.component.ts
new file mode 100644
index 0000000..c14ed4c
--- /dev/null
+++ b/APP/src/app/features/expense/pages/monthly-statement-page/monthly-statement-page.component.ts
@@ -0,0 +1,103 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { GridModule } from '@progress/kendo-angular-grid';
+import { ButtonsModule } from '@progress/kendo-angular-buttons';
+import { DialogsModule } from '@progress/kendo-angular-dialog';
+import { InputsModule } from '@progress/kendo-angular-inputs';
+import { MonthlyStatementApiService } from '../../services/monthly-statement-api.service';
+import { MonthlyStatementDto } from '../../models/expense.model';
+
+@Component({
+ selector: 'app-monthly-statement-page',
+ standalone: true,
+ imports: [CommonModule, FormsModule, GridModule, ButtonsModule, DialogsModule, InputsModule],
+ templateUrl: './monthly-statement-page.component.html',
+ styleUrls: ['./monthly-statement-page.component.scss'],
+})
+export class MonthlyStatementPageComponent implements OnInit {
+ rows: MonthlyStatementDto[] = [];
+ loading = false;
+ yearFilter: number | null = null;
+
+ dialogOpen = false;
+ editing: MonthlyStatementDto | null = null;
+ form = {
+ year: new Date().getFullYear(),
+ month: new Date().getMonth() + 1,
+ openingBalance: 0,
+ totalOtherIncome: 0,
+ bankStatementBalance: 0,
+ notes: '',
+ };
+ preview: MonthlyStatementDto | null = null;
+
+ constructor(private api: MonthlyStatementApiService) {}
+
+ ngOnInit(): void { this.load(); }
+
+ load(): void {
+ this.loading = true;
+ this.api.getAll(this.yearFilter ?? undefined).subscribe({
+ next: r => { this.rows = r; this.loading = false; },
+ error: () => { this.loading = false; },
+ });
+ }
+
+ openNew(): void {
+ this.editing = null;
+ this.preview = null;
+ this.form = {
+ year: new Date().getFullYear(),
+ month: new Date().getMonth() + 1,
+ openingBalance: 0,
+ totalOtherIncome: 0,
+ bankStatementBalance: 0,
+ notes: '',
+ };
+ this.dialogOpen = true;
+ }
+
+ openEdit(row: MonthlyStatementDto): void {
+ this.editing = row;
+ this.preview = row;
+ this.form = {
+ year: row.year,
+ month: row.month,
+ openingBalance: row.openingBalance,
+ totalOtherIncome: row.totalOtherIncome,
+ bankStatementBalance: row.bankStatementBalance,
+ notes: row.notes ?? '',
+ };
+ this.dialogOpen = true;
+ }
+
+ save(): void {
+ const done = () => { this.dialogOpen = false; this.load(); };
+ if (this.editing == null) {
+ this.api.create({
+ year: this.form.year,
+ month: this.form.month,
+ openingBalance: this.form.openingBalance,
+ totalOtherIncome: this.form.totalOtherIncome,
+ bankStatementBalance: this.form.bankStatementBalance,
+ notes: this.form.notes || null,
+ }).subscribe({
+ next: created => this.api.getById(created.id).subscribe(s => { this.preview = s; done(); }),
+ error: err => alert(err?.error?.message ?? 'Create failed (a statement for that month may already exist).'),
+ });
+ } else {
+ this.api.update(this.editing.id, {
+ openingBalance: this.form.openingBalance,
+ totalOtherIncome: this.form.totalOtherIncome,
+ bankStatementBalance: this.form.bankStatementBalance,
+ notes: this.form.notes || null,
+ }).subscribe(done);
+ }
+ }
+
+ finalize(row: MonthlyStatementDto): void {
+ if (!confirm(`Finalize ${row.year}-${String(row.month).padStart(2, '0')}? This locks the statement.`)) return;
+ this.api.finalize(row.id).subscribe(() => this.load());
+ }
+}