From f9da50f1a19fe86c992d3e099ac8a3bf62348563 Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Mon, 25 Sep 2023 20:51:15 +0200 Subject: [PATCH] Add tag management --- apps/api/src/app/app.module.ts | 2 + apps/api/src/app/tag/create-tag.dto.ts | 6 + apps/api/src/app/tag/tag.controller.ts | 104 +++++++++ apps/api/src/app/tag/tag.module.ts | 13 ++ apps/api/src/app/tag/tag.service.ts | 79 +++++++ apps/api/src/app/tag/update-tag.dto.ts | 9 + .../admin-settings.component.html | 5 +- .../admin-settings/admin-settings.module.ts | 8 +- .../admin-tag/admin-tag.component.html | 85 ++++++++ .../admin-tag/admin-tag.component.scss | 5 + .../admin-tag/admin-tag.component.ts | 199 ++++++++++++++++++ .../components/admin-tag/admin-tag.module.ts | 26 +++ .../create-or-update-tag-dialog.component.ts | 30 +++ .../create-or-update-tag-dialog.html | 23 ++ .../create-or-update-tag-dialog.module.ts | 23 ++ .../create-or-update-tag-dialog.scss | 7 + .../interfaces/interfaces.ts | 5 + apps/client/src/app/services/admin.service.ts | 20 +- libs/common/src/lib/permissions.ts | 6 + 19 files changed, 650 insertions(+), 5 deletions(-) create mode 100644 apps/api/src/app/tag/create-tag.dto.ts create mode 100644 apps/api/src/app/tag/tag.controller.ts create mode 100644 apps/api/src/app/tag/tag.module.ts create mode 100644 apps/api/src/app/tag/tag.service.ts create mode 100644 apps/api/src/app/tag/update-tag.dto.ts create mode 100644 apps/client/src/app/components/admin-tag/admin-tag.component.html create mode 100644 apps/client/src/app/components/admin-tag/admin-tag.component.scss create mode 100644 apps/client/src/app/components/admin-tag/admin-tag.component.ts create mode 100644 apps/client/src/app/components/admin-tag/admin-tag.module.ts create mode 100644 apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.component.ts create mode 100644 apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.html create mode 100644 apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.module.ts create mode 100644 apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.scss create mode 100644 apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/interfaces/interfaces.ts diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index a521e7fa9..03c6a4aaa 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -39,6 +39,7 @@ import { RedisCacheModule } from './redis-cache/redis-cache.module'; import { SitemapModule } from './sitemap/sitemap.module'; import { SubscriptionModule } from './subscription/subscription.module'; import { SymbolModule } from './symbol/symbol.module'; +import { TagModule } from './tag/tag.module'; import { UserModule } from './user/user.module'; @Module({ @@ -101,6 +102,7 @@ import { UserModule } from './user/user.module'; SitemapModule, SubscriptionModule, SymbolModule, + TagModule, TwitterBotModule, UserModule ], diff --git a/apps/api/src/app/tag/create-tag.dto.ts b/apps/api/src/app/tag/create-tag.dto.ts new file mode 100644 index 000000000..650a0ce12 --- /dev/null +++ b/apps/api/src/app/tag/create-tag.dto.ts @@ -0,0 +1,6 @@ +import { IsString } from 'class-validator'; + +export class CreateTagDto { + @IsString() + name: string; +} diff --git a/apps/api/src/app/tag/tag.controller.ts b/apps/api/src/app/tag/tag.controller.ts new file mode 100644 index 000000000..950719201 --- /dev/null +++ b/apps/api/src/app/tag/tag.controller.ts @@ -0,0 +1,104 @@ +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import type { RequestWithUser } from '@ghostfolio/common/types'; +import { + Body, + Controller, + Delete, + Get, + HttpException, + Inject, + Param, + Post, + Put, + UseGuards +} from '@nestjs/common'; +import { REQUEST } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; +import { Tag } from '@prisma/client'; +import { StatusCodes, getReasonPhrase } from 'http-status-codes'; + +import { CreateTagDto } from './create-tag.dto'; +import { TagService } from './tag.service'; +import { UpdateTagDto } from './update-tag.dto'; + +@Controller('tag') +export class TagController { + public constructor( + @Inject(REQUEST) private readonly request: RequestWithUser, + private readonly tagService: TagService + ) {} + + @Get() + @UseGuards(AuthGuard('jwt')) + public async getTags() { + return this.tagService.getTagsWithActivityCount(); + } + + @Post() + @UseGuards(AuthGuard('jwt')) + public async createTag(@Body() data: CreateTagDto): Promise { + if (!hasPermission(this.request.user.permissions, permissions.createTag)) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + return this.tagService.createTag(data); + } + + @Put(':id') + @UseGuards(AuthGuard('jwt')) + public async updateTag(@Param('id') id: string, @Body() data: UpdateTagDto) { + if (!hasPermission(this.request.user.permissions, permissions.updateTag)) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + const originalTag = await this.tagService.getTag({ + id + }); + + if (!originalTag) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + return this.tagService.updateTag({ + data: { + ...data + }, + where: { + id + } + }); + } + + @Delete(':id') + @UseGuards(AuthGuard('jwt')) + public async deleteTag(@Param('id') id: string) { + if (!hasPermission(this.request.user.permissions, permissions.deleteTag)) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + const originalTag = await this.tagService.getTag({ + id + }); + + if (!originalTag) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + return this.tagService.deleteTag({ id }); + } +} diff --git a/apps/api/src/app/tag/tag.module.ts b/apps/api/src/app/tag/tag.module.ts new file mode 100644 index 000000000..810105c51 --- /dev/null +++ b/apps/api/src/app/tag/tag.module.ts @@ -0,0 +1,13 @@ +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 {} diff --git a/apps/api/src/app/tag/tag.service.ts b/apps/api/src/app/tag/tag.service.ts new file mode 100644 index 000000000..9da7cc475 --- /dev/null +++ b/apps/api/src/app/tag/tag.service.ts @@ -0,0 +1,79 @@ +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 { + return this.prismaService.tag.delete({ where }); + } + + public async getTag( + tagWhereUniqueInput: Prisma.TagWhereUniqueInput + ): Promise { + 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 }) => { + return { + id, + name, + activityCount: _count.orders + }; + }); + } + + public async updateTag({ + data, + where + }: { + data: Prisma.TagUpdateInput; + where: Prisma.TagWhereUniqueInput; + }): Promise { + return this.prismaService.tag.update({ + data, + where + }); + } +} diff --git a/apps/api/src/app/tag/update-tag.dto.ts b/apps/api/src/app/tag/update-tag.dto.ts new file mode 100644 index 000000000..b26ffde11 --- /dev/null +++ b/apps/api/src/app/tag/update-tag.dto.ts @@ -0,0 +1,9 @@ +import { IsString } from 'class-validator'; + +export class UpdateTagDto { + @IsString() + id: string; + + @IsString() + name: string; +} diff --git a/apps/client/src/app/components/admin-settings/admin-settings.component.html b/apps/client/src/app/components/admin-settings/admin-settings.component.html index 6f23a4056..4c4a6df1e 100644 --- a/apps/client/src/app/components/admin-settings/admin-settings.component.html +++ b/apps/client/src/app/components/admin-settings/admin-settings.component.html @@ -2,14 +2,13 @@

Platforms

- +
- diff --git a/apps/client/src/app/components/admin-settings/admin-settings.module.ts b/apps/client/src/app/components/admin-settings/admin-settings.module.ts index aaa16651f..e778c113d 100644 --- a/apps/client/src/app/components/admin-settings/admin-settings.module.ts +++ b/apps/client/src/app/components/admin-settings/admin-settings.module.ts @@ -2,12 +2,18 @@ import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { GfAdminPlatformModule } from '@ghostfolio/client/components/admin-platform/admin-platform.module'; +import { GfAdminTagModule } from '@ghostfolio/client/components/admin-tag/admin-tag.module'; import { AdminSettingsComponent } from './admin-settings.component'; @NgModule({ declarations: [AdminSettingsComponent], - imports: [CommonModule, GfAdminPlatformModule, RouterModule], + imports: [ + CommonModule, + GfAdminPlatformModule, + GfAdminTagModule, + RouterModule + ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) export class GfAdminSettingsModule {} diff --git a/apps/client/src/app/components/admin-tag/admin-tag.component.html b/apps/client/src/app/components/admin-tag/admin-tag.component.html new file mode 100644 index 000000000..d21523321 --- /dev/null +++ b/apps/client/src/app/components/admin-tag/admin-tag.component.html @@ -0,0 +1,85 @@ +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ Name + + {{ element.name }} + + Activities + + {{ element.activityCount }} + + + + + + +
+
+
+
diff --git a/apps/client/src/app/components/admin-tag/admin-tag.component.scss b/apps/client/src/app/components/admin-tag/admin-tag.component.scss new file mode 100644 index 000000000..b5b58f67e --- /dev/null +++ b/apps/client/src/app/components/admin-tag/admin-tag.component.scss @@ -0,0 +1,5 @@ +@import 'apps/client/src/styles/ghostfolio-style'; + +:host { + display: block; +} diff --git a/apps/client/src/app/components/admin-tag/admin-tag.component.ts b/apps/client/src/app/components/admin-tag/admin-tag.component.ts new file mode 100644 index 000000000..48eb25554 --- /dev/null +++ b/apps/client/src/app/components/admin-tag/admin-tag.component.ts @@ -0,0 +1,199 @@ +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + OnDestroy, + OnInit, + ViewChild +} from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { ActivatedRoute, Router } from '@angular/router'; +import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto'; +import { UpdateTagDto } from '@ghostfolio/api/app/tag/update-tag.dto'; +import { AdminService } from '@ghostfolio/client/services/admin.service'; +import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { Tag } from '@prisma/client'; +import { get } from 'lodash'; +import { DeviceDetectorService } from 'ngx-device-detector'; +import { Subject, takeUntil } from 'rxjs'; + +import { CreateOrUpdateTagDialog } from './create-or-update-tag-dialog/create-or-update-tag-dialog.component'; + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'gf-admin-tag', + styleUrls: ['./admin-tag.component.scss'], + templateUrl: './admin-tag.component.html' +}) +export class AdminTagComponent implements OnInit, OnDestroy { + @ViewChild(MatSort) sort: MatSort; + + public dataSource: MatTableDataSource = new MatTableDataSource(); + public deviceType: string; + public displayedColumns = ['name', 'activities', 'actions']; + public tags: Tag[]; + + private unsubscribeSubject = new Subject(); + + public constructor( + private adminService: AdminService, + private changeDetectorRef: ChangeDetectorRef, + private deviceService: DeviceDetectorService, + private dialog: MatDialog, + private route: ActivatedRoute, + private router: Router, + private userService: UserService + ) { + this.route.queryParams + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((params) => { + if (params['createTagDialog']) { + this.openCreateTagDialog(); + } else if (params['editTagDialog']) { + if (this.tags) { + const tag = this.tags.find(({ id }) => { + return id === params['tagId']; + }); + + this.openUpdateTagDialog(tag); + } else { + this.router.navigate(['.'], { relativeTo: this.route }); + } + } + }); + } + + public ngOnInit() { + this.deviceType = this.deviceService.getDeviceInfo().deviceType; + + this.fetchTags(); + } + + public onDeleteTag(aId: string) { + const confirmation = confirm( + $localize`Do you really want to delete this tag?` + ); + + if (confirmation) { + this.deleteTag(aId); + } + } + + public onUpdateTag({ id }: Tag) { + this.router.navigate([], { + queryParams: { editTagDialog: true, tagId: id } + }); + } + + public ngOnDestroy() { + this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); + } + + private deleteTag(aId: string) { + this.adminService + .deleteTag(aId) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe({ + next: () => { + this.userService + .get(true) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + + this.fetchTags(); + } + }); + } + + private fetchTags() { + this.adminService + .fetchTags() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((tags) => { + this.tags = tags; + this.dataSource = new MatTableDataSource(this.tags); + this.dataSource.sort = this.sort; + this.dataSource.sortingDataAccessor = get; + + this.changeDetectorRef.markForCheck(); + }); + } + + private openCreateTagDialog() { + const dialogRef = this.dialog.open(CreateOrUpdateTagDialog, { + data: { + tag: { + name: null + } + }, + height: this.deviceType === 'mobile' ? '97.5vh' : '80vh', + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); + + dialogRef + .afterClosed() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((data) => { + const tag: CreateTagDto = data?.tag; + + if (tag) { + this.adminService + .postTag(tag) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe({ + next: () => { + this.userService + .get(true) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + + this.fetchTags(); + } + }); + } + + this.router.navigate(['.'], { relativeTo: this.route }); + }); + } + + private openUpdateTagDialog({ id, name }) { + const dialogRef = this.dialog.open(CreateOrUpdateTagDialog, { + data: { + tag: { + id, + name + } + }, + height: this.deviceType === 'mobile' ? '97.5vh' : '80vh', + width: this.deviceType === 'mobile' ? '100vw' : '50rem' + }); + + dialogRef + .afterClosed() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((data) => { + const tag: UpdateTagDto = data?.tag; + + if (tag) { + this.adminService + .putTag(tag) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe({ + next: () => { + this.userService + .get(true) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + + this.fetchTags(); + } + }); + } + + this.router.navigate(['.'], { relativeTo: this.route }); + }); + } +} diff --git a/apps/client/src/app/components/admin-tag/admin-tag.module.ts b/apps/client/src/app/components/admin-tag/admin-tag.module.ts new file mode 100644 index 000000000..aec5ac5a6 --- /dev/null +++ b/apps/client/src/app/components/admin-tag/admin-tag.module.ts @@ -0,0 +1,26 @@ +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatSortModule } from '@angular/material/sort'; +import { MatTableModule } from '@angular/material/table'; +import { RouterModule } from '@angular/router'; + +import { AdminTagComponent } from './admin-tag.component'; +import { GfCreateOrUpdateTagDialogModule } from './create-or-update-tag-dialog/create-or-update-tag-dialog.module'; + +@NgModule({ + declarations: [AdminTagComponent], + exports: [AdminTagComponent], + imports: [ + CommonModule, + GfCreateOrUpdateTagDialogModule, + MatButtonModule, + MatMenuModule, + MatSortModule, + MatTableModule, + RouterModule + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class GfAdminTagModule {} diff --git a/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.component.ts b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.component.ts new file mode 100644 index 000000000..aaa5a0221 --- /dev/null +++ b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.component.ts @@ -0,0 +1,30 @@ +import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Subject } from 'rxjs'; + +import { CreateOrUpdateTagDialogParams } from './interfaces/interfaces'; + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'h-100' }, + selector: 'gf-create-or-update-tag-dialog', + styleUrls: ['./create-or-update-tag-dialog.scss'], + templateUrl: 'create-or-update-tag-dialog.html' +}) +export class CreateOrUpdateTagDialog { + private unsubscribeSubject = new Subject(); + + public constructor( + @Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateTagDialogParams, + public dialogRef: MatDialogRef + ) {} + + public onCancel() { + this.dialogRef.close(); + } + + public ngOnDestroy() { + this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); + } +} diff --git a/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.html b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.html new file mode 100644 index 000000000..c2e8f4ee1 --- /dev/null +++ b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.html @@ -0,0 +1,23 @@ +
+

Update tag

+

Add tag

+
+
+ + Name + + +
+
+
+ + +
+
diff --git a/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.module.ts b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.module.ts new file mode 100644 index 000000000..d8b12edc2 --- /dev/null +++ b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.module.ts @@ -0,0 +1,23 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; + +import { CreateOrUpdateTagDialog } from './create-or-update-tag-dialog.component'; + +@NgModule({ + declarations: [CreateOrUpdateTagDialog], + imports: [ + CommonModule, + FormsModule, + MatButtonModule, + MatDialogModule, + MatFormFieldModule, + MatInputModule, + ReactiveFormsModule + ] +}) +export class GfCreateOrUpdateTagDialogModule {} diff --git a/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.scss b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.scss new file mode 100644 index 000000000..b63df0134 --- /dev/null +++ b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/create-or-update-tag-dialog.scss @@ -0,0 +1,7 @@ +:host { + display: block; + + .mat-mdc-dialog-content { + max-height: unset; + } +} diff --git a/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/interfaces/interfaces.ts new file mode 100644 index 000000000..bd7214786 --- /dev/null +++ b/apps/client/src/app/components/admin-tag/create-or-update-tag-dialog/interfaces/interfaces.ts @@ -0,0 +1,5 @@ +import { Tag } from '@prisma/client'; + +export interface CreateOrUpdateTagDialogParams { + tag: Tag; +} diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index 3f72ff01d..e62641db7 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -4,6 +4,8 @@ import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-pr import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto'; import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-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 { DATE_FORMAT } from '@ghostfolio/common/helper'; import { @@ -15,7 +17,7 @@ import { Filter, UniqueAsset } from '@ghostfolio/common/interfaces'; -import { DataSource, MarketData, Platform, Prisma } from '@prisma/client'; +import { DataSource, MarketData, Platform, Prisma, Tag } from '@prisma/client'; import { JobStatus } from 'bull'; import { format, parseISO } from 'date-fns'; import { Observable, map } from 'rxjs'; @@ -64,6 +66,10 @@ export class AdminService { ); } + public deleteTag(aId: string) { + return this.http.delete(`/api/v1/tag/${aId}`); + } + public fetchAdminData() { return this.http.get('/api/v1/admin'); } @@ -139,6 +145,10 @@ export class AdminService { return this.http.get('/api/v1/platform'); } + public fetchTags() { + return this.http.get('/api/v1/tag'); + } + public gather7Days() { return this.http.post('/api/v1/admin/gather', {}); } @@ -208,6 +218,10 @@ export class AdminService { return this.http.post(`/api/v1/platform`, aPlatform); } + public postTag(aTag: CreateTagDto) { + return this.http.post(`/api/v1/tag`, aTag); + } + public putMarketData({ dataSource, date, @@ -233,4 +247,8 @@ export class AdminService { aPlatform ); } + + public putTag(aTag: UpdateTagDto) { + return this.http.put(`/api/v1/tag/${aTag.id}`, aTag); + } } diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index 30bd627bc..0c2e8578e 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -7,12 +7,14 @@ export const permissions = { createAccount: 'createAccount', createOrder: 'createOrder', createPlatform: 'createPlatform', + createTag: 'createTag', createUserAccount: 'createUserAccount', deleteAccess: 'deleteAccess', deleteAccount: 'deleteAcccount', deleteAuthDevice: 'deleteAuthDevice', deleteOrder: 'deleteOrder', deletePlatform: 'deletePlatform', + deleteTag: 'deleteTag', deleteUser: 'deleteUser', enableFearAndGreedIndex: 'enableFearAndGreedIndex', enableImport: 'enableImport', @@ -29,6 +31,7 @@ export const permissions = { updateAuthDevice: 'updateAuthDevice', updateOrder: 'updateOrder', updatePlatform: 'updatePlatform', + updateTag: 'updateTag', updateUserSettings: 'updateUserSettings', updateViewMode: 'updateViewMode' }; @@ -42,16 +45,19 @@ export function getPermissions(aRole: Role): string[] { permissions.createAccount, permissions.createOrder, permissions.createPlatform, + permissions.createTag, permissions.deleteAccess, permissions.deleteAccount, permissions.deleteAuthDevice, permissions.deleteOrder, permissions.deletePlatform, + permissions.deleteTag, permissions.deleteUser, permissions.updateAccount, permissions.updateAuthDevice, permissions.updateOrder, permissions.updatePlatform, + permissions.updateTag, permissions.updateUserSettings, permissions.updateViewMode ];