From e40cc482635703e7d5fa581bda35dd0a9bfd7c72 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 30 Jul 2026 20:55:18 +0200 Subject: [PATCH] Consolidate markets pages --- .../fear-and-greed-index.component.html | 24 ------------ .../fear-and-greed-index.component.scss | 8 ---- .../components/markets/markets.component.ts | 7 +++- .../src/app/components/markets/markets.html | 1 + .../src/app/pages/api/api-page.component.ts | 2 +- libs/common/src/lib/helper.ts | 6 ++- .../fear-and-greed-index.component.html | 25 ++++++++++++ .../fear-and-greed-index.component.scss | 3 ++ .../fear-and-greed-index.component.stories.ts | 39 +++++++++++++++++++ .../fear-and-greed-index.component.ts | 23 +++++------ libs/ui/src/lib/fear-and-greed-index/index.ts | 1 + libs/ui/src/lib/i18n.ts | 1 + 12 files changed, 93 insertions(+), 47 deletions(-) delete mode 100644 apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.html delete mode 100644 apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.scss create mode 100644 libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.html create mode 100644 libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.scss create mode 100644 libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.stories.ts rename {apps/client/src/app/components => libs/ui/src/lib}/fear-and-greed-index/fear-and-greed-index.component.ts (53%) create mode 100644 libs/ui/src/lib/fear-and-greed-index/index.ts diff --git a/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.html b/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.html deleted file mode 100644 index dd2925f431..0000000000 --- a/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.html +++ /dev/null @@ -1,24 +0,0 @@ -
-
-
{{ fearAndGreedIndexEmoji }}
-
-
- {{ fearAndGreedIndexText }} - {{ fearAndGreedIndex | number: '1.0-0' }}/100 -
- Current Market Mood -
-
- @if (!fearAndGreedIndex) { - - } -
diff --git a/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.scss b/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.scss deleted file mode 100644 index dfe024a035..0000000000 --- a/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -:host { - display: block; - - ngx-skeleton-loader { - bottom: 0; - top: 0; - } -} diff --git a/apps/client/src/app/components/markets/markets.component.ts b/apps/client/src/app/components/markets/markets.component.ts index 0d9743bdb5..27f60d86ac 100644 --- a/apps/client/src/app/components/markets/markets.component.ts +++ b/apps/client/src/app/components/markets/markets.component.ts @@ -1,4 +1,3 @@ -import { GfFearAndGreedIndexComponent } from '@ghostfolio/client/components/fear-and-greed-index/fear-and-greed-index.component'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { resetHours } from '@ghostfolio/common/helper'; import { @@ -12,6 +11,7 @@ import { import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { FearAndGreedIndexMode } from '@ghostfolio/common/types'; import { GfBenchmarkComponent } from '@ghostfolio/ui/benchmark'; +import { GfFearAndGreedIndexComponent } from '@ghostfolio/ui/fear-and-greed-index'; import { GfLineChartComponent } from '@ghostfolio/ui/line-chart'; import { DataService } from '@ghostfolio/ui/services'; import { GfToggleComponent } from '@ghostfolio/ui/toggle'; @@ -64,6 +64,7 @@ export class GfMarketsComponent implements OnInit { protected hasPermissionToAccessFearAndGreedIndex: boolean; protected hasPermissionToReadMarketDataOfMarkets: boolean; protected historicalDataItems: HistoricalDataItem[]; + protected isLoadingFearAndGreedIndex = true; protected user: User; private fearAndGreedIndexData: MarketDataOfMarketsResponse['fearAndGreedIndex']; @@ -104,6 +105,8 @@ export class GfMarketsComponent implements OnInit { !this.fearAndGreedIndexData ) { this.fetchMarketDataOfMarkets(); + } else { + this.isLoadingFearAndGreedIndex = false; } this.changeDetectorRef.markForCheck(); @@ -137,6 +140,8 @@ export class GfMarketsComponent implements OnInit { this.initializeFearAndGreedIndex(); + this.isLoadingFearAndGreedIndex = false; + this.changeDetectorRef.markForCheck(); }); } diff --git a/apps/client/src/app/components/markets/markets.html b/apps/client/src/app/components/markets/markets.html index 5e802d1ab0..bd9013a00e 100644 --- a/apps/client/src/app/components/markets/markets.html +++ b/apps/client/src/app/components/markets/markets.html @@ -41,6 +41,7 @@ diff --git a/apps/client/src/app/pages/api/api-page.component.ts b/apps/client/src/app/pages/api/api-page.component.ts index 5568902710..8b80370e8d 100644 --- a/apps/client/src/app/pages/api/api-page.component.ts +++ b/apps/client/src/app/pages/api/api-page.component.ts @@ -1,4 +1,3 @@ -import { GfFearAndGreedIndexComponent } from '@ghostfolio/client/components/fear-and-greed-index/fear-and-greed-index.component'; import { HEADER_KEY_SKIP_INTERCEPTOR, HEADER_KEY_TOKEN @@ -14,6 +13,7 @@ import { MarketDataOfMarketsResponse, QuotesResponse } from '@ghostfolio/common/interfaces'; +import { GfFearAndGreedIndexComponent } from '@ghostfolio/ui/fear-and-greed-index'; import { CommonModule } from '@angular/common'; import { diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index 5d1ff538e5..b2a3ab419a 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -560,8 +560,10 @@ export function resetHours(aDate: Date) { return new Date(Date.UTC(year, month, day)); } -export function resolveFearAndGreedIndex(aValue: number) { - if (aValue <= 25) { +export function resolveFearAndGreedIndex(aValue?: number) { + if (isNil(aValue)) { + return { emoji: '⚪', key: 'UNKNOWN', text: 'Unknown' }; + } else if (aValue <= 25) { return { emoji: '🥵', key: 'EXTREME_FEAR', text: 'Extreme Fear' }; } else if (aValue <= 45) { return { emoji: '😨', key: 'FEAR', text: 'Fear' }; diff --git a/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.html b/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.html new file mode 100644 index 0000000000..e441c53f6b --- /dev/null +++ b/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.html @@ -0,0 +1,25 @@ +@if (isLoading() && !fearAndGreedIndex()) { + +} @else { +
+
{{ fearAndGreedIndexEmoji() }}
+
+
+ {{ fearAndGreedIndexText() }} + {{ + (fearAndGreedIndex() | number: '1.0-0') ?? placeholder + }}/100 +
+ Current Market Mood +
+
+} diff --git a/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.scss b/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.scss new file mode 100644 index 0000000000..5d4e87f30f --- /dev/null +++ b/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.scss @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.stories.ts b/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.stories.ts new file mode 100644 index 0000000000..b5b2074da9 --- /dev/null +++ b/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.stories.ts @@ -0,0 +1,39 @@ +import '@angular/localize/init'; +import { moduleMetadata } from '@storybook/angular'; +import type { Meta, StoryObj } from '@storybook/angular'; +import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; + +import { GfFearAndGreedIndexComponent } from './fear-and-greed-index.component'; + +export default { + title: 'Fear & Greed Index', + component: GfFearAndGreedIndexComponent, + decorators: [ + moduleMetadata({ + imports: [NgxSkeletonLoaderModule] + }) + ] +} as Meta; + +type Story = StoryObj; + +export const Loading: Story = { + args: { + fearAndGreedIndex: undefined, + isLoading: true + } +}; + +export const Default: Story = { + args: { + fearAndGreedIndex: 50, + isLoading: false + } +}; + +export const Unknown: Story = { + args: { + fearAndGreedIndex: undefined, + isLoading: false + } +}; diff --git a/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.ts b/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.ts similarity index 53% rename from apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.ts rename to libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.ts index b507f0008b..4416752b37 100644 --- a/apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.ts +++ b/libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.ts @@ -5,8 +5,8 @@ import { DecimalPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, - Input, - OnChanges + computed, + input } from '@angular/core'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @@ -17,16 +17,17 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; styleUrls: ['./fear-and-greed-index.component.scss'], templateUrl: './fear-and-greed-index.component.html' }) -export class GfFearAndGreedIndexComponent implements OnChanges { - @Input() fearAndGreedIndex: number; +export class GfFearAndGreedIndexComponent { + public readonly fearAndGreedIndex = input(); + public readonly isLoading = input(false); - public fearAndGreedIndexEmoji: string; - public fearAndGreedIndexText: string; + protected readonly placeholder = '—'; - public ngOnChanges() { - const { emoji, key } = resolveFearAndGreedIndex(this.fearAndGreedIndex); + protected readonly fearAndGreedIndexEmoji = computed(() => { + return resolveFearAndGreedIndex(this.fearAndGreedIndex()).emoji; + }); - this.fearAndGreedIndexEmoji = emoji; - this.fearAndGreedIndexText = translate(key); - } + protected readonly fearAndGreedIndexText = computed(() => { + return translate(resolveFearAndGreedIndex(this.fearAndGreedIndex()).key); + }); } diff --git a/libs/ui/src/lib/fear-and-greed-index/index.ts b/libs/ui/src/lib/fear-and-greed-index/index.ts new file mode 100644 index 0000000000..0f1db159ba --- /dev/null +++ b/libs/ui/src/lib/fear-and-greed-index/index.ts @@ -0,0 +1 @@ +export * from './fear-and-greed-index.component'; diff --git a/libs/ui/src/lib/i18n.ts b/libs/ui/src/lib/i18n.ts index 26091be054..9e55a5edbf 100644 --- a/libs/ui/src/lib/i18n.ts +++ b/libs/ui/src/lib/i18n.ts @@ -85,6 +85,7 @@ const locales = { FEAR: $localize`Fear`, GREED: $localize`Greed`, NEUTRAL: $localize`Neutral`, + UNKNOWN: $localize`Unknown`, // Sectors 'Basic Materials': $localize`Basic Materials`,