help page

This commit is contained in:
Dragos 2021-05-06 20:49:12 +03:00
parent e9f4934bf0
commit a99c39c5a4
15 changed files with 245 additions and 27 deletions

Binary file not shown.

Binary file not shown.

View File

@ -71,6 +71,18 @@ const appRoutes: Routes = [
icon: 'user-cog' 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', path: 'unauthorized',
component: UnauthorizedComponent component: UnauthorizedComponent

View File

@ -31,13 +31,6 @@
{{ 'navbar.menu.security' | translate }} {{ 'navbar.menu.security' | translate }}
</a> </a>
</li> </li>
<!--<li class="dropdown-divider"></li>
<li class="nav-item">
<a class="nav-link" [routerLink]="['./file-manager']" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: true }">
<fa-icon [fixedWidth]="true" icon="folder"></fa-icon>
{{ 'navbar.menu.fileManager' | translate }}
</a>
</li>-->
<li class="dropdown-divider"></li> <li class="dropdown-divider"></li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" [routerLink]="['./catalog/images']" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: true }"> <a class="nav-link" [routerLink]="['./catalog/images']" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: true }">
@ -45,24 +38,12 @@
{{ 'navbar.menu.images' | translate }} {{ 'navbar.menu.images' | translate }}
</a> </a>
</li> </li>
<!--<li class="nav-item"> <li class="dropdown-divider"></li>
<a class="nav-link" [routerLink]="['./catalog/docker-images']" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: true }">
<fa-icon [fixedWidth]="true" [icon]="['fab', 'docker']"></fa-icon>
{{ 'navbar.menu.dockerImages' | translate }}
</a>
</li>-->
<!--<li class="nav-item">
<a class="nav-link" [routerLink]="['./catalog/docker-registry']" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: true }">
<fa-icon [fixedWidth]="true" [icon]="['fab', 'docker']"></fa-icon>
{{ 'navbar.menu.dockerRegistry' | translate }}
</a>
</li>-->
<!--<li class="dropdown-divider"></li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" [routerLink]="['./account']" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: true }"> <a class="nav-link" [routerLink]="['./help']" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: true }">
<fa-icon [fixedWidth]="true" icon="user-cog"></fa-icon> <fa-icon [fixedWidth]="true" icon="question-circle"></fa-icon>
{{ 'navbar.menu.account' | translate }} {{ 'navbar.menu.help' | translate }}
</a> </a>
</li>--> </li>
</ul> </ul>
</nav> </nav>

View File

@ -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));
}
}

View File

@ -0,0 +1,32 @@
<div class="d-flex flex-column h-100">
<div class="container text-center mt-1">
<div class="spinner-border text-center text-info text-faded" role="status" *ngIf="loadingIndicator">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div class="overflow-auto flex-grow-1 my-3">
<div class="container my-2">
<accordion [closeOthers]="true">
<accordion-group *ngFor="let helpTopic of helpTopics" (isOpenChange)="getHelpTopicContent($event, helpTopic)">
<div class="d-flex justify-content-between align-items-center sticky-top" accordion-heading>
<h4 class="mb-0 text-info">{{ helpTopic.title }}</h4>
<fa-icon icon="angle-right" [fixedWidth]="true" [rotate]="helpTopic.expanded ? 90 : 0" class="text-info"></fa-icon>
</div>
<div class="p-3">
<div class="spinner-border text-center text-info text-faded" role="status" *ngIf="helpTopic.loading">
<span class="visually-hidden">Loading...</span>
</div>
<article [innerHtml]="helpTopic.content"></article>
</div>
</accordion-group>
</accordion>
</div>
</div>
</div>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HelpComponent } from './help.component';
describe('HelpComponent', () => {
let component: HelpComponent;
let fixture: ComponentFixture<HelpComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HelpComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(HelpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -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
{
}
}

View File

@ -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();
});
});

View File

@ -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<string>
{
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);
}
}

View File

@ -33,7 +33,7 @@ import
faArrowsAlt, faTags, faEllipsisV, faHatWizard, faUserCog, faCircle, faAngleLeft, faExternalLinkAlt, faCheck, faPowerOff, faBars, faSpinner, 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, 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, faCloud, faCloudUploadAlt, faEye, faFingerprint, faLink, faClipboard, faCoins, faArrowRight, faEllipsisH, faStar, faCommentAlt, faOutdent,
faUndo faUndo, faQuestionCircle
} from '@fortawesome/free-solid-svg-icons'; } from '@fortawesome/free-solid-svg-icons';
import { faDocker } from '@fortawesome/free-brands-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, 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, 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, faCloud, faCloudUploadAlt, faEye, faFingerprint, faLink, faClipboard, faCoins, faArrowRight, faEllipsisH, faStar, faCommentAlt, faOutdent,
faUndo faUndo, faQuestionCircle
); );
} }
} }

View File

@ -0,0 +1 @@
<h1>Title goes here...</h1>

View File

@ -11,7 +11,8 @@
"virtualNetworks": "Virtual Networks", "virtualNetworks": "Virtual Networks",
"firewallRules": "Firewall rules", "firewallRules": "Firewall rules",
"security": "Security", "security": "Security",
"account": "Account" "account": "Account",
"help": "Help"
} }
}, },
"account": "account":
@ -61,5 +62,9 @@
{ {
"title": "Security", "title": "Security",
"subTitle": "Manage your users, roles and security policies" "subTitle": "Manage your users, roles and security policies"
},
"help":
{
"title": "Help"
} }
} }

View File

@ -0,0 +1,7 @@
{
"help":
{
"title": "Help"
}
}