diff --git a/APP/src/app/features/giving/pages/giving-categories-page/giving-categories-page.component.html b/APP/src/app/features/giving/pages/giving-categories-page/giving-categories-page.component.html
new file mode 100644
index 0000000..79bdd68
--- /dev/null
+++ b/APP/src/app/features/giving/pages/giving-categories-page/giving-categories-page.component.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+ {{ c.isActive ? 'Yes' : 'No' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/APP/src/app/features/giving/pages/giving-categories-page/giving-categories-page.component.scss b/APP/src/app/features/giving/pages/giving-categories-page/giving-categories-page.component.scss
new file mode 100644
index 0000000..e249197
--- /dev/null
+++ b/APP/src/app/features/giving/pages/giving-categories-page/giving-categories-page.component.scss
@@ -0,0 +1,31 @@
+.page-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1rem;
+}
+
+.header-actions {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.inactive-toggle {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ cursor: pointer;
+}
+
+.form-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.form-grid label {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
diff --git a/APP/src/app/features/giving/pages/giving-categories-page/giving-categories-page.component.ts b/APP/src/app/features/giving/pages/giving-categories-page/giving-categories-page.component.ts
new file mode 100644
index 0000000..3d61cf5
--- /dev/null
+++ b/APP/src/app/features/giving/pages/giving-categories-page/giving-categories-page.component.ts
@@ -0,0 +1,74 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { GridModule } from '@progress/kendo-angular-grid';
+import { InputsModule } from '@progress/kendo-angular-inputs';
+import { ButtonsModule } from '@progress/kendo-angular-buttons';
+import { DialogsModule } from '@progress/kendo-angular-dialog';
+import { GivingCategoryApiService } from '../../services/giving-category-api.service';
+import {
+ GivingCategoryDto, CreateGivingCategoryRequest, UpdateGivingCategoryRequest,
+} from '../../models/giving.model';
+
+@Component({
+ selector: 'app-giving-categories-page',
+ standalone: true,
+ imports: [CommonModule, FormsModule, GridModule, InputsModule, ButtonsModule, DialogsModule],
+ templateUrl: './giving-categories-page.component.html',
+ styleUrls: ['./giving-categories-page.component.scss'],
+})
+export class GivingCategoriesPageComponent implements OnInit {
+ data: GivingCategoryDto[] = [];
+ isLoading = false;
+ includeInactive = false;
+
+ showDialog = false;
+ editing: GivingCategoryDto | null = null;
+ form: UpdateGivingCategoryRequest = this.blankForm();
+
+ constructor(private api: GivingCategoryApiService) {}
+
+ ngOnInit(): void { this.load(); }
+
+ load(): void {
+ this.isLoading = true;
+ this.api.getAll(this.includeInactive).subscribe({
+ next: rows => { this.data = rows; this.isLoading = false; },
+ error: () => { this.isLoading = false; },
+ });
+ }
+
+ openAdd(): void { this.editing = null; this.form = this.blankForm(); this.showDialog = true; }
+
+ openEdit(c: GivingCategoryDto): void {
+ this.editing = c;
+ this.form = {
+ name_en: c.name_en, name_zh: c.name_zh,
+ description_en: c.description_en, description_zh: c.description_zh,
+ isActive: c.isActive, sortOrder: c.sortOrder,
+ };
+ this.showDialog = true;
+ }
+
+ save(): void {
+ if (this.editing) {
+ this.api.update(this.editing.id, this.form).subscribe(() => { this.showDialog = false; this.load(); });
+ } else {
+ const create: CreateGivingCategoryRequest = {
+ name_en: this.form.name_en, name_zh: this.form.name_zh,
+ description_en: this.form.description_en, description_zh: this.form.description_zh,
+ sortOrder: this.form.sortOrder,
+ };
+ this.api.create(create).subscribe(() => { this.showDialog = false; this.load(); });
+ }
+ }
+
+ deactivate(c: GivingCategoryDto): void {
+ if (!confirm(`Deactivate "${c.name_en}"?`)) return;
+ this.api.deactivate(c.id).subscribe(() => this.load());
+ }
+
+ private blankForm(): UpdateGivingCategoryRequest {
+ return { name_en: '', name_zh: null, description_en: null, description_zh: null, isActive: true, sortOrder: 0 };
+ }
+}