Feature/move tags from info to user service (#3859)
* Move tags from info to user service * Update changelog
This commit is contained in:
parent
fc5ed887ff
commit
98957d282e
@ -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: {
|
||||||
where: {
|
orders: {
|
||||||
userId: null
|
where: {
|
||||||
}
|
userId
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getInUseByUser(userId: string) {
|
|
||||||
return this.prismaService.tag.findMany({
|
|
||||||
orderBy: {
|
|
||||||
name: 'asc'
|
|
||||||
},
|
|
||||||
where: {
|
|
||||||
orders: {
|
|
||||||
some: {
|
|
||||||
userId
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
name: 'asc'
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
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…
x
Reference in New Issue
Block a user