From 9a03d26c989047d63033f0f84af809faa4b58190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Nagy?= Date: Sun, 12 Apr 2026 01:13:17 +0200 Subject: [PATCH] #EX-378: Add balance column and table headers to category-list --- .../modules/category/CategoryGet.ts | 1 + .../sessions-list.component.html | 45 +------------------ .../category-list.component.html | 14 +++++- .../category-list/category-list.component.ts | 22 ++++++--- .../chart-widget/data/chart-widget.data.ts | 3 +- .../data/create-transaction-dialog.data.ts | 20 +++++++-- .../unit/stores/tests/category.store.spec.ts | 21 +++++++++ 7 files changed, 71 insertions(+), 55 deletions(-) diff --git a/frontend/Exence/src/app/data-model/modules/category/CategoryGet.ts b/frontend/Exence/src/app/data-model/modules/category/CategoryGet.ts index 5d7b027e..fef78391 100644 --- a/frontend/Exence/src/app/data-model/modules/category/CategoryGet.ts +++ b/frontend/Exence/src/app/data-model/modules/category/CategoryGet.ts @@ -8,4 +8,5 @@ export interface CategoryGet { color: string; type: CategoryType; note?: string; + balance: number; } diff --git a/frontend/Exence/src/app/private/session/sessions-list/sessions-list.component.html b/frontend/Exence/src/app/private/session/sessions-list/sessions-list.component.html index 6d9b022a..66c0a470 100644 --- a/frontend/Exence/src/app/private/session/sessions-list/sessions-list.component.html +++ b/frontend/Exence/src/app/private/session/sessions-list/sessions-list.component.html @@ -6,9 +6,8 @@

{{ 'sessionList.thisSession' | translate }} desktop_windows
- Windows 11 - YourPC - + {{ currSession?.operatingSystem }} - + {{ currSession?.deviceName }}
{{ 'sessionList.lastUsed' | translate }} @@ -64,46 +63,6 @@

{{ 'sessionList.otherSessions' | translate

} - - -

{{ 'sessionList.otherSessions' | translate }}

-
  • - desktop_windows -
    -
    Arch Linux - LaptopARCH
    -
    - {{ 'sessionList.lastUsed' | translate }} - 3 days ago -
    -
    - -
  • - -
  • - desktop_windows -
    -
    IOS iPhone - Tamás iPhone-ja
    -
    - {{ 'sessionList.lastUsed' | translate }} - 1 month ago -
    -
    - -
  • - -
    - - {{ 'sessionList.deleteAll' | translate }} - -
    diff --git a/frontend/Exence/src/app/private/transactions-and-categories/category-list/category-list.component.html b/frontend/Exence/src/app/private/transactions-and-categories/category-list/category-list.component.html index 2b2f2127..67ceb4be 100644 --- a/frontend/Exence/src/app/private/transactions-and-categories/category-list/category-list.component.html +++ b/frontend/Exence/src/app/private/transactions-and-categories/category-list/category-list.component.html @@ -1,5 +1,5 @@ - +
    {{ row.name }}
    @@ -22,4 +22,14 @@ {{ codeForCategoryType(row.type) | translate }} + + + + {{ row.balance | currency }} + +
    diff --git a/frontend/Exence/src/app/private/transactions-and-categories/category-list/category-list.component.ts b/frontend/Exence/src/app/private/transactions-and-categories/category-list/category-list.component.ts index 437be297..83984616 100644 --- a/frontend/Exence/src/app/private/transactions-and-categories/category-list/category-list.component.ts +++ b/frontend/Exence/src/app/private/transactions-and-categories/category-list/category-list.component.ts @@ -1,4 +1,5 @@ import { Component, computed, inject, input } from '@angular/core'; +import { TranslocoService } from '@jsverse/transloco'; import { CategoryGet } from '../../../data-model/modules/category/CategoryGet'; import { CategoryCreate } from '../../../data-model/modules/category/CategoryCreate'; import { CategoryType } from '../../../data-model/modules/category/CategoryType'; @@ -9,6 +10,7 @@ import { DialogService } from '../../../shared/dialog/dialog.service'; import { MatIconModule } from '@angular/material/icon'; import { TranslatePipe } from '../../../shared/pipes/translate.pipe'; import { TranslationCode } from '../../../shared/i18n/translation-types'; +import { CurrencyPipe } from '../../../shared/pipes/currency.pipe'; import { CreateCategoryDialogComponent } from '../create-category-dialog/create-category-dialog.component'; import { PagedResponse } from '../../../data-model/modules/common/PagedResponse'; @@ -16,21 +18,23 @@ import { PagedResponse } from '../../../data-model/modules/common/PagedResponse' selector: 'ex-category-list', templateUrl: './category-list.component.html', styleUrl: './category-list.component.scss', - imports: [DataTableComponent, ExCellDirective, MatIconModule, TranslatePipe], + imports: [DataTableComponent, ExCellDirective, MatIconModule, TranslatePipe, CurrencyPipe], }) export class CategoryListComponent { private readonly categoryStore = inject(CategoryStore); private readonly dialog = inject(DialogService); + private readonly translocoService = inject(TranslocoService); title = input(''); matIcon = input(); - columns: ColumnDef[] = [ - { key: 'name', header: '', width: '40%' }, - { key: 'icon', header: '', width: '60px' }, - { key: 'type', header: '', width: '100px' }, + columns = computed(() => [ + { key: 'title', header: this.translocoService.translate('dataTable.title'), width: '40%' }, + { key: 'icon', header: this.translocoService.translate('dataTable.icon'), width: '60px' }, + { key: 'type', header: this.translocoService.translate('dataTable.type'), width: '100px' }, + { key: 'amount', header: this.translocoService.translate('literals.balance'), width: '120px' }, { key: 'actions', header: '', width: '48px' }, - ]; + ]); actions: TableAction[] = [ { @@ -62,6 +66,12 @@ export class CategoryListComponent { return `categoryType.${type}`; } + amountClass(row: CategoryGet): 'income' | 'expense' | null { + if (row.type === CategoryType.INCOME) return 'income'; + if (row.type === CategoryType.EXPENSE) return 'expense'; + return row.balance > 0 ? 'income' : row.balance < 0 ? 'expense' : null; + } + async openCreate(): Promise { const result = await this.dialog.openNonModal(CreateCategoryDialogComponent, undefined); if (!result) return; diff --git a/frontend/Exence/src/integration/chart-widget/data/chart-widget.data.ts b/frontend/Exence/src/integration/chart-widget/data/chart-widget.data.ts index da3a5c8c..abd0d604 100644 --- a/frontend/Exence/src/integration/chart-widget/data/chart-widget.data.ts +++ b/frontend/Exence/src/integration/chart-widget/data/chart-widget.data.ts @@ -1,6 +1,7 @@ import { ChartWidget } from '../../../app/data-model/modules/statistics/ChartWidget'; import { Timeframe } from '../../../app/data-model/modules/statistics/Timeframe'; import { WidgetType } from '../../../app/data-model/modules/statistics/widget-config.model'; +import { SeriesPayload } from '../../../app/data-model/modules/statistics/WidgetDataPayload'; export const MOCK_BAR_WIDGET: ChartWidget = { id: 1, @@ -46,4 +47,4 @@ export const MOCK_HEATMAP_WIDGET: ChartWidget = { rows: 2, }; -export const MOCK_PAYLOAD = { series: [] } as unknown; +export const MOCK_PAYLOAD: SeriesPayload = { series: [] }; diff --git a/frontend/Exence/src/integration/create-transaction-dialog/data/create-transaction-dialog.data.ts b/frontend/Exence/src/integration/create-transaction-dialog/data/create-transaction-dialog.data.ts index cf80ffea..f7fb3096 100644 --- a/frontend/Exence/src/integration/create-transaction-dialog/data/create-transaction-dialog.data.ts +++ b/frontend/Exence/src/integration/create-transaction-dialog/data/create-transaction-dialog.data.ts @@ -3,7 +3,21 @@ import { MaterialIcon } from '../../../app/data-model/modules/category/MaterialI import { CategoryType } from '../../../app/data-model/modules/category/CategoryType'; export const MOCK_CATEGORIES: CategoryGet[] = [ - { id: 1, name: 'Groceries', type: CategoryType.EXPENSE, icon: MaterialIcon.LOCAL_GROCERY_STORE, color: '#ff0000' }, - { id: 2, name: 'Salary', type: CategoryType.INCOME, icon: MaterialIcon.WORK, color: '#00ff00' }, - { id: 3, name: 'Shared', type: CategoryType.MIXED, icon: MaterialIcon.ACCOUNT_BALANCE, color: '#0000ff' }, + { + id: 1, + name: 'Groceries', + type: CategoryType.EXPENSE, + icon: MaterialIcon.LOCAL_GROCERY_STORE, + color: '#ff0000', + balance: -150, + }, + { id: 2, name: 'Salary', type: CategoryType.INCOME, icon: MaterialIcon.WORK, color: '#00ff00', balance: 3000 }, + { + id: 3, + name: 'Shared', + type: CategoryType.MIXED, + icon: MaterialIcon.ACCOUNT_BALANCE, + color: '#0000ff', + balance: 0, + }, ]; diff --git a/frontend/Exence/src/unit/stores/tests/category.store.spec.ts b/frontend/Exence/src/unit/stores/tests/category.store.spec.ts index 9673f54a..3ebabfdd 100644 --- a/frontend/Exence/src/unit/stores/tests/category.store.spec.ts +++ b/frontend/Exence/src/unit/stores/tests/category.store.spec.ts @@ -18,6 +18,7 @@ const mockCategory: CategoryGet = { icon: MaterialIcon.SHOPPING_CART, color: '#FF5733', type: CategoryType.EXPENSE, + balance: -200, }; const mockTopCategories: Record = { @@ -106,6 +107,26 @@ describe('CategoryStore', () => { }); }); + // categoryResource + describe('categoryResource', () => { + async function waitForResource(): Promise { + await new Promise(resolve => setTimeout(resolve, 0)); + TestBed.flushEffects(); + } + + it('loads categories including balance from the service', async () => { + store.categoryResource.reload(); + await waitForResource(); + expect(store.categoryResource.value()).toEqual([mockCategory]); + }); + + it('exposes the balance on each loaded category', async () => { + store.categoryResource.reload(); + await waitForResource(); + expect(store.categoryResource.value()?.[0].balance).toBe(-200); + }); + }); + // deleteCategory describe('deleteCategory', () => { it('calls categoryService.delete with the category id', async () => {