Browse Source

Merge remote-tracking branch 'origin/main' into feature/enable-strict-null-checks-in-ui

pull/6264/head
KenTandrian 1 day ago
parent
commit
cae6dab390
  1. 5
      CHANGELOG.md
  2. 36
      libs/ui/src/lib/activities-table/activities-table.component.html
  3. 138
      libs/ui/src/lib/activities-table/activities-table.component.ts
  4. 581
      package-lock.json
  5. 24
      package.json

5
CHANGELOG.md

@ -5,12 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased ## 2.237.0 - 2026-02-08
### Changed ### Changed
- Removed the `transactionCount` in the portfolio calculator and service - Removed the deprecated `transactionCount` in the portfolio calculator and service
- Refreshed the cryptocurrencies list - Refreshed the cryptocurrencies list
- Upgraded `Nx` from version `22.4.1` to `22.4.5`
### Fixed ### Fixed

36
libs/ui/src/lib/activities-table/activities-table.component.html

@ -21,7 +21,7 @@
<mat-menu #activitiesMenu="matMenu" class="no-max-width" xPosition="before"> <mat-menu #activitiesMenu="matMenu" class="no-max-width" xPosition="before">
<button <button
mat-menu-item mat-menu-item
[disabled]="dataSource?.data.length === 0" [disabled]="dataSource()?.data.length === 0"
(click)="onImportDividends()" (click)="onImportDividends()"
> >
<span class="align-items-center d-flex"> <span class="align-items-center d-flex">
@ -33,7 +33,7 @@
<button <button
class="align-items-center d-flex" class="align-items-center d-flex"
mat-menu-item mat-menu-item
[disabled]="dataSource?.data.length === 0" [disabled]="dataSource()?.data.length === 0"
(click)="onExport()" (click)="onExport()"
> >
<span class="align-items-center d-flex"> <span class="align-items-center d-flex">
@ -60,7 +60,7 @@
class="align-items-center d-flex" class="align-items-center d-flex"
mat-menu-item mat-menu-item
[disabled]=" [disabled]="
dataSource?.data.length === 0 || !hasPermissionToDeleteActivity dataSource()?.data.length === 0 || !hasPermissionToDeleteActivity
" "
(click)="onDeleteActivities()" (click)="onDeleteActivities()"
> >
@ -78,7 +78,7 @@
class="gf-table w-100" class="gf-table w-100"
mat-table mat-table
matSort matSort
[dataSource]="dataSource" [dataSource]="dataSource()"
[matSortActive]="sortColumn" [matSortActive]="sortColumn"
[matSortDirection]="sortDirection" [matSortDirection]="sortDirection"
[matSortDisabled]="sortDisabled" [matSortDisabled]="sortDisabled"
@ -177,7 +177,7 @@
[deviceType]="deviceType" [deviceType]="deviceType"
[isDate]="true" [isDate]="true"
[locale]="locale" [locale]="locale"
[value]="isLoading ? undefined : element.date" [value]="isLoading() ? undefined : element.date"
/> />
</div> </div>
</td> </td>
@ -201,7 +201,7 @@
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[locale]="locale" [locale]="locale"
[value]="isLoading ? undefined : element.quantity" [value]="isLoading() ? undefined : element.quantity"
/> />
</div> </div>
</td> </td>
@ -225,7 +225,7 @@
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[locale]="locale" [locale]="locale"
[value]="isLoading ? undefined : element.unitPrice" [value]="isLoading() ? undefined : element.unitPrice"
/> />
</div> </div>
</td> </td>
@ -249,7 +249,7 @@
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[locale]="locale" [locale]="locale"
[value]="isLoading ? undefined : element.fee" [value]="isLoading() ? undefined : element.fee"
/> />
</div> </div>
</td> </td>
@ -272,7 +272,7 @@
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[locale]="locale" [locale]="locale"
[value]="isLoading ? undefined : element.value" [value]="isLoading() ? undefined : element.value"
/> />
</div> </div>
</td> </td>
@ -304,7 +304,7 @@
<gf-value <gf-value
[isCurrency]="true" [isCurrency]="true"
[locale]="locale" [locale]="locale"
[value]="isLoading ? undefined : element.valueInBaseCurrency" [value]="isLoading() ? undefined : element.valueInBaseCurrency"
/> />
</div> </div>
</td> </td>
@ -388,7 +388,7 @@
@if (hasPermissionToCreateActivity) { @if (hasPermissionToCreateActivity) {
<button <button
mat-menu-item mat-menu-item
[disabled]="dataSource?.data.length === 0" [disabled]="dataSource()?.data.length === 0"
(click)="onImportDividends()" (click)="onImportDividends()"
> >
<span class="align-items-center d-flex"> <span class="align-items-center d-flex">
@ -403,7 +403,7 @@
<button <button
class="align-items-center d-flex" class="align-items-center d-flex"
mat-menu-item mat-menu-item
[disabled]="dataSource?.data.length === 0" [disabled]="dataSource()?.data.length === 0"
(click)="onExport()" (click)="onExport()"
> >
<span class="align-items-center d-flex"> <span class="align-items-center d-flex">
@ -488,9 +488,9 @@
</td> </td>
</ng-container> </ng-container>
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr> <tr *matHeaderRowDef="displayedColumns()" mat-header-row></tr>
<tr <tr
*matRowDef="let row; columns: displayedColumns" *matRowDef="let row; columns: displayedColumns()"
mat-row mat-row
[ngClass]="{ [ngClass]="{
'cursor-pointer': canClickActivity(row) 'cursor-pointer': canClickActivity(row)
@ -500,7 +500,7 @@
</table> </table>
</div> </div>
@if (isLoading) { @if (isLoading()) {
<ngx-skeleton-loader <ngx-skeleton-loader
animation="pulse" animation="pulse"
class="px-4 py-3" class="px-4 py-3"
@ -514,7 +514,7 @@
<mat-paginator <mat-paginator
[length]="totalItems" [length]="totalItems"
[ngClass]="{ [ngClass]="{
'd-none': (isLoading && !totalItems) || totalItems <= pageSize 'd-none': (isLoading() && !totalItems) || totalItems <= pageSize
}" }"
[pageIndex]="pageIndex" [pageIndex]="pageIndex"
[pageSize]="pageSize" [pageSize]="pageSize"
@ -524,9 +524,9 @@
@if ( @if (
!hasActivities && !hasActivities &&
dataSource?.data.length === 0 && dataSource()?.data.length === 0 &&
hasPermissionToCreateActivity && hasPermissionToCreateActivity &&
!isLoading !isLoading()
) { ) {
<div class="p-3 text-center"> <div class="p-3 text-center">
<gf-no-transactions-info-indicator [hasBorder]="false" /> <gf-no-transactions-info-indicator [hasBorder]="false" />

138
libs/ui/src/lib/activities-table/activities-table.component.ts

@ -21,11 +21,13 @@ import {
Component, Component,
EventEmitter, EventEmitter,
Input, Input,
OnChanges,
OnDestroy, OnDestroy,
OnInit, OnInit,
Output, Output,
ViewChild ViewChild,
computed,
inject,
input
} from '@angular/core'; } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
@ -45,7 +47,6 @@ import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { isUUID } from 'class-validator'; import { isUUID } from 'class-validator';
import { endOfToday, isAfter } from 'date-fns';
import { addIcons } from 'ionicons'; import { addIcons } from 'ionicons';
import { import {
alertCircleOutline, alertCircleOutline,
@ -62,7 +63,7 @@ import {
trashOutline trashOutline
} from 'ionicons/icons'; } from 'ionicons/icons';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject, Subscription, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
import { GfActivityTypeComponent } from '../activity-type/activity-type.component'; import { GfActivityTypeComponent } from '../activity-type/activity-type.component';
import { GfEntityLogoComponent } from '../entity-logo/entity-logo.component'; import { GfEntityLogoComponent } from '../entity-logo/entity-logo.component';
@ -94,10 +95,9 @@ import { GfValueComponent } from '../value/value.component';
templateUrl: './activities-table.component.html' templateUrl: './activities-table.component.html'
}) })
export class GfActivitiesTableComponent export class GfActivitiesTableComponent
implements AfterViewInit, OnChanges, OnDestroy, OnInit implements AfterViewInit, OnDestroy, OnInit
{ {
@Input() baseCurrency: string; @Input() baseCurrency: string;
@Input() dataSource: MatTableDataSource<Activity>;
@Input() deviceType: string; @Input() deviceType: string;
@Input() hasActivities: boolean; @Input() hasActivities: boolean;
@Input() hasPermissionToCreateActivity: boolean; @Input() hasPermissionToCreateActivity: boolean;
@ -107,10 +107,7 @@ export class GfActivitiesTableComponent
@Input() locale = getLocale(); @Input() locale = getLocale();
@Input() pageIndex: number; @Input() pageIndex: number;
@Input() pageSize = DEFAULT_PAGE_SIZE; @Input() pageSize = DEFAULT_PAGE_SIZE;
@Input() showAccountColumn = true;
@Input() showActions = true; @Input() showActions = true;
@Input() showCheckbox = false;
@Input() showNameColumn = true;
@Input() sortColumn: string; @Input() sortColumn: string;
@Input() sortDirection: SortDirection; @Input() sortDirection: SortDirection;
@Input() sortDisabled = false; @Input() sortDisabled = false;
@ -132,19 +129,66 @@ export class GfActivitiesTableComponent
@ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
public displayedColumns: string[] = [];
public endOfToday = endOfToday();
public hasDrafts = false; public hasDrafts = false;
public hasErrors = false; public hasErrors = false;
public isAfter = isAfter;
public isLoading = true;
public isUUID = isUUID; public isUUID = isUUID;
public routeQueryParams: Subscription;
public selectedRows = new SelectionModel<Activity>(true, []); public selectedRows = new SelectionModel<Activity>(true, []);
private unsubscribeSubject = new Subject<void>(); public readonly dataSource = input.required<
MatTableDataSource<Activity> | undefined
>();
public readonly showAccountColumn = input(true);
public readonly showCheckbox = input(false);
public readonly showNameColumn = input(true);
public constructor(private notificationService: NotificationService) { protected readonly displayedColumns = computed(() => {
let columns = [
'select',
'importStatus',
'icon',
'nameWithSymbol',
'type',
'date',
'quantity',
'unitPrice',
'fee',
'value',
'currency',
'valueInBaseCurrency',
'account',
'comment',
'actions'
];
if (!this.showAccountColumn()) {
columns = columns.filter((column) => {
return column !== 'account';
});
}
if (!this.showCheckbox()) {
columns = columns.filter((column) => {
return column !== 'importStatus' && column !== 'select';
});
}
if (!this.showNameColumn()) {
columns = columns.filter((column) => {
return column !== 'nameWithSymbol';
});
}
return columns;
});
protected readonly isLoading = computed(() => {
return !this.dataSource();
});
private readonly notificationService = inject(NotificationService);
private readonly unsubscribeSubject = new Subject<void>();
public constructor() {
addIcons({ addIcons({
alertCircleOutline, alertCircleOutline,
calendarClearOutline, calendarClearOutline,
@ -162,7 +206,7 @@ export class GfActivitiesTableComponent
} }
public ngOnInit() { public ngOnInit() {
if (this.showCheckbox) { if (this.showCheckbox()) {
this.toggleAllRows(); this.toggleAllRows();
this.selectedRows.changed this.selectedRows.changed
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
@ -173,8 +217,10 @@ export class GfActivitiesTableComponent
} }
public ngAfterViewInit() { public ngAfterViewInit() {
if (this.dataSource) { const dataSource = this.dataSource();
this.dataSource.paginator = this.paginator;
if (dataSource) {
dataSource.paginator = this.paginator;
} }
this.sort.sortChange.subscribe((value: Sort) => { this.sort.sortChange.subscribe((value: Sort) => {
@ -182,51 +228,9 @@ export class GfActivitiesTableComponent
}); });
} }
public ngOnChanges() {
this.displayedColumns = [
'select',
'importStatus',
'icon',
'nameWithSymbol',
'type',
'date',
'quantity',
'unitPrice',
'fee',
'value',
'currency',
'valueInBaseCurrency',
'account',
'comment',
'actions'
];
if (!this.showAccountColumn) {
this.displayedColumns = this.displayedColumns.filter((column) => {
return column !== 'account';
});
}
if (!this.showCheckbox) {
this.displayedColumns = this.displayedColumns.filter((column) => {
return column !== 'importStatus' && column !== 'select';
});
}
if (!this.showNameColumn) {
this.displayedColumns = this.displayedColumns.filter((column) => {
return column !== 'nameWithSymbol';
});
}
if (this.dataSource) {
this.isLoading = false;
}
}
public areAllRowsSelected() { public areAllRowsSelected() {
const numSelectedRows = this.selectedRows.selected.length; const numSelectedRows = this.selectedRows.selected.length;
const numTotalRows = this.dataSource.data.length; const numTotalRows = this.dataSource()?.data.length;
return numSelectedRows === numTotalRows; return numSelectedRows === numTotalRows;
} }
@ -241,7 +245,7 @@ export class GfActivitiesTableComponent
public isExcludedFromAnalysis(activity: Activity) { public isExcludedFromAnalysis(activity: Activity) {
return ( return (
activity.account?.isExcluded || activity.account?.isExcluded ??
activity.tags?.some(({ id }) => { activity.tags?.some(({ id }) => {
return id === TAG_ID_EXCLUDE_FROM_ANALYSIS; return id === TAG_ID_EXCLUDE_FROM_ANALYSIS;
}) })
@ -253,7 +257,7 @@ export class GfActivitiesTableComponent
} }
public onClickActivity(activity: Activity) { public onClickActivity(activity: Activity) {
if (this.showCheckbox) { if (this.showCheckbox()) {
if (!activity.error) { if (!activity.error) {
this.selectedRows.toggle(activity); this.selectedRows.toggle(activity);
} }
@ -299,8 +303,8 @@ export class GfActivitiesTableComponent
public onExportDrafts() { public onExportDrafts() {
this.exportDrafts.emit( this.exportDrafts.emit(
this.dataSource.filteredData this.dataSource()
.filter((activity) => { ?.filteredData.filter((activity) => {
return activity.isDraft; return activity.isDraft;
}) })
.map((activity) => { .map((activity) => {
@ -331,7 +335,7 @@ export class GfActivitiesTableComponent
if (this.areAllRowsSelected()) { if (this.areAllRowsSelected()) {
this.selectedRows.clear(); this.selectedRows.clear();
} else { } else {
this.dataSource.data.forEach((row) => { this.dataSource()?.data.forEach((row) => {
this.selectedRows.select(row); this.selectedRows.select(row);
}); });
} }

581
package-lock.json

File diff suppressed because it is too large

24
package.json

@ -1,6 +1,6 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.236.0", "version": "2.237.0",
"homepage": "https://ghostfol.io", "homepage": "https://ghostfol.io",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio", "repository": "https://github.com/ghostfolio/ghostfolio",
@ -152,16 +152,16 @@
"@eslint/js": "9.35.0", "@eslint/js": "9.35.0",
"@nestjs/schematics": "11.0.9", "@nestjs/schematics": "11.0.9",
"@nestjs/testing": "11.1.8", "@nestjs/testing": "11.1.8",
"@nx/angular": "22.4.1", "@nx/angular": "22.4.5",
"@nx/eslint-plugin": "22.4.1", "@nx/eslint-plugin": "22.4.5",
"@nx/jest": "22.4.1", "@nx/jest": "22.4.5",
"@nx/js": "22.4.1", "@nx/js": "22.4.5",
"@nx/module-federation": "22.4.1", "@nx/module-federation": "22.4.5",
"@nx/nest": "22.4.1", "@nx/nest": "22.4.5",
"@nx/node": "22.4.1", "@nx/node": "22.4.5",
"@nx/storybook": "22.4.1", "@nx/storybook": "22.4.5",
"@nx/web": "22.4.1", "@nx/web": "22.4.5",
"@nx/workspace": "22.4.1", "@nx/workspace": "22.4.5",
"@schematics/angular": "21.1.1", "@schematics/angular": "21.1.1",
"@storybook/addon-docs": "10.1.10", "@storybook/addon-docs": "10.1.10",
"@storybook/angular": "10.1.10", "@storybook/angular": "10.1.10",
@ -186,7 +186,7 @@
"jest": "30.2.0", "jest": "30.2.0",
"jest-environment-jsdom": "30.2.0", "jest-environment-jsdom": "30.2.0",
"jest-preset-angular": "16.0.0", "jest-preset-angular": "16.0.0",
"nx": "22.4.1", "nx": "22.4.5",
"prettier": "3.8.1", "prettier": "3.8.1",
"prettier-plugin-organize-attributes": "1.0.0", "prettier-plugin-organize-attributes": "1.0.0",
"prisma": "6.19.0", "prisma": "6.19.0",

Loading…
Cancel
Save