From d507aef5709e88f897ee063fd37adb6d39d5f55a Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Tue, 2 Dec 2025 16:18:47 -0500 Subject: [PATCH] Added category page. --- finnow-api/source/api_mapping.d | 1 + finnow-api/source/transaction/api.d | 5 + finnow-api/source/transaction/service.d | 7 ++ web-app/src/api/transaction.ts | 4 + .../src/components/CategoryDisplayItem.vue | 67 ++++------ web-app/src/pages/CategoryPage.vue | 118 ++++++++++++++++++ web-app/src/router/index.ts | 5 + 7 files changed, 161 insertions(+), 46 deletions(-) create mode 100644 web-app/src/pages/CategoryPage.vue diff --git a/finnow-api/source/api_mapping.d b/finnow-api/source/api_mapping.d index 9ea4159..220a99e 100644 --- a/finnow-api/source/api_mapping.d +++ b/finnow-api/source/api_mapping.d @@ -77,6 +77,7 @@ HttpRequestHandler mapApiHandlers(string webOrigin) { // Transaction category endpoints: a.map(HttpMethod.GET, PROFILE_PATH ~ "/categories", &handleGetCategories); a.map(HttpMethod.GET, PROFILE_PATH ~ "/categories/:categoryId:ulong", &handleGetCategory); + a.map(HttpMethod.GET, PROFILE_PATH ~ "/categories/:categoryId:ulong/children", &handleGetChildCategories); a.map(HttpMethod.POST, PROFILE_PATH ~ "/categories", &handleCreateCategory); a.map(HttpMethod.PUT, PROFILE_PATH ~ "/categories/:categoryId:ulong", &handleUpdateCategory); a.map(HttpMethod.DELETE, PROFILE_PATH ~ "/categories/:categoryId:ulong", &handleDeleteCategory); diff --git a/finnow-api/source/transaction/api.d b/finnow-api/source/transaction/api.d index 02566eb..839e8a3 100644 --- a/finnow-api/source/transaction/api.d +++ b/finnow-api/source/transaction/api.d @@ -132,6 +132,11 @@ void handleGetCategory(ref ServerHttpRequest request, ref ServerHttpResponse res writeJsonBody(response, category); } +void handleGetChildCategories(ref ServerHttpRequest request, ref ServerHttpResponse response) { + auto children = getChildCategories(getProfileDataSource(request), getCategoryId(request)); + writeJsonBody(response, children); +} + struct CategoryPayload { string name; string description; diff --git a/finnow-api/source/transaction/service.d b/finnow-api/source/transaction/service.d index 4aa995c..d1ae318 100644 --- a/finnow-api/source/transaction/service.d +++ b/finnow-api/source/transaction/service.d @@ -341,6 +341,13 @@ TransactionCategoryResponse getCategory(ProfileDataSource ds, ulong categoryId) return TransactionCategoryResponse.of(category); } +TransactionCategoryResponse[] getChildCategories(ProfileDataSource ds, ulong categoryId) { + import std.algorithm : map; + import std.array : array; + auto categories = ds.getTransactionCategoryRepository().findAllByParentId(Optional!ulong.of(categoryId)); + return categories.map!(TransactionCategoryResponse.of).array; +} + TransactionCategoryResponse createCategory(ProfileDataSource ds, in CategoryPayload payload) { TransactionCategoryRepository repo = ds.getTransactionCategoryRepository(); if (payload.name is null || payload.name.length == 0) { diff --git a/web-app/src/api/transaction.ts b/web-app/src/api/transaction.ts index 53cd70e..8cc7ed9 100644 --- a/web-app/src/api/transaction.ts +++ b/web-app/src/api/transaction.ts @@ -176,6 +176,10 @@ export class TransactionApiClient extends ApiClient { return super.getJson(this.path + '/categories/' + id) } + getChildCategories(parentId: number): Promise { + return super.getJson(this.path + '/categories/' + parentId + '/children') + } + createCategory(data: CreateCategoryPayload): Promise { return super.postJson(this.path + '/categories', data) } diff --git a/web-app/src/components/CategoryDisplayItem.vue b/web-app/src/components/CategoryDisplayItem.vue index 9e3065d..d472f68 100644 --- a/web-app/src/components/CategoryDisplayItem.vue +++ b/web-app/src/components/CategoryDisplayItem.vue @@ -2,6 +2,10 @@ import type { TransactionCategoryTree } from '@/api/transaction' import AppButton from './common/AppButton.vue' import { computed, ref } from 'vue' +import { useRoute } from 'vue-router'; +import { getSelectedProfile } from '@/api/profile'; + +const route = useRoute() const props = defineProps<{ category: TransactionCategoryTree @@ -15,59 +19,30 @@ const expanded = ref(false) const canExpand = computed(() => props.category.children.length > 0) diff --git a/web-app/src/pages/CategoryPage.vue b/web-app/src/pages/CategoryPage.vue new file mode 100644 index 0000000..7ec2b4d --- /dev/null +++ b/web-app/src/pages/CategoryPage.vue @@ -0,0 +1,118 @@ + + + diff --git a/web-app/src/router/index.ts b/web-app/src/router/index.ts index f1caea1..e319936 100644 --- a/web-app/src/router/index.ts +++ b/web-app/src/router/index.ts @@ -92,6 +92,11 @@ const router = createRouter({ component: () => import('@/pages/CategoriesPage.vue'), meta: { title: 'Categories' }, }, + { + path: 'categories/:id', + component: () => import('@/pages/CategoryPage.vue'), + meta: { title: 'Category' }, + }, ], }, ],