diff --git a/app/.vs/app/v16/.suo b/app/.vs/app/v16/.suo index d867b7f..4d63784 100644 Binary files a/app/.vs/app/v16/.suo and b/app/.vs/app/v16/.suo differ diff --git a/app/.vs/slnx.sqlite b/app/.vs/slnx.sqlite index efcb274..83a2718 100644 Binary files a/app/.vs/slnx.sqlite and b/app/.vs/slnx.sqlite differ diff --git a/app/src/app/app-routing.module.ts b/app/src/app/app-routing.module.ts index b75f9e3..7c8962c 100644 --- a/app/src/app/app-routing.module.ts +++ b/app/src/app/app-routing.module.ts @@ -71,6 +71,18 @@ const appRoutes: Routes = [ icon: 'user-cog' } }, + { + path: 'help', + loadChildren: () => import('./help/help.module').then(x => x.HelpModule), + canActivate: [AuthGuardService], + canLoad: [AuthGuardService], + data: + { + title: 'help.title', + subTitle: 'help.subTitle', + icon: 'question-circle' + } + }, { path: 'unauthorized', component: UnauthorizedComponent diff --git a/app/src/app/components/nav-menu/nav-menu.component.html b/app/src/app/components/nav-menu/nav-menu.component.html index a654baf..42c3f01 100644 --- a/app/src/app/components/nav-menu/nav-menu.component.html +++ b/app/src/app/components/nav-menu/nav-menu.component.html @@ -31,13 +31,6 @@ {{ 'navbar.menu.security' | translate }} - - - - + diff --git a/app/src/app/help/help.module.ts b/app/src/app/help/help.module.ts new file mode 100644 index 0000000..7453a27 --- /dev/null +++ b/app/src/app/help/help.module.ts @@ -0,0 +1,49 @@ +import { NgModule } from '@angular/core'; + +import { SharedModule } from '../shared.module'; +import { RouterModule } from '@angular/router'; + +import { WebpackTranslateLoader } from '../helpers/webpack-translate-loader.service'; +import { LangChangeEvent, TranslateCompiler, TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'; +import { HelpComponent } from './help/help.component'; + +@NgModule({ + declarations: [HelpComponent], + imports: [ + SharedModule, + RouterModule.forChild([ + { + path: '', + component: HelpComponent, + data: + { + title: 'help.title', + subTitle: 'help.subTitle', + icon: 'help-circle' + } + } + ]), + TranslateModule.forChild({ + loader: { + provide: TranslateLoader, + //useClass: WebpackTranslateLoader + useFactory: () => new WebpackTranslateLoader('help') + }, + compiler: { + provide: TranslateCompiler, + useFactory: () => new TranslateMessageFormatCompiler() + }, + isolate: true + }) + ] +}) +export class HelpModule +{ + constructor(private readonly translate: TranslateService) + { + translate.use(translate.store.currentLang); + + translate.store.onLangChange.subscribe((event: LangChangeEvent) => translate.use(event.lang)); + } +} diff --git a/app/src/app/help/help/help.component.html b/app/src/app/help/help/help.component.html new file mode 100644 index 0000000..cef58b1 --- /dev/null +++ b/app/src/app/help/help/help.component.html @@ -0,0 +1,32 @@ +
+
+ +
+ Loading... +
+
+ +
+
+ + + +
+

{{ helpTopic.title }}

+ + +
+ +
+
+ Loading... +
+ +
+
+
+
+ +
+
+
diff --git a/app/src/app/help/help/help.component.scss b/app/src/app/help/help/help.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/app/src/app/help/help/help.component.spec.ts b/app/src/app/help/help/help.component.spec.ts new file mode 100644 index 0000000..7f33b9f --- /dev/null +++ b/app/src/app/help/help/help.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HelpComponent } from './help.component'; + +describe('HelpComponent', () => { + let component: HelpComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ HelpComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HelpComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/app/src/app/help/help/help.component.ts b/app/src/app/help/help/help.component.ts new file mode 100644 index 0000000..158217f --- /dev/null +++ b/app/src/app/help/help/help.component.ts @@ -0,0 +1,60 @@ +import { Component, OnInit } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { StaticHtmlService } from '../../helpers/static-html.service'; + +@Component({ + selector: 'app-help', + templateUrl: './help.component.html', + styleUrls: ['./help.component.scss'] +}) +export class HelpComponent implements OnInit +{ + helpTopics = [ + { + title: 'Completing account information', + contentUrl: './assets/help/account-info.html' + }, + { + title: 'Provisioning compute instance', + contentUrl: '' + }, + { + title: 'Managing instances with Triton CLI', + contentUrl: '' + } + ]; + + // ---------------------------------------------------------------------------------------------------------------- + constructor(private readonly staticHtmlService: StaticHtmlService, + private readonly domSanitizer: DomSanitizer) + { + } + + // ---------------------------------------------------------------------------------------------------------------- + getHelpTopicContent(isOpen, helpTopic) + { + helpTopic.expanded = isOpen; + + if (!isOpen || !helpTopic.contentUrl || helpTopic.content) return; + + helpTopic.loading = true; + + this.staticHtmlService + .getStaticHtml(helpTopic.contentUrl, helpTopic.contentUrl.startsWith(window.location.origin)) + .subscribe(response => + { + helpTopic.content = this.domSanitizer.bypassSecurityTrustHtml(response); + helpTopic.loading = false; + }, err => + { + helpTopic.content = err.error?.message; + helpTopic.loading = false; + }); + } + + // ---------------------------------------------------------------------------------------------------------------- + ngOnInit(): void + { + + } +} diff --git a/app/src/app/helpers/static-html.service.spec.ts b/app/src/app/helpers/static-html.service.spec.ts new file mode 100644 index 0000000..6f1a584 --- /dev/null +++ b/app/src/app/helpers/static-html.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { StaticHtmlService } from './static-html.service'; + +describe('StaticHtmlService', () => { + let service: StaticHtmlService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(StaticHtmlService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/app/src/app/helpers/static-html.service.ts b/app/src/app/helpers/static-html.service.ts new file mode 100644 index 0000000..61eae2b --- /dev/null +++ b/app/src/app/helpers/static-html.service.ts @@ -0,0 +1,30 @@ +import { Injectable, SecurityContext } from '@angular/core'; +import { Observable } from 'rxjs'; +import { HttpClient } from '@angular/common/http'; +import { DomSanitizer } from '@angular/platform-browser'; +import { map } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class StaticHtmlService +{ + // ---------------------------------------------------------------------------------------------------------------- + constructor( + private readonly httpClient: HttpClient, + private readonly domSanitizer: DomSanitizer, + ) { } + + // ---------------------------------------------------------------------------------------------------------------- + getStaticHtml(url: string, isTrusted: boolean): Observable + { + return this.httpClient.get(url, { responseType: 'text' }) + .pipe(map(response => this.mapStaticHtml(response, isTrusted))); + } + + // ---------------------------------------------------------------------------------------------------------------- + private mapStaticHtml(htmlString: string, isTrusted: boolean): string + { + return isTrusted ? htmlString : this.domSanitizer.sanitize(SecurityContext.HTML, htmlString); + } +} diff --git a/app/src/app/shared.module.ts b/app/src/app/shared.module.ts index a66bb67..28be3dd 100644 --- a/app/src/app/shared.module.ts +++ b/app/src/app/shared.module.ts @@ -33,7 +33,7 @@ import faArrowsAlt, faTags, faEllipsisV, faHatWizard, faUserCog, faCircle, faAngleLeft, faExternalLinkAlt, faCheck, faPowerOff, faBars, faSpinner, faStop, faPlay, faRedo, faMicrochip, faDesktop, faCopy, faSquare, faCheckSquare, faSave, faDatabase, faClone, faSearch, faHistory, faMask, faCloud, faCloudUploadAlt, faEye, faFingerprint, faLink, faClipboard, faCoins, faArrowRight, faEllipsisH, faStar, faCommentAlt, faOutdent, - faUndo + faUndo, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { faDocker } from '@fortawesome/free-brands-svg-icons'; @@ -149,7 +149,7 @@ export class SharedModule faArrowsAlt, faTags, faEllipsisV, faHatWizard, faUserCog, faCircle, faAngleLeft, faExternalLinkAlt, faCheck, faPowerOff, faBars, faSpinner, faStop, faPlay, faRedo, faMicrochip, faDesktop, faCopy, faSquare, faCheckSquare, faSave, faDatabase, faClone, faSearch, faHistory, faMask, faCloud, faCloudUploadAlt, faEye, faFingerprint, faLink, faClipboard, faCoins, faArrowRight, faEllipsisH, faStar, faCommentAlt, faOutdent, - faUndo + faUndo, faQuestionCircle ); } } diff --git a/app/src/assets/help/account-info.html b/app/src/assets/help/account-info.html new file mode 100644 index 0000000..51992ff --- /dev/null +++ b/app/src/assets/help/account-info.html @@ -0,0 +1 @@ +

Title goes here...

diff --git a/app/src/assets/i18n/en.json b/app/src/assets/i18n/en.json index c59f397..de27c4a 100644 --- a/app/src/assets/i18n/en.json +++ b/app/src/assets/i18n/en.json @@ -11,7 +11,8 @@ "virtualNetworks": "Virtual Networks", "firewallRules": "Firewall rules", "security": "Security", - "account": "Account" + "account": "Account", + "help": "Help" } }, "account": @@ -61,5 +62,9 @@ { "title": "Security", "subTitle": "Manage your users, roles and security policies" + }, + "help": + { + "title": "Help" } } diff --git a/app/src/assets/i18n/help/en.json b/app/src/assets/i18n/help/en.json new file mode 100644 index 0000000..8a7157a --- /dev/null +++ b/app/src/assets/i18n/help/en.json @@ -0,0 +1,7 @@ + +{ + "help": + { + "title": "Help" + } +}