Early mockup of admin management for policies

This commit is contained in:
Travis Ralston 2019-07-01 20:49:44 -06:00
parent e8d876b5d2
commit 77d34197c9
12 changed files with 241 additions and 5 deletions

21
package-lock.json generated
View File

@ -912,6 +912,21 @@
"to-fast-properties": "^2.0.0"
}
},
"@ckeditor/ckeditor5-angular": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-angular/-/ckeditor5-angular-1.1.0.tgz",
"integrity": "sha512-6b9NX/PhFuQKo/mR0tSpCVNGU1fhg1Y8ju5OBWxCPpIbdDZIIoqjHlhWL76EmfztteouYLACsnfrnqVQGC0utA==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"@ckeditor/ckeditor5-build-classic": {
"version": "12.2.0",
"resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-build-classic/-/ckeditor5-build-classic-12.2.0.tgz",
"integrity": "sha512-En64jC5ImZoa+XLa2JrZYCKpq2iNXhdf17xgmpJoAXp+m36EqSHd6/4x0XT/pjrZSDxrcLUZxyZJlQCRtSaeHw==",
"dev": true
},
"@fortawesome/fontawesome": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome/-/fontawesome-1.1.8.tgz",
@ -6139,6 +6154,12 @@
"integrity": "sha1-ojR7o2DeGeM9D/1ZD933dVy/LmQ=",
"dev": true
},
"iso-639-1": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.0.5.tgz",
"integrity": "sha512-2TcJ8AcsqM4AXLi92eFZX3xa7X6Eno/chq9yOR0AvSgb15Smmoh1miXyYJVWCkSmbzDimds3Ix2M4efhnOuxOg==",
"dev": true
},
"isobject": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",

View File

@ -70,6 +70,8 @@
"@angularclass/hmr-loader": "^3.0.4",
"@babel/core": "^7.4.5",
"@babel/preset-env": "^7.4.5",
"@ckeditor/ckeditor5-angular": "^1.1.0",
"@ckeditor/ckeditor5-build-classic": "^12.2.0",
"@fortawesome/fontawesome": "^1.1.8",
"@fortawesome/fontawesome-free-brands": "^5.0.13",
"@fortawesome/fontawesome-free-regular": "^5.0.13",
@ -92,6 +94,7 @@
"goby": "^1.1.2",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"iso-639-1": "^2.0.5",
"jquery": "^3.4.1",
"json-loader": "^0.5.7",
"mini-css-extract-plugin": "^0.7.0",

View File

@ -0,0 +1,70 @@
<div *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div *ngIf="!isLoading">
<my-ibox boxTitle="Policy information">
<div class="my-ibox-content">
<label class="label-block">
Name
<span class="text-muted ">The name of your policy, in English. Example: "Terms of Service"</span>
<input type="text" class="form-control"
placeholder="My Policy" (keyup)="onNameKeyUp()"
[(ngModel)]="languages['en'].name" [disabled]="isUpdating"/>
</label>
<label class="label-block">
Shortcode
<span class="text-muted ">This is automatically generated to give the policy a unique identifier.</span>
<input type="text" class="form-control"
[(ngModel)]="shortcode" [disabled]="true"/>
</label>
</div>
</my-ibox>
<my-ibox>
<div class="my-ibox-title">
<h5>English version</h5>
</div>
<div class="my-ibox-content">
<label class="label-block">
Policy text
<span class="text-muted ">This is where you put your policy's content.</span>
</label>
<ckeditor [editor]="Editor" [(ngModel)]="languages['en'].text"></ckeditor>
</div>
</my-ibox>
<my-ibox *ngFor="let code of otherLanguageCodes">
<div class="my-ibox-title">
<h5>{{languages[code].langName}} version</h5>
</div>
<div class="my-ibox-content">
<label class="label-block">
Name
<span class="text-muted ">The translated name of your policy</span>
<input type="text" class="form-control"
placeholder="My Policy"
[(ngModel)]="languages[code].name" [disabled]="isUpdating"/>
</label>
<label class="label-block">
Policy text
<span class="text-muted ">This is where you put your policy's content.</span>
</label>
<ckeditor [editor]="Editor" [(ngModel)]="languages[code].text" [disabled]="isUpdating"></ckeditor>
</div>
</my-ibox>
<my-ibox [hasTitle]="false">
<div class="my-ibox-content buttons">
<select [(ngModel)]="chosenLanguage" [disabled]="isUpdating" class="form-control form-control-sm">
<option *ngFor="let lang of availableLanguages" [ngValue]="lang.code">{{lang.name}}</option>
</select>
<button type="button" (click)="addLanguage()" title="save" class="btn btn-outline-success btn-sm">
<i class="fa fa-plus"></i> Add language
</button>
</div>
</my-ibox>
<my-ibox [hasTitle]="false">
<div class="my-ibox-content buttons">
<button type="button" (click)="create()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> Create draft
</button>
</div>
</my-ibox>
</div>

View File

@ -0,0 +1,8 @@
.buttons button {
margin-right: 5px;
}
.buttons select {
max-width: 200px;
margin-bottom: 5px;
}

View File

@ -0,0 +1,95 @@
import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster";
import { AdminTermsApiService } from "../../../shared/services/admin/admin-terms-api.service";
import { ActivatedRoute, Router } from "@angular/router";
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import ISO6391 from "iso-639-1";
@Component({
templateUrl: "./new.component.html",
styleUrls: ["./new.component.scss"],
})
export class AdminNewTermsComponent implements OnInit {
public Editor = ClassicEditor;
public isLoading = true;
public isUpdating = false;
public shortcode: string;
public takenShortcodes: string[];
public chosenLanguage: string = ISO6391.getAllCodes()[0];
public languages: {
[languageCode: string]: {
name: string,
text: string,
langName: string,
url: string,
isExternal: boolean,
externalUrl: string,
}
} = {
"en": {
name: "",
text: "",
langName: "English",
url: "", // TODO: Calculate
isExternal: false,
externalUrl: "",
},
};
public get otherLanguageCodes(): string[] {
return Object.keys(this.languages).filter(c => c !== "en");
}
public get availableLanguages(): { name: string, code: string }[] {
return ISO6391.getAllCodes()
.filter(c => !this.otherLanguageCodes.includes(c))
.map(c => {
return {code: c, name: ISO6391.getName(c)};
});
}
constructor(private adminTerms: AdminTermsApiService,
private toaster: ToasterService,
private router: Router,
private activatedRoute: ActivatedRoute) {
}
public ngOnInit() {
this.adminTerms.getAllPolicies().then(policies => {
this.takenShortcodes = policies.map(p => p.shortcode);
this.isLoading = false;
}).catch(err => {
console.error(err);
this.toaster.pop("error", "Failed to load policies");
});
}
public onNameKeyUp() {
const startShortcode = this.languages['en'].name.toLowerCase().replace(/[^a-z0-9]/gi, '_');
let shortcode = startShortcode;
let i = 0;
while (this.takenShortcodes.includes(shortcode)) {
shortcode = `${startShortcode}_${++i}`;
}
this.shortcode = shortcode;
}
public create() {
this.router.navigate([".."], {relativeTo: this.activatedRoute});
}
public addLanguage() {
this.languages[this.chosenLanguage] = {
name: "",
text: "",
url: "", // TODO: Calculate
isExternal: false,
externalUrl: "",
langName: ISO6391.getName(this.chosenLanguage),
};
this.chosenLanguage = this.availableLanguages[0].code;
}
}

View File

@ -15,7 +15,7 @@
<thead>
<tr>
<th>Policy Name</th>
<th>Published Version</th>
<th>Version</th>
<th class="text-center" style="width: 120px;">Actions</th>
</tr>
</thead>
@ -35,6 +35,9 @@
</tr>
</tbody>
</table>
<button type="button" class="btn btn-success btn-sm" (click)="createPolicy()">
<i class="fa fa-plus"></i> New draft policy
</button>
</div>
</my-ibox>
</div>

View File

@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster";
import { FE_TermsEditable } from "../../shared/models/terms";
import { AdminTermsApiService } from "../../shared/services/admin/admin-terms-api.service";
import { ActivatedRoute, Router } from "@angular/router";
@Component({
templateUrl: "./terms.component.html",
@ -13,12 +14,17 @@ export class AdminTermsComponent implements OnInit {
public policies: FE_TermsEditable[];
constructor(private adminTerms: AdminTermsApiService,
private toaster: ToasterService) {
private toaster: ToasterService,
private router: Router,
private activatedRoute: ActivatedRoute) {
}
public ngOnInit() {
this.adminTerms.getAllPolicies().then(policies => {
this.policies = policies;
this.policies = [
...policies.filter(p => p.version === "draft"),
...policies.filter(p => p.version !== "draft"),
];
this.isLoading = false;
}).catch(err => {
console.error(err);
@ -26,4 +32,8 @@ export class AdminTermsComponent implements OnInit {
});
}
public createPolicy() {
this.router.navigate(["new"], {relativeTo: this.activatedRoute});
}
}

View File

@ -114,6 +114,8 @@ import { ReauthExampleWidgetWrapperComponent } from "./widget-wrappers/reauth-ex
import { ManagerTestWidgetWrapperComponent } from "./widget-wrappers/manager-test/manager-test.component";
import { AdminTermsApiService } from "./shared/services/admin/admin-terms-api.service";
import { AdminTermsComponent } from "./admin/terms/terms.component";
import { AdminNewTermsComponent } from "./admin/terms/new/new.component";
import { CKEditorModule } from "@ckeditor/ckeditor5-angular";
@NgModule({
imports: [
@ -128,6 +130,7 @@ import { AdminTermsComponent } from "./admin/terms/terms.component";
ModalModule.forRoot(),
BootstrapModalModule,
BreadcrumbsModule,
CKEditorModule,
],
declarations: [
AppComponent,
@ -207,6 +210,7 @@ import { AdminTermsComponent } from "./admin/terms/terms.component";
ReauthExampleWidgetWrapperComponent,
ManagerTestWidgetWrapperComponent,
AdminTermsComponent,
AdminNewTermsComponent,
// Vendor
],

View File

@ -45,6 +45,7 @@ import { SlackBridgeConfigComponent } from "./configs/bridge/slack/slack.bridge.
import { ReauthExampleWidgetWrapperComponent } from "./widget-wrappers/reauth-example/reauth-example.component";
import { ManagerTestWidgetWrapperComponent } from "./widget-wrappers/manager-test/manager-test.component";
import { AdminTermsComponent } from "./admin/terms/terms.component";
import { AdminNewTermsComponent } from "./admin/terms/new/new.component";
const routes: Routes = [
{path: "", component: HomeComponent},
@ -155,6 +156,11 @@ const routes: Routes = [
path: "",
component: AdminTermsComponent,
},
{
path: "new",
component: AdminNewTermsComponent,
data: {breadcrumb: "New policy", name: "New policy"},
},
],
},
],

View File

@ -1,6 +1,7 @@
<div class="ibox">
<div class="ibox-title" (click)="isCollapsible ? (collapsed = !collapsed) : false"
[ngClass]="[isCollapsible ? 'ibox-title-collapsible' : '']">
[ngClass]="[isCollapsible ? 'ibox-title-collapsible' : '']"
*ngIf="hasTitle">
<h5 *ngIf="boxTitle">
{{ boxTitle }}
</h5>

View File

@ -11,6 +11,7 @@ export class IboxComponent implements OnInit {
@Input() boxTitle: string;
@Input() isCollapsible: boolean;
@Input() defaultCollapsed: boolean;
@Input() hasTitle = true;
public collapsed = false;

View File

@ -2,7 +2,21 @@ import { FE_Integration } from "./models/integration";
export class SessionStorage {
public static scalarToken: string;
private static _scalarToken: string;
public static get scalarToken(): string {
if (this._scalarToken) return this._scalarToken;
this.scalarToken = localStorage.getItem("dimension_scalar_token");
return this._scalarToken;
}
public static set scalarToken(val: string) {
this._scalarToken = val;
if (val) {
localStorage.setItem("dimension_scalar_token", val);
}
}
public static userId: string;
public static roomId: string;
public static isAdmin: boolean;