Browse Source

Consolidate markets pages

pull/7471/head
Thomas Kaul 1 month ago
parent
commit
e40cc48263
  1. 24
      apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.html
  2. 8
      apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.scss
  3. 7
      apps/client/src/app/components/markets/markets.component.ts
  4. 1
      apps/client/src/app/components/markets/markets.html
  5. 2
      apps/client/src/app/pages/api/api-page.component.ts
  6. 6
      libs/common/src/lib/helper.ts
  7. 25
      libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.html
  8. 3
      libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.scss
  9. 39
      libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.stories.ts
  10. 23
      libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.ts
  11. 1
      libs/ui/src/lib/fear-and-greed-index/index.ts
  12. 1
      libs/ui/src/lib/i18n.ts

24
apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.html

@ -1,24 +0,0 @@
<div class="position-relative">
<div class="align-items-center d-flex flex-row" [hidden]="!fearAndGreedIndex">
<div class="h2 mb-0 mr-2">{{ fearAndGreedIndexEmoji }}</div>
<div>
<div class="h4 mb-0">
<span class="mr-2">{{ fearAndGreedIndexText }}</span>
<small class="text-muted"
><strong>{{ fearAndGreedIndex | number: '1.0-0' }}</strong
>/100</small
>
</div>
<small class="d-block" i18n>Current Market Mood</small>
</div>
</div>
@if (!fearAndGreedIndex) {
<ngx-skeleton-loader
animation="pulse"
class="position-absolute w-100"
[theme]="{
height: '100%'
}"
/>
}
</div>

8
apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.scss

@ -1,8 +0,0 @@
:host {
display: block;
ngx-skeleton-loader {
bottom: 0;
top: 0;
}
}

7
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 { UserService } from '@ghostfolio/client/services/user/user.service';
import { resetHours } from '@ghostfolio/common/helper'; import { resetHours } from '@ghostfolio/common/helper';
import { import {
@ -12,6 +11,7 @@ import {
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { FearAndGreedIndexMode } from '@ghostfolio/common/types'; import { FearAndGreedIndexMode } from '@ghostfolio/common/types';
import { GfBenchmarkComponent } from '@ghostfolio/ui/benchmark'; import { GfBenchmarkComponent } from '@ghostfolio/ui/benchmark';
import { GfFearAndGreedIndexComponent } from '@ghostfolio/ui/fear-and-greed-index';
import { GfLineChartComponent } from '@ghostfolio/ui/line-chart'; import { GfLineChartComponent } from '@ghostfolio/ui/line-chart';
import { DataService } from '@ghostfolio/ui/services'; import { DataService } from '@ghostfolio/ui/services';
import { GfToggleComponent } from '@ghostfolio/ui/toggle'; import { GfToggleComponent } from '@ghostfolio/ui/toggle';
@ -64,6 +64,7 @@ export class GfMarketsComponent implements OnInit {
protected hasPermissionToAccessFearAndGreedIndex: boolean; protected hasPermissionToAccessFearAndGreedIndex: boolean;
protected hasPermissionToReadMarketDataOfMarkets: boolean; protected hasPermissionToReadMarketDataOfMarkets: boolean;
protected historicalDataItems: HistoricalDataItem[]; protected historicalDataItems: HistoricalDataItem[];
protected isLoadingFearAndGreedIndex = true;
protected user: User; protected user: User;
private fearAndGreedIndexData: MarketDataOfMarketsResponse['fearAndGreedIndex']; private fearAndGreedIndexData: MarketDataOfMarketsResponse['fearAndGreedIndex'];
@ -104,6 +105,8 @@ export class GfMarketsComponent implements OnInit {
!this.fearAndGreedIndexData !this.fearAndGreedIndexData
) { ) {
this.fetchMarketDataOfMarkets(); this.fetchMarketDataOfMarkets();
} else {
this.isLoadingFearAndGreedIndex = false;
} }
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
@ -137,6 +140,8 @@ export class GfMarketsComponent implements OnInit {
this.initializeFearAndGreedIndex(); this.initializeFearAndGreedIndex();
this.isLoadingFearAndGreedIndex = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
} }

1
apps/client/src/app/components/markets/markets.html

@ -41,6 +41,7 @@
<gf-fear-and-greed-index <gf-fear-and-greed-index
class="d-flex justify-content-center" class="d-flex justify-content-center"
[fearAndGreedIndex]="fearAndGreedIndex" [fearAndGreedIndex]="fearAndGreedIndex"
[isLoading]="isLoadingFearAndGreedIndex"
/> />
</div> </div>
</div> </div>

2
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 { import {
HEADER_KEY_SKIP_INTERCEPTOR, HEADER_KEY_SKIP_INTERCEPTOR,
HEADER_KEY_TOKEN HEADER_KEY_TOKEN
@ -14,6 +13,7 @@ import {
MarketDataOfMarketsResponse, MarketDataOfMarketsResponse,
QuotesResponse QuotesResponse
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { GfFearAndGreedIndexComponent } from '@ghostfolio/ui/fear-and-greed-index';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { import {

6
libs/common/src/lib/helper.ts

@ -560,8 +560,10 @@ export function resetHours(aDate: Date) {
return new Date(Date.UTC(year, month, day)); return new Date(Date.UTC(year, month, day));
} }
export function resolveFearAndGreedIndex(aValue: number) { export function resolveFearAndGreedIndex(aValue?: number) {
if (aValue <= 25) { if (isNil(aValue)) {
return { emoji: '⚪', key: 'UNKNOWN', text: 'Unknown' };
} else if (aValue <= 25) {
return { emoji: '🥵', key: 'EXTREME_FEAR', text: 'Extreme Fear' }; return { emoji: '🥵', key: 'EXTREME_FEAR', text: 'Extreme Fear' };
} else if (aValue <= 45) { } else if (aValue <= 45) {
return { emoji: '😨', key: 'FEAR', text: 'Fear' }; return { emoji: '😨', key: 'FEAR', text: 'Fear' };

25
libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.html

@ -0,0 +1,25 @@
@if (isLoading() && !fearAndGreedIndex()) {
<ngx-skeleton-loader
animation="pulse"
class="w-100"
[theme]="{
height: '2.5rem'
}"
/>
} @else {
<div class="align-items-center d-flex flex-row">
<div class="h2 mb-0 mr-2">{{ fearAndGreedIndexEmoji() }}</div>
<div>
<div class="h4 mb-0">
<span class="mr-2">{{ fearAndGreedIndexText() }}</span>
<small class="text-muted"
><strong>{{
(fearAndGreedIndex() | number: '1.0-0') ?? placeholder
}}</strong
>/100</small
>
</div>
<small class="d-block" i18n>Current Market Mood</small>
</div>
</div>
}

3
libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.scss

@ -0,0 +1,3 @@
:host {
display: block;
}

39
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<GfFearAndGreedIndexComponent>;
type Story = StoryObj<GfFearAndGreedIndexComponent>;
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
}
};

23
apps/client/src/app/components/fear-and-greed-index/fear-and-greed-index.component.ts → libs/ui/src/lib/fear-and-greed-index/fear-and-greed-index.component.ts

@ -5,8 +5,8 @@ import { DecimalPipe } from '@angular/common';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
Input, computed,
OnChanges input
} from '@angular/core'; } from '@angular/core';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
@ -17,16 +17,17 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
styleUrls: ['./fear-and-greed-index.component.scss'], styleUrls: ['./fear-and-greed-index.component.scss'],
templateUrl: './fear-and-greed-index.component.html' templateUrl: './fear-and-greed-index.component.html'
}) })
export class GfFearAndGreedIndexComponent implements OnChanges { export class GfFearAndGreedIndexComponent {
@Input() fearAndGreedIndex: number; public readonly fearAndGreedIndex = input<number>();
public readonly isLoading = input<boolean>(false);
public fearAndGreedIndexEmoji: string; protected readonly placeholder = '—';
public fearAndGreedIndexText: string;
public ngOnChanges() { protected readonly fearAndGreedIndexEmoji = computed(() => {
const { emoji, key } = resolveFearAndGreedIndex(this.fearAndGreedIndex); return resolveFearAndGreedIndex(this.fearAndGreedIndex()).emoji;
});
this.fearAndGreedIndexEmoji = emoji; protected readonly fearAndGreedIndexText = computed(() => {
this.fearAndGreedIndexText = translate(key); return translate(resolveFearAndGreedIndex(this.fearAndGreedIndex()).key);
} });
} }

1
libs/ui/src/lib/fear-and-greed-index/index.ts

@ -0,0 +1 @@
export * from './fear-and-greed-index.component';

1
libs/ui/src/lib/i18n.ts

@ -85,6 +85,7 @@ const locales = {
FEAR: $localize`Fear`, FEAR: $localize`Fear`,
GREED: $localize`Greed`, GREED: $localize`Greed`,
NEUTRAL: $localize`Neutral`, NEUTRAL: $localize`Neutral`,
UNKNOWN: $localize`Unknown`,
// Sectors // Sectors
'Basic Materials': $localize`Basic Materials`, 'Basic Materials': $localize`Basic Materials`,

Loading…
Cancel
Save