Feature/modernize tags endpoint (#4317)
* Modernize tags endpoint * Update changelog
This commit is contained in:
parent
4f76ee6758
commit
f1ab3ff8e8
@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (Asia-Pacific Markets)
|
- Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (Asia-Pacific Markets)
|
||||||
- Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (Japan)
|
- Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (Japan)
|
||||||
- Added support to create custom tags in the holding detail dialog
|
- Added support to create custom tags in the holding detail dialog (experimental)
|
||||||
- Extended the tags selector component by a `readonly` attribute
|
- Extended the tags selector component by a `readonly` attribute
|
||||||
- Extended the tags selector component to support creating custom tags
|
- Extended the tags selector component to support creating custom tags
|
||||||
- Extended the holding detail dialog by the historical market data editor (experimental)
|
- Extended the holding detail dialog by the historical market data editor (experimental)
|
||||||
|
@ -36,6 +36,7 @@ import { ApiKeysModule } from './endpoints/api-keys/api-keys.module';
|
|||||||
import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module';
|
import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module';
|
||||||
import { MarketDataModule } from './endpoints/market-data/market-data.module';
|
import { MarketDataModule } from './endpoints/market-data/market-data.module';
|
||||||
import { PublicModule } from './endpoints/public/public.module';
|
import { PublicModule } from './endpoints/public/public.module';
|
||||||
|
import { TagsModule } from './endpoints/tags/tags.module';
|
||||||
import { ExchangeRateModule } from './exchange-rate/exchange-rate.module';
|
import { ExchangeRateModule } from './exchange-rate/exchange-rate.module';
|
||||||
import { ExportModule } from './export/export.module';
|
import { ExportModule } from './export/export.module';
|
||||||
import { HealthModule } from './health/health.module';
|
import { HealthModule } from './health/health.module';
|
||||||
@ -49,7 +50,6 @@ import { RedisCacheModule } from './redis-cache/redis-cache.module';
|
|||||||
import { SitemapModule } from './sitemap/sitemap.module';
|
import { SitemapModule } from './sitemap/sitemap.module';
|
||||||
import { SubscriptionModule } from './subscription/subscription.module';
|
import { SubscriptionModule } from './subscription/subscription.module';
|
||||||
import { SymbolModule } from './symbol/symbol.module';
|
import { SymbolModule } from './symbol/symbol.module';
|
||||||
import { TagModule } from './tag/tag.module';
|
|
||||||
import { UserModule } from './user/user.module';
|
import { UserModule } from './user/user.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@ -124,7 +124,7 @@ import { UserModule } from './user/user.module';
|
|||||||
SitemapModule,
|
SitemapModule,
|
||||||
SubscriptionModule,
|
SubscriptionModule,
|
||||||
SymbolModule,
|
SymbolModule,
|
||||||
TagModule,
|
TagsModule,
|
||||||
TwitterBotModule,
|
TwitterBotModule,
|
||||||
UserModule
|
UserModule
|
||||||
],
|
],
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
|
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
|
||||||
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
|
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
|
||||||
|
import { TagService } from '@ghostfolio/api/services/tag/tag.service';
|
||||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||||
import { RequestWithUser } from '@ghostfolio/common/types';
|
import { RequestWithUser } from '@ghostfolio/common/types';
|
||||||
|
|
||||||
@ -21,23 +22,15 @@ import { Tag } from '@prisma/client';
|
|||||||
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
||||||
|
|
||||||
import { CreateTagDto } from './create-tag.dto';
|
import { CreateTagDto } from './create-tag.dto';
|
||||||
import { TagService } from './tag.service';
|
|
||||||
import { UpdateTagDto } from './update-tag.dto';
|
import { UpdateTagDto } from './update-tag.dto';
|
||||||
|
|
||||||
@Controller('tag')
|
@Controller('tags')
|
||||||
export class TagController {
|
export class TagsController {
|
||||||
public constructor(
|
public constructor(
|
||||||
@Inject(REQUEST) private readonly request: RequestWithUser,
|
@Inject(REQUEST) private readonly request: RequestWithUser,
|
||||||
private readonly tagService: TagService
|
private readonly tagService: TagService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get()
|
|
||||||
@HasPermission(permissions.readTags)
|
|
||||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
|
||||||
public async getTags() {
|
|
||||||
return this.tagService.getTagsWithActivityCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@UseGuards(AuthGuard('jwt'))
|
@UseGuards(AuthGuard('jwt'))
|
||||||
public async createTag(@Body() data: CreateTagDto): Promise<Tag> {
|
public async createTag(@Body() data: CreateTagDto): Promise<Tag> {
|
||||||
@ -70,6 +63,31 @@ export class TagController {
|
|||||||
return this.tagService.createTag(data);
|
return this.tagService.createTag(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@HasPermission(permissions.deleteTag)
|
||||||
|
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||||
|
public async deleteTag(@Param('id') id: string) {
|
||||||
|
const originalTag = await this.tagService.getTag({
|
||||||
|
id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!originalTag) {
|
||||||
|
throw new HttpException(
|
||||||
|
getReasonPhrase(StatusCodes.FORBIDDEN),
|
||||||
|
StatusCodes.FORBIDDEN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.tagService.deleteTag({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@HasPermission(permissions.readTags)
|
||||||
|
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||||
|
public async getTags() {
|
||||||
|
return this.tagService.getTagsWithActivityCount();
|
||||||
|
}
|
||||||
|
|
||||||
@HasPermission(permissions.updateTag)
|
@HasPermission(permissions.updateTag)
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||||
@ -94,22 +112,4 @@ export class TagController {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
|
||||||
@HasPermission(permissions.deleteTag)
|
|
||||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
|
||||||
public async deleteTag(@Param('id') id: string) {
|
|
||||||
const originalTag = await this.tagService.getTag({
|
|
||||||
id
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!originalTag) {
|
|
||||||
throw new HttpException(
|
|
||||||
getReasonPhrase(StatusCodes.FORBIDDEN),
|
|
||||||
StatusCodes.FORBIDDEN
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.tagService.deleteTag({ id });
|
|
||||||
}
|
|
||||||
}
|
}
|
12
apps/api/src/app/endpoints/tags/tags.module.ts
Normal file
12
apps/api/src/app/endpoints/tags/tags.module.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
|
||||||
|
import { TagModule } from '@ghostfolio/api/services/tag/tag.module';
|
||||||
|
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { TagsController } from './tags.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [TagsController],
|
||||||
|
imports: [PrismaModule, TagModule]
|
||||||
|
})
|
||||||
|
export class TagsModule {}
|
@ -1,14 +0,0 @@
|
|||||||
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
|
|
||||||
|
|
||||||
import { Module } from '@nestjs/common';
|
|
||||||
|
|
||||||
import { TagController } from './tag.controller';
|
|
||||||
import { TagService } from './tag.service';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
controllers: [TagController],
|
|
||||||
exports: [TagService],
|
|
||||||
imports: [PrismaModule],
|
|
||||||
providers: [TagService]
|
|
||||||
})
|
|
||||||
export class TagModule {}
|
|
@ -1,81 +0,0 @@
|
|||||||
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { Prisma, Tag } from '@prisma/client';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TagService {
|
|
||||||
public constructor(private readonly prismaService: PrismaService) {}
|
|
||||||
|
|
||||||
public async createTag(data: Prisma.TagCreateInput) {
|
|
||||||
return this.prismaService.tag.create({
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async deleteTag(where: Prisma.TagWhereUniqueInput): Promise<Tag> {
|
|
||||||
return this.prismaService.tag.delete({ where });
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getTag(
|
|
||||||
tagWhereUniqueInput: Prisma.TagWhereUniqueInput
|
|
||||||
): Promise<Tag> {
|
|
||||||
return this.prismaService.tag.findUnique({
|
|
||||||
where: tagWhereUniqueInput
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getTags({
|
|
||||||
cursor,
|
|
||||||
orderBy,
|
|
||||||
skip,
|
|
||||||
take,
|
|
||||||
where
|
|
||||||
}: {
|
|
||||||
cursor?: Prisma.TagWhereUniqueInput;
|
|
||||||
orderBy?: Prisma.TagOrderByWithRelationInput;
|
|
||||||
skip?: number;
|
|
||||||
take?: number;
|
|
||||||
where?: Prisma.TagWhereInput;
|
|
||||||
} = {}) {
|
|
||||||
return this.prismaService.tag.findMany({
|
|
||||||
cursor,
|
|
||||||
orderBy,
|
|
||||||
skip,
|
|
||||||
take,
|
|
||||||
where
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getTagsWithActivityCount() {
|
|
||||||
const tagsWithOrderCount = await this.prismaService.tag.findMany({
|
|
||||||
include: {
|
|
||||||
_count: {
|
|
||||||
select: { orders: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return tagsWithOrderCount.map(({ _count, id, name, userId }) => {
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
userId,
|
|
||||||
activityCount: _count.orders
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async updateTag({
|
|
||||||
data,
|
|
||||||
where
|
|
||||||
}: {
|
|
||||||
data: Prisma.TagUpdateInput;
|
|
||||||
where: Prisma.TagWhereUniqueInput;
|
|
||||||
}): Promise<Tag> {
|
|
||||||
return this.prismaService.tag.update({
|
|
||||||
data,
|
|
||||||
where
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,52 @@
|
|||||||
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Prisma, Tag } from '@prisma/client';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TagService {
|
export class TagService {
|
||||||
public constructor(private readonly prismaService: PrismaService) {}
|
public constructor(private readonly prismaService: PrismaService) {}
|
||||||
|
|
||||||
|
public async createTag(data: Prisma.TagCreateInput) {
|
||||||
|
return this.prismaService.tag.create({
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteTag(where: Prisma.TagWhereUniqueInput): Promise<Tag> {
|
||||||
|
return this.prismaService.tag.delete({ where });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getTag(
|
||||||
|
tagWhereUniqueInput: Prisma.TagWhereUniqueInput
|
||||||
|
): Promise<Tag> {
|
||||||
|
return this.prismaService.tag.findUnique({
|
||||||
|
where: tagWhereUniqueInput
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getTags({
|
||||||
|
cursor,
|
||||||
|
orderBy,
|
||||||
|
skip,
|
||||||
|
take,
|
||||||
|
where
|
||||||
|
}: {
|
||||||
|
cursor?: Prisma.TagWhereUniqueInput;
|
||||||
|
orderBy?: Prisma.TagOrderByWithRelationInput;
|
||||||
|
skip?: number;
|
||||||
|
take?: number;
|
||||||
|
where?: Prisma.TagWhereInput;
|
||||||
|
} = {}) {
|
||||||
|
return this.prismaService.tag.findMany({
|
||||||
|
cursor,
|
||||||
|
orderBy,
|
||||||
|
skip,
|
||||||
|
take,
|
||||||
|
where
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async getTagsForUser(userId: string) {
|
public async getTagsForUser(userId: string) {
|
||||||
const tags = await this.prismaService.tag.findMany({
|
const tags = await this.prismaService.tag.findMany({
|
||||||
include: {
|
include: {
|
||||||
@ -41,4 +82,36 @@ export class TagService {
|
|||||||
isUsed: _count.orders > 0
|
isUsed: _count.orders > 0
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getTagsWithActivityCount() {
|
||||||
|
const tagsWithOrderCount = await this.prismaService.tag.findMany({
|
||||||
|
include: {
|
||||||
|
_count: {
|
||||||
|
select: { orders: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return tagsWithOrderCount.map(({ _count, id, name, userId }) => {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
userId,
|
||||||
|
activityCount: _count.orders
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateTag({
|
||||||
|
data,
|
||||||
|
where
|
||||||
|
}: {
|
||||||
|
data: Prisma.TagUpdateInput;
|
||||||
|
where: Prisma.TagWhereUniqueInput;
|
||||||
|
}): Promise<Tag> {
|
||||||
|
return this.prismaService.tag.update({
|
||||||
|
data,
|
||||||
|
where
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto';
|
import { CreateTagDto } from '@ghostfolio/api/app/endpoints/tags/create-tag.dto';
|
||||||
import { UpdateTagDto } from '@ghostfolio/api/app/tag/update-tag.dto';
|
import { UpdateTagDto } from '@ghostfolio/api/app/endpoints/tags/update-tag.dto';
|
||||||
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
|
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
|
||||||
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
|
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
|
||||||
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||||
|
|
||||||
@ -43,7 +42,6 @@ export class AdminTagComponent implements OnInit, OnDestroy {
|
|||||||
private unsubscribeSubject = new Subject<void>();
|
private unsubscribeSubject = new Subject<void>();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private adminService: AdminService,
|
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
private deviceService: DeviceDetectorService,
|
private deviceService: DeviceDetectorService,
|
||||||
@ -100,7 +98,7 @@ export class AdminTagComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private deleteTag(aId: string) {
|
private deleteTag(aId: string) {
|
||||||
this.adminService
|
this.dataService
|
||||||
.deleteTag(aId)
|
.deleteTag(aId)
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe({
|
.subscribe({
|
||||||
@ -116,7 +114,7 @@ export class AdminTagComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fetchTags() {
|
private fetchTags() {
|
||||||
this.adminService
|
this.dataService
|
||||||
.fetchTags()
|
.fetchTags()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((tags) => {
|
.subscribe((tags) => {
|
||||||
@ -148,7 +146,7 @@ export class AdminTagComponent implements OnInit, OnDestroy {
|
|||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((tag: CreateTagDto | null) => {
|
.subscribe((tag: CreateTagDto | null) => {
|
||||||
if (tag) {
|
if (tag) {
|
||||||
this.adminService
|
this.dataService
|
||||||
.postTag(tag)
|
.postTag(tag)
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe({
|
.subscribe({
|
||||||
@ -184,7 +182,7 @@ export class AdminTagComponent implements OnInit, OnDestroy {
|
|||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((tag: UpdateTagDto | null) => {
|
.subscribe((tag: UpdateTagDto | null) => {
|
||||||
if (tag) {
|
if (tag) {
|
||||||
this.adminService
|
this.dataService
|
||||||
.putTag(tag)
|
.putTag(tag)
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe({
|
.subscribe({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto';
|
import { CreateTagDto } from '@ghostfolio/api/app/endpoints/tags/create-tag.dto';
|
||||||
import { UpdateTagDto } from '@ghostfolio/api/app/tag/update-tag.dto';
|
import { UpdateTagDto } from '@ghostfolio/api/app/endpoints/tags/update-tag.dto';
|
||||||
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
|
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -2,7 +2,6 @@ import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interf
|
|||||||
import { GfAccountsTableModule } from '@ghostfolio/client/components/accounts-table/accounts-table.module';
|
import { GfAccountsTableModule } from '@ghostfolio/client/components/accounts-table/accounts-table.module';
|
||||||
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
|
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
|
||||||
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
|
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
|
||||||
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||||
import { NUMERICAL_PRECISION_THRESHOLD } from '@ghostfolio/common/config';
|
import { NUMERICAL_PRECISION_THRESHOLD } from '@ghostfolio/common/config';
|
||||||
@ -133,7 +132,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
|
|||||||
private unsubscribeSubject = new Subject<void>();
|
private unsubscribeSubject = new Subject<void>();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private adminService: AdminService,
|
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private dataService: DataService,
|
private dataService: DataService,
|
||||||
public dialogRef: MatDialogRef<GfHoldingDetailDialogComponent>,
|
public dialogRef: MatDialogRef<GfHoldingDetailDialogComponent>,
|
||||||
@ -162,7 +160,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (newTag && this.hasPermissionToCreateOwnTag) {
|
if (newTag && this.hasPermissionToCreateOwnTag) {
|
||||||
this.adminService
|
this.dataService
|
||||||
.postTag({ ...newTag, userId: this.user.id })
|
.postTag({ ...newTag, userId: this.user.id })
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((createdTag) => {
|
switchMap((createdTag) => {
|
||||||
|
@ -388,7 +388,9 @@
|
|||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
|
|
||||||
<gf-tags-selector
|
<gf-tags-selector
|
||||||
[hasPermissionToCreateTag]="hasPermissionToCreateOwnTag"
|
[hasPermissionToCreateTag]="
|
||||||
|
hasPermissionToCreateOwnTag && user?.settings?.isExperimentalFeatures
|
||||||
|
"
|
||||||
[readonly]="!data.hasPermissionToUpdateOrder"
|
[readonly]="!data.hasPermissionToUpdateOrder"
|
||||||
[tags]="activityForm.get('tags')?.value"
|
[tags]="activityForm.get('tags')?.value"
|
||||||
[tagsAvailable]="tagsAvailable"
|
[tagsAvailable]="tagsAvailable"
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto';
|
import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto';
|
||||||
import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto';
|
import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto';
|
||||||
import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto';
|
import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto';
|
||||||
import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto';
|
|
||||||
import { UpdateTagDto } from '@ghostfolio/api/app/tag/update-tag.dto';
|
|
||||||
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
|
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import {
|
import {
|
||||||
HEADER_KEY_SKIP_INTERCEPTOR,
|
HEADER_KEY_SKIP_INTERCEPTOR,
|
||||||
@ -25,7 +23,7 @@ import {
|
|||||||
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { SortDirection } from '@angular/material/sort';
|
import { SortDirection } from '@angular/material/sort';
|
||||||
import { DataSource, MarketData, Platform, Tag } from '@prisma/client';
|
import { DataSource, MarketData, Platform } from '@prisma/client';
|
||||||
import { JobStatus } from 'bull';
|
import { JobStatus } from 'bull';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import { switchMap } from 'rxjs';
|
import { switchMap } from 'rxjs';
|
||||||
@ -75,10 +73,6 @@ export class AdminService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteTag(aId: string) {
|
|
||||||
return this.http.delete<void>(`/api/v1/tag/${aId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public executeJob(aId: string) {
|
public executeJob(aId: string) {
|
||||||
return this.http.get<void>(`/api/v1/admin/queue/job/${aId}/execute`);
|
return this.http.get<void>(`/api/v1/admin/queue/job/${aId}/execute`);
|
||||||
}
|
}
|
||||||
@ -155,10 +149,6 @@ export class AdminService {
|
|||||||
return this.http.get<Platform[]>('/api/v1/platform');
|
return this.http.get<Platform[]>('/api/v1/platform');
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchTags() {
|
|
||||||
return this.http.get<Tag[]>('/api/v1/tag');
|
|
||||||
}
|
|
||||||
|
|
||||||
public fetchUsers({
|
public fetchUsers({
|
||||||
skip,
|
skip,
|
||||||
take = DEFAULT_PAGE_SIZE
|
take = DEFAULT_PAGE_SIZE
|
||||||
@ -261,10 +251,6 @@ export class AdminService {
|
|||||||
return this.http.post<Platform>(`/api/v1/platform`, aPlatform);
|
return this.http.post<Platform>(`/api/v1/platform`, aPlatform);
|
||||||
}
|
}
|
||||||
|
|
||||||
public postTag(aTag: CreateTagDto) {
|
|
||||||
return this.http.post<Tag>(`/api/v1/tag`, aTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
public putPlatform(aPlatform: UpdatePlatformDto) {
|
public putPlatform(aPlatform: UpdatePlatformDto) {
|
||||||
return this.http.put<Platform>(
|
return this.http.put<Platform>(
|
||||||
`/api/v1/platform/${aPlatform.id}`,
|
`/api/v1/platform/${aPlatform.id}`,
|
||||||
@ -272,10 +258,6 @@ export class AdminService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public putTag(aTag: UpdateTagDto) {
|
|
||||||
return this.http.put<Tag>(`/api/v1/tag/${aTag.id}`, aTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
public testMarketData({
|
public testMarketData({
|
||||||
dataSource,
|
dataSource,
|
||||||
scraperConfiguration,
|
scraperConfiguration,
|
||||||
|
@ -4,6 +4,8 @@ import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto
|
|||||||
import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto';
|
import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto';
|
||||||
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
|
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
|
||||||
import { UpdateBulkMarketDataDto } from '@ghostfolio/api/app/admin/update-bulk-market-data.dto';
|
import { UpdateBulkMarketDataDto } from '@ghostfolio/api/app/admin/update-bulk-market-data.dto';
|
||||||
|
import { CreateTagDto } from '@ghostfolio/api/app/endpoints/tags/create-tag.dto';
|
||||||
|
import { UpdateTagDto } from '@ghostfolio/api/app/endpoints/tags/update-tag.dto';
|
||||||
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
||||||
import {
|
import {
|
||||||
Activities,
|
Activities,
|
||||||
@ -308,6 +310,10 @@ export class DataService {
|
|||||||
return this.http.delete<any>(`/api/v1/user`, { body: aData });
|
return this.http.delete<any>(`/api/v1/user`, { body: aData });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public deleteTag(aId: string) {
|
||||||
|
return this.http.delete<void>(`/api/v1/tags/${aId}`);
|
||||||
|
}
|
||||||
|
|
||||||
public deleteUser(aId: string) {
|
public deleteUser(aId: string) {
|
||||||
return this.http.delete<any>(`/api/v1/user/${aId}`);
|
return this.http.delete<any>(`/api/v1/user/${aId}`);
|
||||||
}
|
}
|
||||||
@ -662,6 +668,10 @@ export class DataService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fetchTags() {
|
||||||
|
return this.http.get<Tag[]>('/api/v1/tags');
|
||||||
|
}
|
||||||
|
|
||||||
public loginAnonymous(accessToken: string) {
|
public loginAnonymous(accessToken: string) {
|
||||||
return this.http.post<OAuthResponse>('/api/v1/auth/anonymous', {
|
return this.http.post<OAuthResponse>('/api/v1/auth/anonymous', {
|
||||||
accessToken
|
accessToken
|
||||||
@ -709,6 +719,10 @@ export class DataService {
|
|||||||
return this.http.post<OrderModel>('/api/v1/order', aOrder);
|
return this.http.post<OrderModel>('/api/v1/order', aOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public postTag(aTag: CreateTagDto) {
|
||||||
|
return this.http.post<Tag>(`/api/v1/tags`, aTag);
|
||||||
|
}
|
||||||
|
|
||||||
public postUser() {
|
public postUser() {
|
||||||
return this.http.post<UserItem>('/api/v1/user', {});
|
return this.http.post<UserItem>('/api/v1/user', {});
|
||||||
}
|
}
|
||||||
@ -736,6 +750,10 @@ export class DataService {
|
|||||||
return this.http.put<UserItem>(`/api/v1/order/${aOrder.id}`, aOrder);
|
return this.http.put<UserItem>(`/api/v1/order/${aOrder.id}`, aOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public putTag(aTag: UpdateTagDto) {
|
||||||
|
return this.http.put<Tag>(`/api/v1/tags/${aTag.id}`, aTag);
|
||||||
|
}
|
||||||
|
|
||||||
public putUserSetting(aData: UpdateUserSettingDto) {
|
public putUserSetting(aData: UpdateUserSettingDto) {
|
||||||
return this.http.put<User>('/api/v1/user/setting', aData);
|
return this.http.put<User>('/api/v1/user/setting', aData);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user