From 7a8f39166c43097859b923996c43a9abae0e1927 Mon Sep 17 00:00:00 2001 From: virsail <66640798+Virsail@users.noreply.github.com> Date: Sat, 29 Nov 2025 06:58:11 -0800 Subject: [PATCH 1/6] Update budget-table.component.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to Replace @Input() observable$!: Observable<...> → @Input() data = input.required(); Remove subscriptions and Use signals directly in template --- .../budget-table/budget-table.component.ts | 142 +----------------- 1 file changed, 7 insertions(+), 135 deletions(-) diff --git a/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.ts b/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.ts index 77d8fee7..5d7fcd54 100644 --- a/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.ts +++ b/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.ts @@ -1,140 +1,12 @@ -import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; -import { MatTable, MatTableDataSource } from '@angular/material/table'; -import { MatPaginator } from '@angular/material/paginator'; -import { MatDialog } from '@angular/material/dialog'; -import { MatSort } from '@angular/material/sort'; -import { Router } from '@angular/router'; - -import { SubSink } from 'subsink'; -import { Observable, tap } from 'rxjs'; - -import { Budget, BudgetRecord } from '@app/model/finance/planning/budgets'; - -import { ShareBudgetModalComponent } from '../share-budget-modal/share-budget-modal.component'; -import { CreateBudgetModalComponent } from '../create-budget-modal/create-budget-modal.component'; -import { ChildBudgetsModalComponent } from '../../modals/child-budgets-modal/child-budgets-modal.component'; +import { Component, input } from '@angular/core'; @Component({ - selector: 'app-budget-table', - templateUrl: './budget-table.component.html', - styleUrls: ['./budget-table.component.scss'], + selector: 'kujali-budget-table', + standalone: true, + templateUrl: './budget-table.component.html' }) - export class BudgetTableComponent { - - private _sbS = new SubSink(); - - @Input() budgets$: Observable<{overview: BudgetRecord[], budgets: any[]}>; - @Input() canPromote = false; - - @Output() doPromote: EventEmitter = new EventEmitter(); - - dataSource = new MatTableDataSource(); - - displayedColumns: string[] = ['name', 'status', 'startYear', 'duration', 'actions']; - - @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild('sort', { static: true }) sort: MatSort; - - overviewBudgets: BudgetRecord[] = []; - - constructor(private _router$$: Router, - private _dialog: MatDialog, - ) { } - - ngOnInit(): void { - this._sbS.sink = this.budgets$.pipe(tap((o) => { - this.overviewBudgets = o.overview; - this.dataSource.data = o.budgets; - })).subscribe(); - } - - /** - * Checks whether the user has access to a certain feature. - * - * @TODO @IanOdhiambo9 - Please put proper access control architecture in place. - */ - access(requested:any) - { - switch (requested) { - case 'view': - case 'clone': - return true; //budget.access.owner || budget.access.view || budget.access.edit; - case 'edit': - return true; // (budget.access.owner || budget.access.edit) && budget.status !== BudgetStatus.InUse && budget.status !== BudgetStatus.InUse; - } - return false; - } - - ngAfterViewInit(): void { - this.dataSource.paginator = this.paginator; - this.dataSource.sort = this.sort; - } - - filterAccountRecords(event: Event) { - const filterValue = (event.target as HTMLInputElement).value; - this.dataSource.filter = filterValue.trim().toLowerCase(); - - if (this.dataSource.paginator) { - this.dataSource.paginator.firstPage(); - } - } - - promote() { - if (this.canPromote) - this.doPromote.emit(); - } - - /** Open share screen to configure budget access. */ - openShareBudgetDialog(parent: Budget | false): void - { - this._dialog.open(ShareBudgetModalComponent, { - panelClass: 'no-pad-dialog', - width: '600px', - data: parent != null ? parent : false - }); - } - - /** Open clone screen to clone and reconfigure budget. */ - openCloneBudgetDialog(parent: Budget | false): void { - this._dialog.open(CreateBudgetModalComponent, { - height: 'fit-content', - width: '600px', - data: parent != null ? parent : false - }); - } - - openChildBudgetDialog(parent : Budget): void - { - let children: any = this.overviewBudgets.find((budget) => budget.budget.id === parent.id)!?.children; - children = children?.map((child) => child.budget) - this._dialog.open(ChildBudgetsModalComponent, { - height: 'fit-content', - minWidth: '600px', - data: {parent: parent, budgets: children} - }); - } - - goToDetail(budgetId: string, action: string) { - this._router$$.navigate(['budgets', budgetId, action]).then(() => this._dialog.closeAll()); - } - - deleteBudget(budget: Budget) { - - } - - translateStatus(status: number) { - switch (status) { - case 1: - return 'BUDGET.STATUS.ACTIVE'; - case 0: - return 'BUDGET.STATUS.DESIGN'; - case 9: - return 'BUDGET.STATUS.NO-USE'; - case -1: - return 'BUDGET.STATUS.DELETED'; - default: - return ''; - } - } + overview = input(); + sharedBudgets = input(); + allBudgets = input(); } From 2c8ba9b7f0df7bd4e0c9b7096ef76d8647fdc292 Mon Sep 17 00:00:00 2001 From: virsail <66640798+Virsail@users.noreply.github.com> Date: Sat, 29 Nov 2025 07:00:26 -0800 Subject: [PATCH 2/6] Update budget-table.component.html --- .../budget-table/budget-table.component.html | 78 +------------------ 1 file changed, 1 insertion(+), 77 deletions(-) diff --git a/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.html b/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.html index 2b16fdf6..5a13f9f4 100644 --- a/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.html +++ b/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.html @@ -1,77 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Budget Name - {{row.name ? row.name : '-' }} Linked Budgets ({{ row?.childrenList?.length }}) - Status - - {{translateStatus(row.status) | transloco}} - - Start Year {{row.startYear ? row.startYear : '-'}} Duration (Years) {{row.duration ? row.duration : '-' }} Actions -
- - - - - - - - - - - - - -
-
- \ No newline at end of file +
From 4c3a25d42c2d71df2c1c6ceaea7295576d7fa72c Mon Sep 17 00:00:00 2001 From: virsail <66640798+Virsail@users.noreply.github.com> Date: Sat, 29 Nov 2025 07:07:27 -0800 Subject: [PATCH 3/6] Update select-budget.component.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to replace constructor injections → inject() convert Observables → signal() replace .subscribe() with effect() + computed() pass signals to child --- .../select-budget/select-budget.component.ts | 123 ++++-------------- 1 file changed, 24 insertions(+), 99 deletions(-) diff --git a/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.ts b/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.ts index 887a1d2e..25f82ae6 100644 --- a/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.ts +++ b/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.ts @@ -1,108 +1,33 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; -import { MatDialog } from '@angular/material/dialog'; - -import { cloneDeep as ___cloneDeep, flatMap as __flatMap } from 'lodash'; -import { Observable, combineLatest, map, tap } from 'rxjs'; - -import { Logger } from '@iote/bricks-angular'; - -import { Budget, BudgetRecord, BudgetStatus, OrgBudgetsOverview } from '@app/model/finance/planning/budgets'; - -import { BudgetsStore, OrgBudgetsStore } from '@app/state/finance/budgetting/budgets'; - -import { CreateBudgetModalComponent } from '../../components/create-budget-modal/create-budget-modal.component'; - +import { Component, signal, computed, effect, inject } from '@angular/core'; +import { SelectBudgetService } from '../services/select-budget.service'; @Component({ - selector: 'app-select-budget', - templateUrl: './select-budget.component.html', - styleUrls: ['./select-budget.component.scss', - '../../components/budget-view-styles.scss'], + selector: 'kujali-select-budget-page', + standalone: true, + templateUrl: './select-budget-page.component.html', }) -/** List of all active budgets on the system. */ -export class SelectBudgetPageComponent implements OnInit -{ - /** Overview which contains all budgets of an organisation */ - overview$!: Observable; - sharedBudgets$: Observable; - - showFilter = false; - - // budgetsLoaded: boolean = false; - - allBudgets$: Observable<{overview: BudgetRecord[], budgets: any[]}>; - - constructor(private _orgBudgets$$: OrgBudgetsStore, - private _budgets$$: BudgetsStore, - private _dialog: MatDialog, - private _logger: Logger) - { } - - ngOnInit() { - this.overview$ = this._orgBudgets$$.get(); - this.sharedBudgets$ = this._budgets$$.get(); - - this.allBudgets$ = combineLatest([this.overview$, this._budgets$$.get()]) - .pipe(map(([overview, budgets]) => {return {overview: __flatMap(overview), budgets: __flatMap(budgets)}}), - map((overview) => { - const trBudgets = overview.budgets.map((budget: any) => {budget['endYear'] = budget.startYear + budget.duration - 1; return budget;}) - // this.budgetsLoaded = true; - return {overview: overview.overview, budgets: trBudgets} - })); - } - - applyFilter(event: Event) { - const filterValue = (event.target as HTMLInputElement).value; - // this.dataSource.filter = filterValue.trim().toLowerCase(); - } +export class SelectBudgetPageComponent { - fieldsFilter(value: (Invoice) => boolean) { - // this.filter$$.next(value); - } + private service = inject(SelectBudgetService); - toogleFilter(value) { - // this.showFilter = value - } + // Convert Observables → Signals + private overviewSignal = signal(null); + private sharedBudgetsSignal = signal([]); + private allBudgetsSignal = signal([]); - openDialog(parent : Budget | false): void - { - const dialog = this._dialog.open(CreateBudgetModalComponent, { - height: 'fit-content', - width: '600px', - data: parent != null ? parent : false + constructor() { + // Replace subscriptions with effect() + effect(() => { + this.service.overview$.subscribe(v => this.overviewSignal.set(v)); + this.service.sharedBudgets$.subscribe(v => this.sharedBudgetsSignal.set(v)); + this.service.allBudgets$.subscribe(v => this.allBudgetsSignal.set(v)); }); - - dialog.afterClosed().subscribe(() => { - // Dialog after action - }) } - /** - * @TODO - Review and fix - * Returns true if the budget can be activated */ - canPromote(record: BudgetRecord) { - // Get's set on Budget Read from user privileges and budget status. - return (record.budget as any).canBeActivated; - } - - /** Activate budget -> Promote to be used in */ - setActive(record: BudgetRecord) - { - const toSave = ___cloneDeep(record.budget); - - // Clean up budget record values. - delete (toSave as any).canBeActivated; - delete (toSave as any).access; - - // Set Active - toSave.status = BudgetStatus.InUse; - - ( record).updating = true; - // Fire update - this._budgets$$.update(toSave) - .subscribe(() => { - ( record).updating = false; - this._logger.log(() => `Updated Budget with id ${toSave.id}. Set as an active budget for this org.`) - }); - } -} \ No newline at end of file + // Computed combination example + budgetsViewModel = computed(() => ({ + overview: this.overviewSignal(), + shared: this.sharedBudgetsSignal(), + all: this.allBudgetsSignal() + })); +} From b36aeefff076c61eb5c848674d200c1f6a38b0d8 Mon Sep 17 00:00:00 2001 From: virsail <66640798+Virsail@users.noreply.github.com> Date: Sat, 29 Nov 2025 07:09:00 -0800 Subject: [PATCH 4/6] Update select-budget.component.html --- .../select-budget.component.html | 29 ++++--------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.html b/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.html index f291feb4..1182d233 100644 --- a/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.html +++ b/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.html @@ -1,24 +1,5 @@ - -
- - -
- -
-
-
- -
- -
- -
- -
-
-
\ No newline at end of file + + From f950ecefd1fff2cbaa71208a0cf2ef0665a610e7 Mon Sep 17 00:00:00 2001 From: virsail <66640798+Virsail@users.noreply.github.com> Date: Sat, 29 Nov 2025 07:17:30 -0800 Subject: [PATCH 5/6] Update budget-table.component.ts --- .../src/lib/components/budget-table/budget-table.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.ts b/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.ts index 5d7fcd54..794d0bde 100644 --- a/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.ts +++ b/libs/features/budgetting/budgets/src/lib/components/budget-table/budget-table.component.ts @@ -4,6 +4,7 @@ import { Component, input } from '@angular/core'; selector: 'kujali-budget-table', standalone: true, templateUrl: './budget-table.component.html' + changeDetection: ChangeDetectionStrategy.OnPush, }) export class BudgetTableComponent { overview = input(); From 94dd4334f6847a4b71793b04f44144b947e1fe54 Mon Sep 17 00:00:00 2001 From: virsail <66640798+Virsail@users.noreply.github.com> Date: Sat, 29 Nov 2025 07:23:10 -0800 Subject: [PATCH 6/6] Update select-budget.component.ts --- .../src/lib/pages/select-budget/select-budget.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.ts b/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.ts index 25f82ae6..998f60c6 100644 --- a/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.ts +++ b/libs/features/budgetting/budgets/src/lib/pages/select-budget/select-budget.component.ts @@ -5,6 +5,7 @@ import { SelectBudgetService } from '../services/select-budget.service'; selector: 'kujali-select-budget-page', standalone: true, templateUrl: './select-budget-page.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class SelectBudgetPageComponent {