Feature/move tags from info to user service (#3859)

* Move tags from info to user service

* Update changelog
pull/3902/head
Matej Gerek 2 weeks ago committed by GitHub
parent fc5ed887ff
commit 98957d282e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Moved the tags from the info to the user service
- Switched the `prefer-const` rule from `warn` to `error` in the `eslint` configuration - Switched the `prefer-const` rule from `warn` to `error` in the `eslint` configuration
## 2.113.0 - 2024-10-06 ## 2.113.0 - 2024-10-06

@ -9,7 +9,6 @@ import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-d
import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module';
import { DataGatheringModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; import { DataGatheringModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
import { TagModule } from '@ghostfolio/api/services/tag/tag.module';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt'; import { JwtModule } from '@nestjs/jwt';
@ -33,7 +32,6 @@ import { InfoService } from './info.service';
PropertyModule, PropertyModule,
RedisCacheModule, RedisCacheModule,
SymbolProfileModule, SymbolProfileModule,
TagModule,
TransformDataSourceInResponseModule, TransformDataSourceInResponseModule,
UserModule UserModule
], ],

@ -5,7 +5,6 @@ import { UserService } from '@ghostfolio/api/app/user/user.service';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { TagService } from '@ghostfolio/api/services/tag/tag.service';
import { import {
DEFAULT_CURRENCY, DEFAULT_CURRENCY,
PROPERTY_BETTER_UPTIME_MONITOR_ID, PROPERTY_BETTER_UPTIME_MONITOR_ID,
@ -47,7 +46,6 @@ export class InfoService {
private readonly platformService: PlatformService, private readonly platformService: PlatformService,
private readonly propertyService: PropertyService, private readonly propertyService: PropertyService,
private readonly redisCacheService: RedisCacheService, private readonly redisCacheService: RedisCacheService,
private readonly tagService: TagService,
private readonly userService: UserService private readonly userService: UserService
) {} ) {}
@ -103,8 +101,7 @@ export class InfoService {
isUserSignupEnabled, isUserSignupEnabled,
platforms, platforms,
statistics, statistics,
subscriptions, subscriptions
tags
] = await Promise.all([ ] = await Promise.all([
this.benchmarkService.getBenchmarkAssetProfiles(), this.benchmarkService.getBenchmarkAssetProfiles(),
this.getDemoAuthToken(), this.getDemoAuthToken(),
@ -113,8 +110,7 @@ export class InfoService {
orderBy: { name: 'asc' } orderBy: { name: 'asc' }
}), }),
this.getStatistics(), this.getStatistics(),
this.getSubscriptions(), this.getSubscriptions()
this.tagService.getPublic()
]); ]);
if (isUserSignupEnabled) { if (isUserSignupEnabled) {
@ -130,7 +126,6 @@ export class InfoService {
platforms, platforms,
statistics, statistics,
subscriptions, subscriptions,
tags,
baseCurrency: DEFAULT_CURRENCY, baseCurrency: DEFAULT_CURRENCY,
currencies: this.exchangeRateDataService.getCurrencies() currencies: this.exchangeRateDataService.getCurrencies()
}; };

@ -56,7 +56,7 @@ export class UserService {
{ Account, id, permissions, Settings, subscription }: UserWithSettings, { Account, id, permissions, Settings, subscription }: UserWithSettings,
aLocale = locale aLocale = locale
): Promise<IUser> { ): Promise<IUser> {
const accessesResult = await Promise.all([ const userData = await Promise.all([
this.prismaService.access.findMany({ this.prismaService.access.findMany({
include: { include: {
User: true User: true
@ -70,11 +70,11 @@ export class UserService {
}, },
where: { userId: id } where: { userId: id }
}), }),
this.tagService.getInUseByUser(id) this.tagService.getTagsForUser(id)
]); ]);
const access = accessesResult[0]; const access = userData[0];
const firstActivity = accessesResult[1]; const firstActivity = userData[1];
let tags = accessesResult[2]; let tags = userData[2];
let systemMessage: SystemMessage; let systemMessage: SystemMessage;

@ -6,29 +6,39 @@ import { Injectable } from '@nestjs/common';
export class TagService { export class TagService {
public constructor(private readonly prismaService: PrismaService) {} public constructor(private readonly prismaService: PrismaService) {}
public async getPublic() { public async getTagsForUser(userId: string) {
return this.prismaService.tag.findMany({ const tags = await this.prismaService.tag.findMany({
orderBy: { include: {
name: 'asc' _count: {
select: {
orders: {
where: {
userId
}
}
}
}
}, },
where: {
userId: null
}
});
}
public async getInUseByUser(userId: string) {
return this.prismaService.tag.findMany({
orderBy: { orderBy: {
name: 'asc' name: 'asc'
}, },
where: { where: {
orders: { OR: [
some: { {
userId userId
},
{
userId: null
} }
} ]
} }
}); });
return tags.map(({ _count, id, name, userId }) => ({
id,
name,
userId,
isUsed: _count.orders > 0
}));
} }
} }

@ -147,8 +147,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
) {} ) {}
public ngOnInit() { public ngOnInit() {
const { tags } = this.dataService.fetchInfo();
this.activityForm = this.formBuilder.group({ this.activityForm = this.formBuilder.group({
tags: <string[]>[] tags: <string[]>[]
}); });
@ -158,13 +156,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
{ id: this.data.symbol, type: 'SYMBOL' } { id: this.data.symbol, type: 'SYMBOL' }
]; ];
this.tagsAvailable = tags.map((tag) => {
return {
...tag,
name: translate(tag.name)
};
});
this.activityForm this.activityForm
.get('tags') .get('tags')
.valueChanges.pipe(takeUntil(this.unsubscribeSubject)) .valueChanges.pipe(takeUntil(this.unsubscribeSubject))
@ -434,6 +425,14 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user = state.user;
this.tagsAvailable =
this.user?.tags?.map((tag) => {
return {
...tag,
name: translate(tag.name)
};
}) ?? [];
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
}); });

@ -76,17 +76,19 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
this.locale = this.data.user?.settings?.locale; this.locale = this.data.user?.settings?.locale;
this.dateAdapter.setLocale(this.locale); this.dateAdapter.setLocale(this.locale);
const { currencies, platforms, tags } = this.dataService.fetchInfo(); const { currencies, platforms } = this.dataService.fetchInfo();
this.currencies = currencies; this.currencies = currencies;
this.defaultDateFormat = getDateFormatString(this.locale); this.defaultDateFormat = getDateFormatString(this.locale);
this.platforms = platforms; this.platforms = platforms;
this.tagsAvailable = tags.map((tag) => {
return { this.tagsAvailable =
...tag, this.data.user?.tags?.map((tag) => {
name: translate(tag.name) return {
}; ...tag,
}); name: translate(tag.name)
};
}) ?? [];
Object.keys(Type).forEach((type) => { Object.keys(Type).forEach((type) => {
this.typesTranslationMap[Type[type]] = translate(Type[type]); this.typesTranslationMap[Type[type]] = translate(Type[type]);

@ -1,6 +1,6 @@
import { SubscriptionOffer } from '@ghostfolio/common/types'; import { SubscriptionOffer } from '@ghostfolio/common/types';
import { Platform, SymbolProfile, Tag } from '@prisma/client'; import { Platform, SymbolProfile } from '@prisma/client';
import { Statistics } from './statistics.interface'; import { Statistics } from './statistics.interface';
import { Subscription } from './subscription.interface'; import { Subscription } from './subscription.interface';
@ -19,5 +19,4 @@ export interface InfoItem {
statistics: Statistics; statistics: Statistics;
stripePublicKey?: string; stripePublicKey?: string;
subscriptions: { [offer in SubscriptionOffer]: Subscription }; subscriptions: { [offer in SubscriptionOffer]: Subscription };
tags: Tag[];
} }

@ -23,5 +23,5 @@ export interface User {
offer: SubscriptionOffer; offer: SubscriptionOffer;
type: SubscriptionType; type: SubscriptionType;
}; };
tags: Tag[]; tags: (Tag & { isUsed: boolean })[];
} }

@ -156,7 +156,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
) {} ) {}
public ngOnInit() { public ngOnInit() {
this.accounts = this.user?.accounts;
this.assetClasses = Object.keys(AssetClass).map((assetClass) => { this.assetClasses = Object.keys(AssetClass).map((assetClass) => {
return { return {
id: assetClass, id: assetClass,
@ -164,13 +163,6 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
type: 'ASSET_CLASS' type: 'ASSET_CLASS'
}; };
}); });
this.tags = this.user?.tags.map(({ id, name }) => {
return {
id,
label: translate(name),
type: 'TAG'
};
});
this.searchFormControl.valueChanges this.searchFormControl.valueChanges
.pipe( .pipe(
@ -212,6 +204,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
} }
public ngOnChanges() { public ngOnChanges() {
this.accounts = this.user?.accounts ?? [];
this.dateRangeOptions = [ this.dateRangeOptions = [
{ label: $localize`Today`, value: '1d' }, { label: $localize`Today`, value: '1d' },
{ {
@ -279,6 +273,23 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
emitEvent: false emitEvent: false
} }
); );
this.tags =
this.user?.tags
?.filter(({ isUsed }) => {
return isUsed;
})
.map(({ id, name }) => {
return {
id,
label: translate(name),
type: 'TAG'
};
}) ?? [];
if (this.tags.length === 0) {
this.filterForm.get('tag').disable({ emitEvent: false });
}
} }
public hasFilter(aFormValue: { [key: string]: string }) { public hasFilter(aFormValue: { [key: string]: string }) {

Loading…
Cancel
Save