From f649cbdf5280a449c8156514c5ab8e63c594a01c Mon Sep 17 00:00:00 2001 From: Adam Hlavacek Date: Thu, 29 Aug 2024 17:53:37 +0200 Subject: [PATCH] feat(admin): instance config --- src/api/backend/api/default.service.ts | 101 ++++++++++++++++++ src/api/backend/model/adminInstanceConfig.ts | 16 +++ .../model/adminInstanceConfigResponse.ts | 16 +++ src/api/backend/model/models.ts | 2 + .../components/admin/admin-routing.module.ts | 2 + .../admin-sidebar.component.html | 3 + .../admin-sidebar/admin-sidebar.component.ts | 2 +- src/app/components/admin/admin.module.ts | 9 +- .../page-admin-instance-config.component.html | 45 ++++++++ .../page-admin-instance-config.component.scss | 15 +++ ...ge-admin-instance-config.component.spec.ts | 25 +++++ .../page-admin-instance-config.component.ts | 54 ++++++++++ .../admin-section-card.component.ts | 7 +- .../page-admin-root.component.html | 1 + .../page-admin-root.component.ts | 4 +- src/app/models/routes.ts | 1 + src/assets/i18n/cs.json | 16 ++- src/routes/routes.cs.ts | 3 +- src/routes/routes.ts | 5 +- 19 files changed, 317 insertions(+), 10 deletions(-) create mode 100644 src/api/backend/model/adminInstanceConfig.ts create mode 100644 src/api/backend/model/adminInstanceConfigResponse.ts create mode 100644 src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.html create mode 100644 src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.scss create mode 100644 src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.spec.ts create mode 100644 src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.ts diff --git a/src/api/backend/api/default.service.ts b/src/api/backend/api/default.service.ts index 8f73b29b..e420442e 100644 --- a/src/api/backend/api/default.service.ts +++ b/src/api/backend/api/default.service.ts @@ -22,6 +22,8 @@ import { AchievementGrantRequest } from '../model/achievementGrantRequest'; import { AchievementGrantResponse } from '../model/achievementGrantResponse'; import { AchievementResponse } from '../model/achievementResponse'; import { AchievementsResponse } from '../model/achievementsResponse'; +import { AdminInstanceConfig } from '../model/adminInstanceConfig'; +import { AdminInstanceConfigResponse } from '../model/adminInstanceConfigResponse'; import { AdminTaskCreationRequest } from '../model/adminTaskCreationRequest'; import { AdminTaskDeployResponse } from '../model/adminTaskDeployResponse'; import { AdminTaskMergeResponse } from '../model/adminTaskMergeResponse'; @@ -2969,6 +2971,105 @@ export class DefaultService { ); } + /** + * + * + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public instanceConfigGetAll(observe?: 'body', reportProgress?: boolean): Observable; + public instanceConfigGetAll(observe?: 'response', reportProgress?: boolean): Observable>; + public instanceConfigGetAll(observe?: 'events', reportProgress?: boolean): Observable>; + public instanceConfigGetAll(observe: any = 'body', reportProgress: boolean = false ): Observable { + + let headers = this.defaultHeaders; + + // authentication (ksi) required + if (this.configuration.accessToken) { + const accessToken = typeof this.configuration.accessToken === 'function' + ? this.configuration.accessToken() + : this.configuration.accessToken; + headers = headers.set('Authorization', 'Bearer ' + accessToken); + } + + // to determine the Accept header + let httpHeaderAccepts: string[] = [ + 'application/json' + ]; + const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + if (httpHeaderAcceptSelected != undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + // to determine the Content-Type header + const consumes: string[] = [ + ]; + + return this.httpClient.request('get',`${this.basePath}/admin/instanceConfig`, + { + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * + * + * @param body + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public instanceConfigSetSingle(body: AdminInstanceConfig, observe?: 'body', reportProgress?: boolean): Observable; + public instanceConfigSetSingle(body: AdminInstanceConfig, observe?: 'response', reportProgress?: boolean): Observable>; + public instanceConfigSetSingle(body: AdminInstanceConfig, observe?: 'events', reportProgress?: boolean): Observable>; + public instanceConfigSetSingle(body: AdminInstanceConfig, observe: any = 'body', reportProgress: boolean = false ): Observable { + + if (body === null || body === undefined) { + throw new Error('Required parameter body was null or undefined when calling instanceConfigSetSingle.'); + } + + let headers = this.defaultHeaders; + + // authentication (ksi) required + if (this.configuration.accessToken) { + const accessToken = typeof this.configuration.accessToken === 'function' + ? this.configuration.accessToken() + : this.configuration.accessToken; + headers = headers.set('Authorization', 'Bearer ' + accessToken); + } + + // to determine the Accept header + let httpHeaderAccepts: string[] = [ + 'application/json' + ]; + const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + if (httpHeaderAcceptSelected != undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + // to determine the Content-Type header + const consumes: string[] = [ + 'application/json' + ]; + const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); + if (httpContentTypeSelected != undefined) { + headers = headers.set('Content-Type', httpContentTypeSelected); + } + + return this.httpClient.request('post',`${this.basePath}/admin/instanceConfig`, + { + body: body, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + /** * * diff --git a/src/api/backend/model/adminInstanceConfig.ts b/src/api/backend/model/adminInstanceConfig.ts new file mode 100644 index 00000000..3c240844 --- /dev/null +++ b/src/api/backend/model/adminInstanceConfig.ts @@ -0,0 +1,16 @@ +/** + * web-backend-swagger + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +export interface AdminInstanceConfig { + key: string; + value: string; +} \ No newline at end of file diff --git a/src/api/backend/model/adminInstanceConfigResponse.ts b/src/api/backend/model/adminInstanceConfigResponse.ts new file mode 100644 index 00000000..a2e2f536 --- /dev/null +++ b/src/api/backend/model/adminInstanceConfigResponse.ts @@ -0,0 +1,16 @@ +/** + * web-backend-swagger + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 1.0.0 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ +import { AdminInstanceConfig } from './adminInstanceConfig'; + +export interface AdminInstanceConfigResponse { + config: Array; +} \ No newline at end of file diff --git a/src/api/backend/model/models.ts b/src/api/backend/model/models.ts index f0ae9c7a..b12e4ef6 100644 --- a/src/api/backend/model/models.ts +++ b/src/api/backend/model/models.ts @@ -6,6 +6,8 @@ export * from './achievementGrantRequest'; export * from './achievementGrantResponse'; export * from './achievementResponse'; export * from './achievementsResponse'; +export * from './adminInstanceConfig'; +export * from './adminInstanceConfigResponse'; export * from './adminTask'; export * from './adminTaskCreation'; export * from './adminTaskCreationRequest'; diff --git a/src/app/components/admin/admin-routing.module.ts b/src/app/components/admin/admin-routing.module.ts index d9494a5a..ae2b7cff 100644 --- a/src/app/components/admin/admin-routing.module.ts +++ b/src/app/components/admin/admin-routing.module.ts @@ -7,6 +7,7 @@ import { PageAdminMonitorComponent } from './page-admin-monitor/page-admin-monit import { PageAdminEmailComponent } from './page-admin-email/page-admin-email.component'; import {PageAdminDiscussionComponent} from './page-admin-discussion/page-admin-discussion.component'; import {PageAdminAchievementsComponent} from './page-admin-achievements/page-admin-achievements.component'; +import {PageAdminInstanceConfigComponent} from './page-admin-instance-config/page-admin-instance-config.component'; const routes: Routes = [ {path: '', component: PageAdminRootComponent}, @@ -15,6 +16,7 @@ const routes: Routes = [ {path: ROUTES.admin.email, component: PageAdminEmailComponent}, {path: ROUTES.admin.discussion, component: PageAdminDiscussionComponent}, {path: ROUTES.admin.achievements, component: PageAdminAchievementsComponent}, + {path: ROUTES.admin.instanceConfig, component: PageAdminInstanceConfigComponent}, ]; @NgModule({ diff --git a/src/app/components/admin/admin-sidebar/admin-sidebar.component.html b/src/app/components/admin/admin-sidebar/admin-sidebar.component.html index aa3d0581..ba961a4b 100644 --- a/src/app/components/admin/admin-sidebar/admin-sidebar.component.html +++ b/src/app/components/admin/admin-sidebar/admin-sidebar.component.html @@ -18,6 +18,9 @@ {{'admin.navbar.achievement' | translate}} + + {{'admin.navbar.instance-config' | translate}} + +
+
+

{{'admin.instance-config.title'|translate}}

+ + + + + + + + + + + + + + + +
{{'admin.instance-config.key' | translate}}{{'admin.instance-config.value' | translate}}{{'admin.instance-config.actions' | translate}}
{{config.key}} + + {{config.value}} + + + {{'admin.instance-config.null-value' | translate}} + + + +
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
diff --git a/src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.scss b/src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.scss new file mode 100644 index 00000000..fe61d30c --- /dev/null +++ b/src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.scss @@ -0,0 +1,15 @@ +@import "src/app/styles/vars"; +@import "src/app/styles/mixins"; + +@include page-admin; + +:host { + .content { + justify-self: center; + + > .title { + margin: $ksi-margin; + text-align: center; + } + } +} diff --git a/src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.spec.ts b/src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.spec.ts new file mode 100644 index 00000000..8ee47041 --- /dev/null +++ b/src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PageAdminInstanceConfigComponent } from './page-admin-instance-config.component'; + +describe('PageAdminInstanceConfigComponent', () => { + let component: PageAdminInstanceConfigComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PageAdminInstanceConfigComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PageAdminInstanceConfigComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.ts b/src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.ts new file mode 100644 index 00000000..b4531ee3 --- /dev/null +++ b/src/app/components/admin/page-admin-instance-config/page-admin-instance-config.component.ts @@ -0,0 +1,54 @@ +import {Component, OnInit, ChangeDetectionStrategy, ViewChild, TemplateRef} from '@angular/core'; +import {BackendService, KsiTitleService, ModalService} from '../../../services'; +import {BehaviorSubject, Subject} from 'rxjs'; +import {AdminInstanceConfig} from '../../../../api/backend'; +import {OpenedTemplate} from '../../../models'; + +@Component({ + selector: 'ksi-page-admin-instance-config', + templateUrl: './page-admin-instance-config.component.html', + styleUrls: ['./page-admin-instance-config.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class PageAdminInstanceConfigComponent implements OnInit { + private configSubject: Subject = new BehaviorSubject(([] as AdminInstanceConfig[])); + config$ = this.configSubject.asObservable(); + + editedConfig: AdminInstanceConfig | null = null; + @ViewChild('modalEditConfig', {static: true}) + modalEditConfig: TemplateRef; + modalEditConfigInstance: OpenedTemplate | null = null; + + constructor( + private title: KsiTitleService, + private backend: BackendService, + private modal: ModalService + ) { } + + ngOnInit(): void { + this.title.subtitle = 'admin.root.instance-config.title'; + this.refreshConfig(); + } + + private refreshConfig(): void { + this.backend.http.instanceConfigGetAll().subscribe((response) => { + this.configSubject.next(response.config); + }); + } + + editConfig(config: AdminInstanceConfig): void { + this.editedConfig = config; + this.modalEditConfigInstance = this.modal.showModalTemplate(this.modalEditConfig, 'admin.root.instance-config.edit-config'); + } + + saveConfig(editedConfig: AdminInstanceConfig | null): void { + if (editedConfig) { + this.editedConfig = null; + this.backend.http.instanceConfigSetSingle(editedConfig).subscribe(() => { + this.refreshConfig(); + this.modalEditConfigInstance?.template.instance.close(); + this.modalEditConfigInstance = null; + }); + } + } +} diff --git a/src/app/components/admin/page-admin-root/admin-section-card/admin-section-card.component.ts b/src/app/components/admin/page-admin-root/admin-section-card/admin-section-card.component.ts index 81f13878..48847a7c 100644 --- a/src/app/components/admin/page-admin-root/admin-section-card/admin-section-card.component.ts +++ b/src/app/components/admin/page-admin-root/admin-section-card/admin-section-card.component.ts @@ -18,8 +18,13 @@ export class AdminSectionCardComponent implements OnInit { } ngOnInit(): void { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - this.relativeUrl = ROUTES.admin[this.name]; + this.relativeUrl = ROUTES.admin[this.toCamelCase(this.name)]; + } + + private toCamelCase(str: string): string { + return str.replace(/-./g, match => match.charAt(1).toUpperCase()); } } diff --git a/src/app/components/admin/page-admin-root/page-admin-root.component.html b/src/app/components/admin/page-admin-root/page-admin-root.component.html index 82c733d5..153cbf9a 100644 --- a/src/app/components/admin/page-admin-root/page-admin-root.component.html +++ b/src/app/components/admin/page-admin-root/page-admin-root.component.html @@ -8,6 +8,7 @@

{{'admin.root.title'|translate}}

+ diff --git a/src/app/components/admin/page-admin-root/page-admin-root.component.ts b/src/app/components/admin/page-admin-root/page-admin-root.component.ts index 4e1785ce..0a1b5888 100644 --- a/src/app/components/admin/page-admin-root/page-admin-root.component.ts +++ b/src/app/components/admin/page-admin-root/page-admin-root.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; -import { KsiTitleService } from '../../../services'; +import {KsiTitleService, UserService} from '../../../services'; @Component({ selector: 'ksi-page-admin-root', @@ -9,7 +9,7 @@ import { KsiTitleService } from '../../../services'; }) export class PageAdminRootComponent implements OnInit { - constructor(private title: KsiTitleService) { } + constructor(private title: KsiTitleService, public userService: UserService) { } ngOnInit(): void { this.title.subtitle = 'admin.root.title'; diff --git a/src/app/models/routes.ts b/src/app/models/routes.ts index c94ddc2b..1bced790 100644 --- a/src/app/models/routes.ts +++ b/src/app/models/routes.ts @@ -25,6 +25,7 @@ export interface IRoutes { email: string; discussion: string; achievements: string; + instanceConfig: string; }; privacyPolicy: string; } diff --git a/src/assets/i18n/cs.json b/src/assets/i18n/cs.json index dc9f1925..27516102 100644 --- a/src/assets/i18n/cs.json +++ b/src/assets/i18n/cs.json @@ -465,7 +465,8 @@ "monitor": "Monitor", "email": "E-mail", "discussion": "Diskuze", - "achievement": "Trofeje" + "achievement": "Trofeje", + "instance-config": "Server Admin" }, "root": { "title": "Organizároské rozhraní", @@ -488,11 +489,24 @@ "email": { "title": "Poslat email", "description": "Zasílání emailů řešitelům ohledně nových vln a dalších akcích." + }, + "instance-config": { + "title": "Admin serveru", + "description": "Přímé nastavení základních hodnot serveru." } }, "monitor": { "new-window": "Otevřít v novém panelu" }, + "instance-config": { + "title": "Nastavení serveru", + "key": "Klíč", + "value": "Hodnota", + "save": "Uložit změny", + "actions": "Akce", + "null-value": "Nulová hodnota", + "edit": "Upravit" + }, "email": { "summary": "Tento email budu posílat {{successful}} {{sex}} {{school}} účastnících se ročníku {{year}}. Tento email je {{type}}. Pokročilé možnosti {{advancedUsed}} nastaveny.", "title": "Posílání emailů", diff --git a/src/routes/routes.cs.ts b/src/routes/routes.cs.ts index 50b4f463..2790a48c 100644 --- a/src/routes/routes.cs.ts +++ b/src/routes/routes.cs.ts @@ -27,7 +27,8 @@ export const ROUTES: IRoutes = { monitor: 'monitor', email: 'email', discussion: 'forum', - achievements: 'trofeje' + achievements: 'trofeje', + instanceConfig: 'nastaveni-serveru' }, privacyPolicy: 'zpracovani-osobnich-udaju' }; diff --git a/src/routes/routes.ts b/src/routes/routes.ts index aa1819b0..ee99c5e8 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -24,7 +24,10 @@ export const ROUTES: IRoutes = { _: 'admin', tasks: 'tasks', monitor: 'monitor', - email: 'email' + email: 'email', + discussion: 'discussion', + achievements: 'achievements', + instanceConfig: 'instanceConfig' }, privacyPolicy: 'privacy' };