Browse Source

Merge branch 'ghostfolio:main' into feature/move-accounts-table-to-ui

pull/5278/head
David Requeno 3 weeks ago
committed by GitHub
parent
commit
9b754d4b9a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 19
      CHANGELOG.md
  2. 6
      apps/api/src/app/portfolio/portfolio.controller.ts
  3. 8
      apps/api/src/app/portfolio/portfolio.service.ts
  4. 3
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  5. 2
      apps/client/src/app/components/admin-users/admin-users.component.ts
  6. 17
      apps/client/src/app/components/admin-users/admin-users.html
  7. 14
      apps/client/src/app/pages/landing/landing-page.html
  8. 25
      apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts
  9. 10
      apps/client/src/app/pages/portfolio/analysis/analysis-page.html
  10. 6
      apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts
  11. 3246
      apps/client/src/locales/messages.ca.xlf
  12. 3244
      apps/client/src/locales/messages.de.xlf
  13. 3318
      apps/client/src/locales/messages.es.xlf
  14. 3058
      apps/client/src/locales/messages.fr.xlf
  15. 3248
      apps/client/src/locales/messages.it.xlf
  16. 3254
      apps/client/src/locales/messages.nl.xlf
  17. 3352
      apps/client/src/locales/messages.pl.xlf
  18. 3060
      apps/client/src/locales/messages.pt.xlf
  19. 3384
      apps/client/src/locales/messages.tr.xlf
  20. 3222
      apps/client/src/locales/messages.uk.xlf
  21. 3163
      apps/client/src/locales/messages.xlf
  22. 3334
      apps/client/src/locales/messages.zh.xlf
  23. 2
      apps/client/src/styles.scss
  24. 80
      apps/client/src/styles/bootstrap.scss
  25. 2
      libs/common/src/lib/interfaces/responses/portfolio-report.interface.ts
  26. 50
      libs/ui/src/lib/membership-card/membership-card.component.stories.ts
  27. 33760
      package-lock.json
  28. 70
      package.json

19
CHANGELOG.md

@ -9,20 +9,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added a _Storybook_ story for the membership card component
### Changed
- Moved the support for changing the asset profile identifier (`dataSource` and `symbol`) in the asset profile details dialog of the admin control panel from experimental to general availability
- Improved the balance of headings on the landing page
- Improved the language localization for Spanish (`es`)
- Upgraded `angular` from version `20.0.7` to `20.1.3`
- Upgraded `Nx` from version `21.2.4` to `21.3.9`
## 2.186.0 - 2025-07-30
### Added
- Added the allocation column to the accounts table component of the holding detail dialog
### Changed
- Improved the _Top 3_ and _Bottom 3_ performers on the analysis page by removing items without performance
- Improved the usability of the toggle component
- Simplified the users table of the admin control panel
- Restructured the response of the portfolio report endpoint (_X-ray_)
- Refreshed the cryptocurrencies list
- Improved the language localization for Catalan (`ca`)
- Improved the language localization for Chinese (`zh`)
- Improved the language localization for Dutch (`nl`)
- Improved the language localization for German (`de`)
- Improved the language localization for Spanish (`es`)
- Upgraded `ng-extract-i18n-merge` from version `2.15.1` to `3.0.0`
### Fixed
- Fixed the links of the _Top 3_ and _Bottom 3_ performers on the analysis page
- Excluded the holdings originated of `FEE`, `INTEREST` and `LIABILITY` activities from the closed holdings on the portfolio holdings page
- Fixed an issue with serving _Storybook_ related to missing styles

6
apps/api/src/app/portfolio/portfolio.controller.ts

@ -655,11 +655,11 @@ export class PortfolioController {
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') &&
this.request.user.subscription.type === 'Basic'
) {
for (const rule in report['x-ray'].rules) {
report['x-ray'].rules[rule] = null;
for (const rule in report.xRay.rules) {
report.xRay.rules[rule] = null;
}
report['x-ray'].statistics = {
report.xRay.statistics = {
rulesActiveCount: 0,
rulesFulfilledCount: 0
};

8
apps/api/src/app/portfolio/portfolio.service.ts

@ -1157,7 +1157,7 @@ export class PortfolioService {
})
).toNumber();
const rules: PortfolioReportResponse['x-ray']['rules'] = {
const rules: PortfolioReportResponse['xRay']['rules'] = {
accountClusterRisk:
summary.activityCount > 0
? await this.rulesService.evaluate(
@ -1313,7 +1313,7 @@ export class PortfolioService {
};
return {
'x-ray': {
xRay: {
rules,
statistics: this.getReportStatistics(rules)
}
@ -1737,8 +1737,8 @@ export class PortfolioService {
}
private getReportStatistics(
evaluatedRules: PortfolioReportResponse['x-ray']['rules']
): PortfolioReportResponse['x-ray']['statistics'] {
evaluatedRules: PortfolioReportResponse['xRay']['rules']
): PortfolioReportResponse['xRay']['statistics'] {
const rulesActiveCount = Object.values(evaluatedRules)
.flat()
.filter((rule) => {

3
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -233,8 +233,7 @@ export class GfAssetProfileDialogComponent implements OnDestroy, OnInit {
public get canEditAssetProfileIdentifier() {
return (
this.assetProfile?.assetClass &&
!['MANUAL'].includes(this.assetProfile?.dataSource) &&
this.user?.settings?.isExperimentalFeatures
!['MANUAL'].includes(this.assetProfile?.dataSource)
);
}

2
apps/client/src/app/components/admin-users/admin-users.component.ts

@ -101,7 +101,6 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit {
if (this.hasPermissionForSubscription) {
this.displayedColumns = [
'index',
'user',
'country',
'registration',
@ -114,7 +113,6 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit {
];
} else {
this.displayedColumns = [
'index',
'user',
'registration',
'accounts',

17
apps/client/src/app/components/admin-users/admin-users.html

@ -3,23 +3,6 @@
<div class="col">
<div class="overflow-x-auto">
<table class="gf-table" mat-table [dataSource]="dataSource">
<ng-container matColumnDef="index">
<th
*matHeaderCellDef
class="mat-mdc-header-cell px-1 py-2 text-right"
mat-header-cell
>
#
</th>
<td
*matCellDef="let element; let i = index"
class="mat-mdc-cell px-1 py-2 text-right"
mat-cell
>
{{ i + 1 }}
</td>
</ng-container>
<ng-container matColumnDef="user">
<th
*matHeaderCellDef

14
apps/client/src/app/pages/landing/landing-page.html

@ -2,11 +2,11 @@
<div class="row">
<div class="col text-center">
<div>
<h1 class="font-weight-bold intro" i18n>
<h1 class="font-weight-bold gf-text-wrap-balance intro" i18n>
Manage your wealth like a boss
</h1>
</div>
<p class="lead mb-4" i18n>
<p class="gf-text-wrap-balance lead mb-4" i18n>
Ghostfolio is a privacy-first, open source dashboard for your personal
finances. Break down your asset allocation, know your net worth and make
solid, data-driven investment decisions.
@ -222,11 +222,11 @@
<div class="pt-3 row">
<div class="col text-center">
<h2 class="h4 mb-1 text-center" i18n>
<h2 class="gf-text-wrap-balance h4 mb-1 text-center" i18n>
Protect your <strong>assets</strong>. Refine your
<strong>personal investment strategy</strong>.
</h2>
<p class="lead m-0" i18n>
<p class="gf-text-wrap-balance lead m-0" i18n>
Ghostfolio empowers busy people to keep track of stocks, ETFs or
cryptocurrencies without being tracked.
</p>
@ -270,7 +270,7 @@
<div class="row my-5">
<div class="col-md-6 offset-md-3">
<h2 class="h4 mb-1 text-center" i18n>Why <strong>Ghostfolio</strong>?</h2>
<p class="lead mb-3 text-center" i18n>
<p class="gf-text-wrap-balance lead mb-3 text-center" i18n>
Ghostfolio is for you if you are...
</p>
<ul class="list-unstyled">
@ -363,7 +363,7 @@
@if (hasPermissionForSubscription) {
<div class="row my-5">
<div class="col-12">
<h2 class="h4 text-center" i18n>
<h2 class="gf-text-wrap-balance h4 text-center" i18n>
Members from around the globe are using
<a href="pricing"><strong>Ghostfolio Premium</strong></a>
</h2>
@ -431,7 +431,7 @@
<h2 class="h4 mb-1 text-center" i18n>
Are <strong>you</strong> ready?
</h2>
<p class="lead mb-3 text-center" i18n>
<p class="gf-text-wrap-balance lead mb-3 text-center" i18n>
Join now
@if (hasPermissionForDemo) {
or check out the example account

25
apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts

@ -32,6 +32,7 @@ import { MatCardModule } from '@angular/material/card';
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSnackBar } from '@angular/material/snack-bar';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { SymbolProfile } from '@prisma/client';
import { addIcons } from 'ionicons';
@ -55,7 +56,8 @@ import { takeUntil } from 'rxjs/operators';
MatCardModule,
MatMenuModule,
MatProgressSpinnerModule,
NgxSkeletonLoaderModule
NgxSkeletonLoaderModule,
RouterModule
],
selector: 'gf-analysis-page',
styleUrls: ['./analysis-page.scss'],
@ -342,13 +344,20 @@ export class GfAnalysisPageComponent implements OnDestroy, OnInit {
'netPerformancePercentWithCurrencyEffect'
).reverse();
this.top3 = holdingsSorted.slice(0, 3);
if (holdings?.length > 3) {
this.bottom3 = holdingsSorted.slice(-3).reverse();
} else {
this.bottom3 = [];
}
this.top3 = holdingsSorted
.filter(
({ netPerformancePercentWithCurrencyEffect }) =>
netPerformancePercentWithCurrencyEffect > 0
)
.slice(0, 3);
this.bottom3 = holdingsSorted
.filter(
({ netPerformancePercentWithCurrencyEffect }) =>
netPerformancePercentWithCurrencyEffect < 0
)
.slice(-3)
.reverse();
this.changeDetectorRef.markForCheck();
});

10
apps/client/src/app/pages/portfolio/analysis/analysis-page.html

@ -93,7 +93,7 @@
<div class="mb-5 row">
<div class="col">
<mat-card appearance="outlined" class="mb-3">
<mat-card appearance="outlined">
<mat-card-content>
<div class="d-flex py-1">
<div
@ -232,8 +232,8 @@
</div>
<div class="mb-5 row">
<div class="col-md-6">
<mat-card appearance="outlined" class="mb-3">
<div class="col-md-6 mb-3">
<mat-card appearance="outlined" class="h-100">
<mat-card-header>
<mat-card-title class="align-items-center d-flex" i18n
>Top</mat-card-title
@ -281,8 +281,8 @@
</mat-card-content>
</mat-card>
</div>
<div class="col-md-6">
<mat-card appearance="outlined" class="mb-3">
<div class="col-md-6 mb-3">
<mat-card appearance="outlined" class="h-100">
<mat-card-header>
<mat-card-title class="align-items-center d-flex" i18n
>Bottom</mat-card-title

6
apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts

@ -47,7 +47,7 @@ export class GfXRayPageComponent {
public inactiveRules: PortfolioReportRule[];
public isLoading = false;
public regionalMarketClusterRiskRules: PortfolioReportRule[];
public statistics: PortfolioReportResponse['x-ray']['statistics'];
public statistics: PortfolioReportResponse['xRay']['statistics'];
public user: User;
private unsubscribeSubject = new Subject<void>();
@ -115,7 +115,7 @@ export class GfXRayPageComponent {
this.dataService
.fetchPortfolioReport()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ 'x-ray': { rules, statistics } }) => {
.subscribe(({ xRay: { rules, statistics } }) => {
this.inactiveRules = this.mergeInactiveRules(rules);
this.statistics = statistics;
@ -161,7 +161,7 @@ export class GfXRayPageComponent {
}
private mergeInactiveRules(
rules: PortfolioReportResponse['x-ray']['rules']
rules: PortfolioReportResponse['xRay']['rules']
): PortfolioReportRule[] {
let inactiveRules: PortfolioReportRule[] = [];

3246
apps/client/src/locales/messages.ca.xlf

File diff suppressed because it is too large

3244
apps/client/src/locales/messages.de.xlf

File diff suppressed because it is too large

3318
apps/client/src/locales/messages.es.xlf

File diff suppressed because it is too large

3058
apps/client/src/locales/messages.fr.xlf

File diff suppressed because it is too large

3248
apps/client/src/locales/messages.it.xlf

File diff suppressed because it is too large

3254
apps/client/src/locales/messages.nl.xlf

File diff suppressed because it is too large

3352
apps/client/src/locales/messages.pl.xlf

File diff suppressed because it is too large

3060
apps/client/src/locales/messages.pt.xlf

File diff suppressed because it is too large

3384
apps/client/src/locales/messages.tr.xlf

File diff suppressed because it is too large

3222
apps/client/src/locales/messages.uk.xlf

File diff suppressed because it is too large

3163
apps/client/src/locales/messages.xlf

File diff suppressed because it is too large

3334
apps/client/src/locales/messages.zh.xlf

File diff suppressed because it is too large

2
apps/client/src/styles.scss

@ -1,7 +1,7 @@
@import './styles/bootstrap';
@import './styles/table';
@import 'node_modules/svgmap/dist/svgMap';
@import 'svgmap/dist/svgMap';
:root {
--dark-background: rgb(25, 25, 25);

80
apps/client/src/styles/bootstrap.scss

@ -1,44 +1,44 @@
/*!
* Bootstrap v4.5.2 (https://getbootstrap.com/)
* Copyright 2011-2020 The Bootstrap Authors
* Copyright 2011-2020 Twitter, Inc.
* Bootstrap v4.6.2 (https://getbootstrap.com/)
* Copyright 2011-2022 The Bootstrap Authors
* Copyright 2011-2022 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
@import 'node_modules/bootstrap/scss/functions';
@import 'node_modules/bootstrap/scss/variables';
@import 'node_modules/bootstrap/scss/mixins';
@import 'node_modules/bootstrap/scss/root';
@import 'node_modules/bootstrap/scss/reboot';
@import 'node_modules/bootstrap/scss/type';
@import 'node_modules/bootstrap/scss/images';
// @import 'node_modules/bootstrap/scss/code';
@import 'node_modules/bootstrap/scss/grid';
// @import 'node_modules/bootstrap/scss/tables';
// @import 'node_modules/bootstrap/scss/forms';
// @import 'node_modules/bootstrap/scss/buttons';
// @import 'node_modules/bootstrap/scss/transitions';
// @import 'node_modules/bootstrap/scss/dropdown';
// @import 'node_modules/bootstrap/scss/button-group';
// @import 'node_modules/bootstrap/scss/input-group';
// @import 'node_modules/bootstrap/scss/custom-forms';
// @import 'node_modules/bootstrap/scss/nav';
// @import 'node_modules/bootstrap/scss/navbar';
// @import 'node_modules/bootstrap/scss/card';
@import 'node_modules/bootstrap/scss/breadcrumb';
// @import 'node_modules/bootstrap/scss/pagination';
@import 'node_modules/bootstrap/scss/badge';
// @import 'node_modules/bootstrap/scss/jumbotron';
// @import 'node_modules/bootstrap/scss/alert';
// @import 'node_modules/bootstrap/scss/progress';
// @import 'node_modules/bootstrap/scss/media';
// @import 'node_modules/bootstrap/scss/list-group';
// @import 'node_modules/bootstrap/scss/close';
// @import 'node_modules/bootstrap/scss/toasts';
// @import 'node_modules/bootstrap/scss/modal';
// @import 'node_modules/bootstrap/scss/tooltip';
// @import 'node_modules/bootstrap/scss/popover';
// @import 'node_modules/bootstrap/scss/carousel';
// @import 'node_modules/bootstrap/scss/spinners';
@import 'node_modules/bootstrap/scss/utilities';
// @import 'node_modules/bootstrap/scss/print';
@import 'bootstrap/scss/functions';
@import 'bootstrap/scss/variables';
@import 'bootstrap/scss/mixins';
@import 'bootstrap/scss/root';
@import 'bootstrap/scss/reboot';
@import 'bootstrap/scss/type';
@import 'bootstrap/scss/images';
// @import 'bootstrap/scss/code';
@import 'bootstrap/scss/grid';
// @import 'bootstrap/scss/tables';
// @import 'bootstrap/scss/forms';
// @import 'bootstrap/scss/buttons';
// @import 'bootstrap/scss/transitions';
// @import 'bootstrap/scss/dropdown';
// @import 'bootstrap/scss/button-group';
// @import 'bootstrap/scss/input-group';
// @import 'bootstrap/scss/custom-forms';
// @import 'bootstrap/scss/nav';
// @import 'bootstrap/scss/navbar';
// @import 'bootstrap/scss/card';
@import 'bootstrap/scss/breadcrumb';
// @import 'bootstrap/scss/pagination';
@import 'bootstrap/scss/badge';
// @import 'bootstrap/scss/jumbotron';
// @import 'bootstrap/scss/alert';
// @import 'bootstrap/scss/progress';
// @import 'bootstrap/scss/media';
// @import 'bootstrap/scss/list-group';
// @import 'bootstrap/scss/close';
// @import 'bootstrap/scss/toasts';
// @import 'bootstrap/scss/modal';
// @import 'bootstrap/scss/tooltip';
// @import 'bootstrap/scss/popover';
// @import 'bootstrap/scss/carousel';
// @import 'bootstrap/scss/spinners';
@import 'bootstrap/scss/utilities';
// @import 'bootstrap/scss/print';

2
libs/common/src/lib/interfaces/responses/portfolio-report.interface.ts

@ -1,7 +1,7 @@
import { PortfolioReportRule } from '../portfolio-report-rule.interface';
export interface PortfolioReportResponse {
'x-ray': {
xRay: {
rules: { [group: string]: PortfolioReportRule[] };
statistics: {
rulesActiveCount: number;

50
libs/ui/src/lib/membership-card/membership-card.component.stories.ts

@ -0,0 +1,50 @@
import { CommonModule } from '@angular/common';
import '@angular/localize/init';
import { MatButtonModule } from '@angular/material/button';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { moduleMetadata } from '@storybook/angular';
import type { Meta, StoryObj } from '@storybook/angular';
import { addYears } from 'date-fns';
import { GfLogoComponent } from '../logo';
import { GfMembershipCardComponent } from './membership-card.component';
export default {
title: 'Membership Card',
component: GfMembershipCardComponent,
decorators: [
moduleMetadata({
imports: [
CommonModule,
GfLogoComponent,
IonIcon,
MatButtonModule,
RouterModule.forChild([])
],
providers: [{ provide: ActivatedRoute, useValue: {} }]
})
],
argTypes: {
name: {
control: { type: 'select' },
options: ['Basic', 'Premium']
}
}
} as Meta<GfMembershipCardComponent>;
type Story = StoryObj<GfMembershipCardComponent>;
export const Basic: Story = {
args: {
name: 'Basic'
}
};
export const Premium: Story = {
args: {
expiresAt: addYears(new Date(), 1).toLocaleDateString(),
hasPermissionToCreateApiKey: true,
name: 'Premium'
}
};

33760
package-lock.json

File diff suppressed because it is too large

70
package.json

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "2.185.0",
"version": "2.186.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio",
@ -56,17 +56,17 @@
"workspace-generator": "nx workspace-generator"
},
"dependencies": {
"@angular/animations": "20.0.7",
"@angular/cdk": "20.0.6",
"@angular/common": "20.0.7",
"@angular/compiler": "20.0.7",
"@angular/core": "20.0.7",
"@angular/forms": "20.0.7",
"@angular/material": "20.0.6",
"@angular/platform-browser": "20.0.7",
"@angular/platform-browser-dynamic": "20.0.7",
"@angular/router": "20.0.7",
"@angular/service-worker": "20.0.7",
"@angular/animations": "20.1.3",
"@angular/cdk": "20.1.3",
"@angular/common": "20.1.3",
"@angular/compiler": "20.1.3",
"@angular/core": "20.1.3",
"@angular/forms": "20.1.3",
"@angular/material": "20.1.3",
"@angular/platform-browser": "20.1.3",
"@angular/platform-browser-dynamic": "20.1.3",
"@angular/router": "20.1.3",
"@angular/service-worker": "20.1.3",
"@codewithdan/observable-store": "2.2.15",
"@date-fns/utc": "2.1.0",
"@dfinity/agent": "0.15.7",
@ -122,7 +122,7 @@
"lodash": "4.17.21",
"marked": "15.0.4",
"ms": "3.0.0-canary.1",
"ng-extract-i18n-merge": "2.15.1",
"ng-extract-i18n-merge": "3.0.0",
"ngx-device-detector": "10.0.2",
"ngx-markdown": "20.0.0",
"ngx-skeleton-loader": "11.2.1",
@ -143,33 +143,33 @@
"zone.js": "0.15.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "20.0.6",
"@angular-devkit/core": "20.0.6",
"@angular-devkit/schematics": "20.0.6",
"@angular-devkit/build-angular": "20.1.3",
"@angular-devkit/core": "20.1.3",
"@angular-devkit/schematics": "20.1.3",
"@angular-eslint/eslint-plugin": "20.1.1",
"@angular-eslint/eslint-plugin-template": "20.1.1",
"@angular-eslint/template-parser": "20.1.1",
"@angular/cli": "20.0.6",
"@angular/compiler-cli": "20.0.7",
"@angular/language-service": "20.0.7",
"@angular/localize": "20.0.7",
"@angular/pwa": "20.0.6",
"@angular/cli": "20.1.3",
"@angular/compiler-cli": "20.1.3",
"@angular/language-service": "20.1.3",
"@angular/localize": "20.1.3",
"@angular/pwa": "20.1.3",
"@eslint/eslintrc": "3.3.1",
"@eslint/js": "9.24.0",
"@nestjs/schematics": "11.0.5",
"@nestjs/testing": "11.1.3",
"@nx/angular": "21.2.4",
"@nx/cypress": "21.2.4",
"@nx/eslint-plugin": "21.2.4",
"@nx/jest": "21.2.4",
"@nx/js": "21.2.4",
"@nx/module-federation": "21.2.4",
"@nx/nest": "21.2.4",
"@nx/node": "21.2.4",
"@nx/storybook": "21.2.4",
"@nx/web": "21.2.4",
"@nx/workspace": "21.2.4",
"@schematics/angular": "20.0.6",
"@nx/angular": "21.3.9",
"@nx/cypress": "21.3.9",
"@nx/eslint-plugin": "21.3.9",
"@nx/jest": "21.3.9",
"@nx/js": "21.3.9",
"@nx/module-federation": "21.3.9",
"@nx/nest": "21.3.9",
"@nx/node": "21.3.9",
"@nx/storybook": "21.3.9",
"@nx/web": "21.3.9",
"@nx/workspace": "21.3.9",
"@schematics/angular": "20.1.3",
"@storybook/addon-docs": "9.0.17",
"@storybook/angular": "9.0.17",
"@trivago/prettier-plugin-sort-imports": "5.2.2",
@ -193,7 +193,7 @@
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"jest-preset-angular": "14.6.0",
"nx": "21.2.4",
"nx": "21.3.9",
"prettier": "3.6.2",
"prettier-plugin-organize-attributes": "1.0.0",
"prisma": "6.12.0",
@ -202,7 +202,7 @@
"replace-in-file": "8.3.0",
"shx": "0.3.4",
"storybook": "9.0.17",
"ts-jest": "29.1.0",
"ts-jest": "29.4.0",
"ts-node": "10.9.2",
"tslib": "2.8.1",
"typescript": "5.8.3",

Loading…
Cancel
Save