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 }}
-
@@ -45,24 +38,12 @@
{{ 'navbar.menu.images' | 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 @@
+
+
+
+
+
+
+
+
+
+
{{ helpTopic.title }}
+
+
+
+
+
+
+
+
+
+
+
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"
+ }
+}