diff --git a/CHANGELOG.md b/CHANGELOG.md index f86fccddb..349c140a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Extended the tags selector component by a `readonly` attribute +- Extended the tags selector component to support creating custom tags +- Added global styles to the _Storybook_ setup + +### Changed + +- Improved the language localization for German (`de`) + +## 2.138.0 - 2025-02-08 + +### Added + +- Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (Emerging Markets) - Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (Europe) - Added a link to _Duck.ai_ to the _Copy AI prompt to clipboard_ action on the analysis page (experimental) - Extracted the tags selector to a reusable component used in the create or update activity dialog and holding detail dialog +- Added stories for the tags selector component ### Changed - Improved the caching of the portfolio snapshot in the portfolio calculator by expiring cache entries when a user changes tags in the holding detail dialog +- Improved the error handling in the _CoinGecko_ service - Improved the language localization for German (`de`) - Upgraded `svgmap` from version `2.6.0` to `2.12.2` diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index bbe2a6ca5..72568f37a 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -15,6 +15,7 @@ import { EconomicMarketClusterRiskDevelopedMarkets } from '@ghostfolio/api/model import { EconomicMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/emerging-markets'; import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup'; import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; +import { RegionalMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/emerging-markets'; import { RegionalMarketClusterRiskEurope } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/europe'; import { RegionalMarketClusterRiskNorthAmerica } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/north-america'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; @@ -1279,6 +1280,11 @@ export class PortfolioService { summary.ordersCount > 0 ? await this.rulesService.evaluate( [ + new RegionalMarketClusterRiskEmergingMarkets( + this.exchangeRateDataService, + marketsAdvancedTotalInBaseCurrency, + marketsAdvanced.emergingMarkets.valueInBaseCurrency + ), new RegionalMarketClusterRiskEurope( this.exchangeRateDataService, marketsAdvancedTotalInBaseCurrency, diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 44a29e737..1dae9d45b 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -13,6 +13,7 @@ import { EconomicMarketClusterRiskDevelopedMarkets } from '@ghostfolio/api/model import { EconomicMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/emerging-markets'; import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup'; import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; +import { RegionalMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/emerging-markets'; import { RegionalMarketClusterRiskEurope } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/europe'; import { RegionalMarketClusterRiskNorthAmerica } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/north-america'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; @@ -271,6 +272,12 @@ export class UserService { undefined, undefined ).getSettings(user.Settings.settings), + RegionalMarketClusterRiskEmergingMarkets: + new RegionalMarketClusterRiskEmergingMarkets( + undefined, + undefined, + undefined + ).getSettings(user.Settings.settings), RegionalMarketClusterRiskEurope: new RegionalMarketClusterRiskEurope( undefined, undefined, diff --git a/apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts b/apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts new file mode 100644 index 000000000..2d9a3b394 --- /dev/null +++ b/apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts @@ -0,0 +1,79 @@ +import { Rule } from '@ghostfolio/api/models/rule'; +import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { UserSettings } from '@ghostfolio/common/interfaces'; + +import { Settings } from './interfaces/rule-settings.interface'; + +export class RegionalMarketClusterRiskEmergingMarkets extends Rule { + private currentValueInBaseCurrency: number; + private emergingMarketsValueInBaseCurrency: number; + + public constructor( + protected exchangeRateDataService: ExchangeRateDataService, + currentValueInBaseCurrency: number, + emergingMarketsValueInBaseCurrency: number + ) { + super(exchangeRateDataService, { + key: RegionalMarketClusterRiskEmergingMarkets.name, + name: 'Emerging Markets' + }); + + this.currentValueInBaseCurrency = currentValueInBaseCurrency; + this.emergingMarketsValueInBaseCurrency = + emergingMarketsValueInBaseCurrency; + } + + public evaluate(ruleSettings: Settings) { + const emergingMarketsValueRatio = this.currentValueInBaseCurrency + ? this.emergingMarketsValueInBaseCurrency / + this.currentValueInBaseCurrency + : 0; + + if (emergingMarketsValueRatio > ruleSettings.thresholdMax) { + return { + evaluation: `The Emerging Markets contribution of your current investment (${(emergingMarketsValueRatio * 100).toPrecision(3)}%) exceeds ${( + ruleSettings.thresholdMax * 100 + ).toPrecision(3)}%`, + value: false + }; + } else if (emergingMarketsValueRatio < ruleSettings.thresholdMin) { + return { + evaluation: `The Emerging Markets contribution of your current investment (${(emergingMarketsValueRatio * 100).toPrecision(3)}%) is below ${( + ruleSettings.thresholdMin * 100 + ).toPrecision(3)}%`, + value: false + }; + } + + return { + evaluation: `The Emerging Markets contribution of your current investment (${(emergingMarketsValueRatio * 100).toPrecision(3)}%) is within the range of ${( + ruleSettings.thresholdMin * 100 + ).toPrecision( + 3 + )}% and ${(ruleSettings.thresholdMax * 100).toPrecision(3)}%`, + value: true + }; + } + + public getConfiguration() { + return { + threshold: { + max: 1, + min: 0, + step: 0.01, + unit: '%' + }, + thresholdMax: true, + thresholdMin: true + }; + } + + public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + return { + baseCurrency, + isActive: xRayRules?.[this.getKey()]?.isActive ?? true, + thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.12, + thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.08 + }; + } +} diff --git a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts index 30dbe0ae1..fb1fa9b63 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -112,7 +112,7 @@ export class CoinGeckoService implements DataProviderInterface { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; }> { try { - const { prices } = await fetch( + const { error, prices, status } = await fetch( `${ this.apiUrl }/coins/${symbol}/market_chart/range?vs_currency=${DEFAULT_CURRENCY.toLowerCase()}&from=${getUnixTime( @@ -124,6 +124,14 @@ export class CoinGeckoService implements DataProviderInterface { } ).then((res) => res.json()); + if (error?.status) { + throw new Error(error.status.error_message); + } + + if (status) { + throw new Error(status.error_message); + } + const result: { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; } = { diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html index a20c9af7a..d820ddf7d 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -366,33 +366,12 @@ -
-
- -
-
- - @if (!data.hasPermissionToUpdateOrder && tagsAvailable?.length > 0) { -
-
-
Tags
- - @for (tag of tags; track tag) { - {{ tag.name }} - } - -
-
- } + @if ( dataSource?.data.length > 0 && diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index 3bee762a2..82f3f96bb 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -2218,12 +2218,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -2639,7 +2639,7 @@ Informar d’un Problema amb les Dades apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7748,6 +7748,14 @@ 154 + + Create + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index 9f5fd16f8..0040f8cb6 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -1357,12 +1357,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -1370,7 +1370,7 @@ Datenfehler melden apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7748,6 +7748,14 @@ 154 + + Create + Erstelle + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index c71d49f5d..bc6a62411 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -1358,12 +1358,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -1371,7 +1371,7 @@ Reporta un anomalía de los datos apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7749,6 +7749,14 @@ 154 + + Create + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index 521cfe6fa..353a2a3f6 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -969,12 +969,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -1718,7 +1718,7 @@ Signaler une Erreur de Données apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7748,6 +7748,14 @@ 154 + + Create + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index 4d428d419..c6cb7ac7a 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -1358,12 +1358,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -1371,7 +1371,7 @@ Segnala un’anomalia dei dati apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7749,6 +7749,14 @@ 154 + + Create + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index a3c6c6d67..53efeab95 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -1357,12 +1357,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -1370,7 +1370,7 @@ Gegevensstoring melden apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7748,6 +7748,14 @@ 154 + + Create + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index 135e5847f..2942a4898 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -2046,12 +2046,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -2803,7 +2803,7 @@ Zgłoś Błąd Danych apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7748,6 +7748,14 @@ 154 + + Create + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index 37d4688f7..8b9b7ac03 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -1665,12 +1665,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -1678,7 +1678,7 @@ Dados do Relatório com Problema apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7748,6 +7748,14 @@ 154 + + Create + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index 0c6c72067..3c7d3f102 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -1782,12 +1782,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -2647,7 +2647,7 @@ Rapor Veri Sorunu apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7748,6 +7748,14 @@ 154 + + Create + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.uk.xlf b/apps/client/src/locales/messages.uk.xlf index 235d99bf8..26690077b 100644 --- a/apps/client/src/locales/messages.uk.xlf +++ b/apps/client/src/locales/messages.uk.xlf @@ -2258,12 +2258,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -2767,7 +2767,7 @@ Повідомити про збій даних apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7748,6 +7748,14 @@ 154 + + Create + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index 5a70868dc..ac05bc01a 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -1943,12 +1943,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -2623,7 +2623,7 @@ Report Data Glitch apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7007,6 +7007,13 @@ 154 + + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index 984c0bed1..680906979 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -2063,12 +2063,12 @@ 87 - apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 387 + libs/ui/src/lib/tags-selector/tags-selector.component.html + 4 libs/ui/src/lib/tags-selector/tags-selector.component.html - 2 + 16 @@ -2820,7 +2820,7 @@ 报告数据故障 apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html - 406 + 385 @@ -7749,6 +7749,14 @@ 154 + + Create + Create + + libs/ui/src/lib/tags-selector/tags-selector.component.html + 50 + + diff --git a/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts b/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts index f1ca5c683..4a31ef7b2 100644 --- a/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts +++ b/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts @@ -9,6 +9,7 @@ export interface XRayRulesSettings { EconomicMarketClusterRiskEmergingMarkets?: RuleSettings; EmergencyFundSetup?: RuleSettings; FeeRatioInitialInvestment?: RuleSettings; + RegionalMarketClusterRiskEmergingMarkets?: RuleSettings; RegionalMarketClusterRiskEurope?: RuleSettings; RegionalMarketClusterRiskNorthAmerica?: RuleSettings; } diff --git a/libs/common/src/lib/personal-finance-tools.ts b/libs/common/src/lib/personal-finance-tools.ts index 63d5805cd..e4c820fb7 100644 --- a/libs/common/src/lib/personal-finance-tools.ts +++ b/libs/common/src/lib/personal-finance-tools.ts @@ -202,6 +202,16 @@ export const personalFinanceTools: Product[] = [ pricingPerYear: '$100', slogan: 'All your wealth, in one place.' }, + { + founded: 2018, + hasFreePlan: false, + hasSelfHostingAbility: false, + key: 'fey', + name: 'Fey', + origin: 'Canada', + pricingPerYear: '$300', + slogan: 'Make better investments.' + }, { founded: 2023, hasFreePlan: true, diff --git a/libs/ui/.storybook/preview.js b/libs/ui/.storybook/preview.js index 37eb3a59d..e69de29bb 100644 --- a/libs/ui/.storybook/preview.js +++ b/libs/ui/.storybook/preview.js @@ -1 +0,0 @@ -// import '!style-loader!css-loader!sass-loader!../../../apps/client/src/styles.scss'; diff --git a/libs/ui/project.json b/libs/ui/project.json index 58e959344..1b3f0e4e7 100644 --- a/libs/ui/project.json +++ b/libs/ui/project.json @@ -44,7 +44,12 @@ "outputDir": "dist/storybook/ui", "configDir": "libs/ui/.storybook", "browserTarget": "ui:build-storybook", - "compodoc": false + "compodoc": false, + "styles": [ + "apps/client/src/assets/fonts/inter.css", + "apps/client/src/styles/theme.scss", + "apps/client/src/styles.scss" + ] }, "configurations": { "ci": { diff --git a/libs/ui/src/lib/logo/logo.component.stories.ts b/libs/ui/src/lib/logo/logo.component.stories.ts index c720ebd92..50f047184 100644 --- a/libs/ui/src/lib/logo/logo.component.stories.ts +++ b/libs/ui/src/lib/logo/logo.component.stories.ts @@ -25,7 +25,7 @@ export const Large: Story = { } }; -export const NoLabel: Story = { +export const WithoutLabel: Story = { args: { showLabel: false } diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.html b/libs/ui/src/lib/tags-selector/tags-selector.component.html index 55f8a39f2..4f3929423 100644 --- a/libs/ui/src/lib/tags-selector/tags-selector.component.html +++ b/libs/ui/src/lib/tags-selector/tags-selector.component.html @@ -1,32 +1,60 @@ - - Tags - - @for (tag of tagsSelected(); track tag.id) { - - {{ tag.name }} - - +
+
+ @if (readonly) { +
Tags
+ @if (tags?.length > 0) { + + @for (tag of tags; track tag) { + {{ tag.name }} + } + + } @else { +
-
+ } + } @else { + + Tags + + @for (tag of tagsSelected(); track tag.id) { + + {{ tag.name }} + + + } + + + + @for (tag of filteredOptions | async; track tag.id) { + + {{ tag.name }} + + } + + @if (hasPermissionToCreateTags && tagInputControl.value) { + + + + Create "{{ + tagInputControl.value.trim() + }}" + + + } + + } - - - - @for (tag of filteredOptions | async; track tag.id) { - - {{ tag.name }} - - } - - +
+
diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts b/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts new file mode 100644 index 000000000..8d1431b46 --- /dev/null +++ b/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts @@ -0,0 +1,95 @@ +import { CommonModule } from '@angular/common'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'; + +import { GfTagsSelectorComponent } from './tags-selector.component'; + +export default { + title: 'Tags Selector', + component: GfTagsSelectorComponent, + decorators: [ + moduleMetadata({ + imports: [CommonModule, NoopAnimationsModule] + }) + ] +} as Meta; + +type Story = StoryObj; + +const OPTIONS = [ + { + id: '3ef7e6d9-4598-4eb2-b0e8-00e61cfc0ea6', + name: 'Gambling', + userId: 'c6a71541-d0e3-4e22-ae83-b5e5611b6695' + }, + { + id: 'EMERGENCY_FUND', + name: 'Emergency Fund', + userId: null + }, + { + id: 'RETIREMENT_FUND', + name: 'Retirement Fund', + userId: null + } +]; + +export const Default: Story = { + args: { + tags: [ + { + id: 'EMERGENCY_FUND', + name: 'Emergency Fund', + userId: null + } + ], + tagsAvailable: OPTIONS + } +}; + +export const CreateCustomTags: Story = { + args: { + hasPermissionToCreateTags: true, + tags: [ + { + id: 'EMERGENCY_FUND', + name: 'Emergency Fund', + userId: null + } + ], + tagsAvailable: OPTIONS + } +}; + +export const Readonly: Story = { + args: { + readonly: true, + tags: [ + { + id: 'EMERGENCY_FUND', + name: 'Emergency Fund', + userId: null + }, + { + id: 'RETIREMENT_FUND', + name: 'Retirement Fund', + userId: null + } + ], + tagsAvailable: OPTIONS + } +}; + +export const WithoutValue: Story = { + args: { + tags: [], + tagsAvailable: OPTIONS + } +}; + +export const WithoutOptions: Story = { + args: { + tags: [], + tagsAvailable: [] + } +}; diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.ts b/libs/ui/src/lib/tags-selector/tags-selector.component.ts index 77c776ece..611c1e938 100644 --- a/libs/ui/src/lib/tags-selector/tags-selector.component.ts +++ b/libs/ui/src/lib/tags-selector/tags-selector.component.ts @@ -42,6 +42,8 @@ import { BehaviorSubject, Subject, takeUntil } from 'rxjs'; templateUrl: 'tags-selector.component.html' }) export class GfTagsSelectorComponent implements OnInit, OnChanges, OnDestroy { + @Input() hasPermissionToCreateTags = false; + @Input() readonly = false; @Input() tags: Tag[]; @Input() tagsAvailable: Tag[]; @@ -75,10 +77,18 @@ export class GfTagsSelectorComponent implements OnInit, OnChanges, OnDestroy { } public onAddTag(event: MatAutocompleteSelectedEvent) { - const tag = this.tagsAvailable.find(({ id }) => { + let tag = this.tagsAvailable.find(({ id }) => { return id === event.option.value; }); + if (!tag && this.hasPermissionToCreateTags) { + tag = { + id: undefined, + name: event.option.value as string, + userId: null + }; + } + this.tagsSelected.update((tags) => { return [...(tags ?? []), tag]; }); diff --git a/package-lock.json b/package-lock.json index ec2717b5f..4c0d0bced 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.137.1", + "version": "2.138.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.137.1", + "version": "2.138.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 719fef873..9120a9d19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.137.1", + "version": "2.138.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio",