mirror of
https://github.com/sartography/cr-connect-bpmn.git
synced 2025-02-25 22:55:32 +00:00
Adds navbar, footer, and Material theme
This commit is contained in:
parent
f9a47e36f7
commit
fe1a481f43
@ -26,10 +26,9 @@
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"node_modules/bpmn-js/dist/assets/diagram-js.css",
|
||||
"node_modules/bpmn-js/dist/assets/bpmn-font/css/bpmn.css",
|
||||
"src/styles.css"
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
@ -145,8 +144,7 @@
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"src/styles.css"
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [],
|
||||
"assets": [
|
||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -11641,9 +11641,9 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sartography-workflow-lib": {
|
||||
"version": "0.0.45",
|
||||
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.45.tgz",
|
||||
"integrity": "sha512-0c3XT5ID0gF8SM18DmD88jDwOccjaViEhJ6DWzh9irblPmvbeA/B7a1MS0i0fm6IC3pw7tdja9pBFeDXz0UOZw==",
|
||||
"version": "0.0.46",
|
||||
"resolved": "https://registry.npmjs.org/sartography-workflow-lib/-/sartography-workflow-lib-0.0.46.tgz",
|
||||
"integrity": "sha512-gxm3mfSyQ/g9Axen4w0HAH+s3mQARVANN5KfPQZgSnvT1wP/ymnhZDikg/hfbUJQZ5qwMv+phKK53HeAsexU3w==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
|
@ -47,7 +47,7 @@
|
||||
"file-saver": "^2.0.2",
|
||||
"hammerjs": "^2.0.8",
|
||||
"rxjs": "~6.5.4",
|
||||
"sartography-workflow-lib": "^0.0.45",
|
||||
"sartography-workflow-lib": "^0.0.46",
|
||||
"tslib": "^1.10.0",
|
||||
"uuid": "^3.4.0",
|
||||
"zone.js": "~0.9.1"
|
||||
|
67
src/_config.scss
Normal file
67
src/_config.scss
Normal file
@ -0,0 +1,67 @@
|
||||
// GLOBAL SCSS VARIABLES
|
||||
$body-font-family: '"franklin-gothic-urw", sans-serif';
|
||||
$heading-font-family: '"franklin-gothic-urw-cond", sans-serif';
|
||||
|
||||
// COLOR PALETTE
|
||||
|
||||
// gray
|
||||
$brand-gray: #4e4e4e;
|
||||
$brand-gray-tint-1: #666666;
|
||||
$brand-gray-tint-2: #DADADA;
|
||||
$brand-gray-tint-3: #F1F1EF;
|
||||
$brand-gray-tint-4: scale-color($brand-gray, $lightness: 90%);
|
||||
$body-color: $brand-gray;
|
||||
$body-color-muted: $brand-gray-tint-1;
|
||||
$body-color-light: $brand-gray-tint-4;
|
||||
$brand-gray-muted: $brand-gray-tint-1;
|
||||
$brand-gray-light: $brand-gray-tint-4;
|
||||
|
||||
// primary (UVA "Jefferson Blue")
|
||||
$brand-primary: #232D4B;
|
||||
$brand-primary-tint-1: #394E79;
|
||||
$brand-primary-tint-2: #6C799C;
|
||||
$brand-primary-tint-3: #A9AFC7;
|
||||
$brand-primary-tint-4: scale-color($brand-primary, $lightness: 90%);
|
||||
$brand-primary-shade-1: #092255;
|
||||
$brand-primary-shade-2: #041D4F;
|
||||
$brand-primary-shade-3: #02194A;
|
||||
$brand-primary-shade-4: #021745;
|
||||
$brand-primary-shade-5: #03143E;
|
||||
$brand-primary-muted: $brand-primary-tint-1;
|
||||
$brand-primary-light: $brand-primary-tint-4;
|
||||
|
||||
// accent (UVA "Rotunda Orange")
|
||||
$brand-accent: #E57200;
|
||||
$brand-accent-tint-1: #F69350;
|
||||
$brand-accent-tint-2: #FAB584;
|
||||
$brand-accent-tint-3: #FDD8BB;
|
||||
$brand-accent-tint-4: scale-color($brand-accent, $lightness: 90%);
|
||||
$brand-accent-shade-1: #E76E25;
|
||||
$brand-accent-shade-2: #DD6923;
|
||||
$brand-accent-shade-3: #D36421;
|
||||
$brand-accent-shade-4: #C8601F;
|
||||
$brand-accent-shade-5: #C05B1D;
|
||||
$brand-accent-muted: $brand-accent-tint-1;
|
||||
$brand-accent-light: $brand-accent-tint-4;
|
||||
|
||||
// warning (UVA "Emergency Red")
|
||||
$brand-warning: #DF1E43;
|
||||
$brand-warning-muted: desaturate(scale-color($brand-warning, $lightness: 30%), 20%);
|
||||
$brand-warning-light: scale-color($brand-warning, $lightness: 90%);
|
||||
$easeDuration: 300ms;
|
||||
$animationDuration: 500ms;
|
||||
$tile-height-1x: 226px;
|
||||
$tile-height-2x: 288px;
|
||||
$uva-header-height: 40px;
|
||||
$site-header-height: 64px;
|
||||
$search-bar-height-sm: 64px;
|
||||
$search-bar-height-md: 128px;
|
||||
$search-bar-height-lg: 64px;
|
||||
$header-height-sm: ($uva-header-height + $site-header-height + $search-bar-height-sm);
|
||||
$header-height-md: ($uva-header-height + $site-header-height + $search-bar-height-md);
|
||||
$header-height-lg: ($uva-header-height + $site-header-height + $search-bar-height-lg);
|
||||
|
||||
// Green
|
||||
$brand-green: #64B343;
|
||||
$brand-green-muted: #8EC774;
|
||||
$brand-green-light: #B5D9A3;
|
92
src/_material.scss
Normal file
92
src/_material.scss
Normal file
@ -0,0 +1,92 @@
|
||||
@import 'config';
|
||||
@import '../node_modules/@angular/material/theming';
|
||||
|
||||
// Define a custom typography config that overrides the font-family
|
||||
$custom-typography: mat-typography-config(
|
||||
$font-family: $body-font-family,
|
||||
$display-4: mat-typography-level(2.500rem, 1.0, 700, $heading-font-family),
|
||||
$display-3: mat-typography-level(2.250rem, 1.0, 700, $heading-font-family),
|
||||
$display-2: mat-typography-level(2.000rem, 1.0, 700, $heading-font-family),
|
||||
$display-1: mat-typography-level(1.750rem, 1.0, 700, $heading-font-family),
|
||||
$headline: mat-typography-level(48px, 1.0, 700, $heading-font-family),
|
||||
$title: mat-typography-level(1.500rem, 1.0, 700, $body-font-family),
|
||||
$subheading-2: mat-typography-level(1.375rem, 1.0, 500, $body-font-family),
|
||||
$subheading-1: mat-typography-level(1.250rem, 1.0, 500, $body-font-family),
|
||||
$body-2: mat-typography-level(1.000rem, 1.5, 500, $body-font-family),
|
||||
$body-1: mat-typography-level(1.000rem, 1.5, 500, $body-font-family),
|
||||
$caption: mat-typography-level(1.000rem, 1.5, 500, $body-font-family),
|
||||
$button: mat-typography-level(1.000rem, 1.5, 500, $body-font-family),
|
||||
$input: mat-typography-level(1.000rem, 1.5, 500, $body-font-family)
|
||||
);
|
||||
|
||||
$mat-blue: (
|
||||
50: #f1f5f7,
|
||||
100: #b3c1d3,
|
||||
200: $brand-primary-tint-3,
|
||||
300: $brand-primary-tint-2,
|
||||
400: $brand-primary-tint-1,
|
||||
500: $brand-primary,
|
||||
600: $brand-primary-shade-1,
|
||||
700: $brand-primary-shade-2,
|
||||
800: $brand-primary-shade-3,
|
||||
900: $brand-primary-shade-4,
|
||||
A100: #b3c1d3,
|
||||
A200: $brand-primary-tint-3,
|
||||
A400: $brand-primary-tint-2,
|
||||
A700: $brand-primary-tint-1,
|
||||
contrast: (
|
||||
50: $body-color,
|
||||
100: $body-color,
|
||||
200: $body-color,
|
||||
300: #ffffff,
|
||||
400: #ffffff,
|
||||
500: #ffffff,
|
||||
600: #ffffff,
|
||||
700: #ffffff,
|
||||
800: #ffffff,
|
||||
900: #ffffff,
|
||||
A100: $body-color,
|
||||
A200: #ffffff,
|
||||
A400: #ffffff,
|
||||
A700: #ffffff,
|
||||
)
|
||||
);
|
||||
|
||||
$mat-orange: (
|
||||
50: #fceee0,
|
||||
100: $brand-accent-tint-3,
|
||||
200: $brand-accent-tint-2,
|
||||
300: $brand-accent-tint-1,
|
||||
400: $brand-accent,
|
||||
500: $brand-accent-shade-1,
|
||||
600: $brand-accent-shade-2,
|
||||
700: $brand-accent-shade-3,
|
||||
800: $brand-accent-shade-4,
|
||||
900: $brand-accent-shade-5,
|
||||
A100: #fceee0,
|
||||
A200: $brand-accent-tint-3,
|
||||
A400: $brand-accent-tint-2,
|
||||
A700: $brand-accent-tint-1,
|
||||
contrast: (
|
||||
50: $body-color,
|
||||
100: $body-color,
|
||||
200: $body-color,
|
||||
300: $body-color,
|
||||
400: $body-color,
|
||||
500: #ffffff,
|
||||
600: #ffffff,
|
||||
700: #ffffff,
|
||||
800: #ffffff,
|
||||
900: #ffffff,
|
||||
A100: $body-color,
|
||||
A200: $body-color,
|
||||
A400: $body-color,
|
||||
A700: $body-color,
|
||||
)
|
||||
);
|
||||
|
||||
$cr-connect-primary: mat-palette($mat-blue);
|
||||
$cr-connect-accent: mat-palette($mat-orange);
|
||||
$cr-connect-warn: mat-palette($mat-red);
|
||||
$cr-connect-theme: mat-light-theme($cr-connect-primary, $cr-connect-accent, $cr-connect-warn);
|
||||
|
4
src/app/_forms/validators/phone.regex.ts
Normal file
4
src/app/_forms/validators/phone.regex.ts
Normal file
@ -0,0 +1,4 @@
|
||||
// tslint:disable-next-line:max-line-length
|
||||
export const PHONE_REGEX = /^(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?$/i;
|
||||
|
||||
export default PHONE_REGEX;
|
23
src/app/_forms/validators/url.regex.ts
Normal file
23
src/app/_forms/validators/url.regex.ts
Normal file
@ -0,0 +1,23 @@
|
||||
export const URL_REGEX = new RegExp(
|
||||
'^' +
|
||||
'(?:(?:https?|ftp)://)' +
|
||||
'(?:\\S+(?::\\S*)?@)?' +
|
||||
'(?:' +
|
||||
'(?!(?:10|127)(?:\\.\\d{1,3}){3})' +
|
||||
'(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})' +
|
||||
'(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})' +
|
||||
'(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' +
|
||||
'(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' +
|
||||
'(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))' +
|
||||
'|' +
|
||||
'(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' +
|
||||
'(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' +
|
||||
'(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' +
|
||||
'\\.?' +
|
||||
')' +
|
||||
'(?::\\d{2,5})?' +
|
||||
'(?:[/?#]\\S*)?' +
|
||||
'$', 'i'
|
||||
);
|
||||
|
||||
export default URL_REGEX;
|
31
src/app/_forms/validators/url.validator.ts
Normal file
31
src/app/_forms/validators/url.validator.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { AbstractControl, ValidationErrors } from '@angular/forms';
|
||||
|
||||
export function ValidateUrl(control: AbstractControl): ValidationErrors {
|
||||
|
||||
const urlRegEx = new RegExp(
|
||||
'^' +
|
||||
'(?:(?:https?|ftp)://)' +
|
||||
'(?:\\S+(?::\\S*)?@)?' +
|
||||
'(?:' +
|
||||
'(?!(?:10|127)(?:\\.\\d{1,3}){3})' +
|
||||
'(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})' +
|
||||
'(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})' +
|
||||
'(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' +
|
||||
'(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' +
|
||||
'(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))' +
|
||||
'|' +
|
||||
'(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' +
|
||||
'(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' +
|
||||
'(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' +
|
||||
'\\.?' +
|
||||
')' +
|
||||
'(?::\\d{2,5})?' +
|
||||
'(?:[/?#]\\S*)?' +
|
||||
'$', 'i'
|
||||
);
|
||||
|
||||
if (!urlRegEx.test(control.value) && control.value && control.value !== '') {
|
||||
const error: ValidationErrors = { url: true };
|
||||
return error;
|
||||
}
|
||||
}
|
@ -1,16 +1,25 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {SessionRedirectComponent} from 'sartography-workflow-lib';
|
||||
import {HomeComponent} from './home/home.component';
|
||||
import {ModelerComponent} from './modeler/modeler.component';
|
||||
import {SignInComponent} from './sign-in/sign-in.component';
|
||||
import {SignOutComponent} from './sign-out/sign-out.component';
|
||||
import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list.component';
|
||||
|
||||
|
||||
const appRoutes: Routes = [
|
||||
{path: 'modeler/:workflowSpecId', component: ModelerComponent},
|
||||
{path: 'modeler/:workflowSpecId/:fileMetaId', component: ModelerComponent},
|
||||
{path: '', component: WorkflowSpecListComponent},
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: HomeComponent
|
||||
},
|
||||
{
|
||||
path: 'modeler/:workflowSpecId',
|
||||
component: ModelerComponent
|
||||
},
|
||||
{
|
||||
path: 'modeler/:workflowSpecId/:fileMetaId',
|
||||
component: ModelerComponent
|
||||
},
|
||||
{
|
||||
path: 'sign-in',
|
||||
component: SignInComponent
|
||||
@ -28,7 +37,11 @@ const appRoutes: Routes = [
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
RouterModule.forRoot(appRoutes)
|
||||
RouterModule.forRoot(routes, {
|
||||
scrollPositionRestoration: 'enabled',
|
||||
anchorScrolling: 'enabled',
|
||||
scrollOffset: [0, 84],
|
||||
})
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
|
@ -1 +1,5 @@
|
||||
<router-outlet></router-outlet>
|
||||
<div class="mat-typography">
|
||||
<app-navbar *ngIf="isSignedIn()" class="mat-elevation-z6" id="globalHeader"></app-navbar>
|
||||
<router-outlet></router-outlet>
|
||||
<app-footer></app-footer>
|
||||
</div>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {Component} from '@angular/core';
|
||||
import {isSignedIn} from 'sartography-workflow-lib';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -7,4 +8,5 @@ import { Component } from '@angular/core';
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'CR Connect Configuration';
|
||||
isSignedIn = isSignedIn;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import {BrowserModule} from '@angular/platform-browser';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {FormlyModule} from '@ngx-formly/core';
|
||||
import {FormlyMaterialModule} from '@ngx-formly/material';
|
||||
import {AppEnvironment, AuthInterceptor} from 'sartography-workflow-lib';
|
||||
import {AppEnvironment, AuthInterceptor, SessionRedirectComponent} from 'sartography-workflow-lib';
|
||||
import {environment} from '../environments/environment';
|
||||
import {DeleteFileDialogComponent} from './_dialogs/delete-file-dialog/delete-file-dialog.component';
|
||||
import {DeleteWorkflowSpecDialogComponent} from './_dialogs/delete-workflow-spec-dialog/delete-workflow-spec-dialog.component';
|
||||
@ -33,10 +33,13 @@ import {AppRoutingModule} from './app-routing.module';
|
||||
import {AppComponent} from './app.component';
|
||||
import {DiagramComponent} from './diagram/diagram.component';
|
||||
import {FileListComponent} from './file-list/file-list.component';
|
||||
import {FooterComponent} from './footer/footer.component';
|
||||
import {ModelerComponent} from './modeler/modeler.component';
|
||||
import {NavbarComponent} from './navbar/navbar.component';
|
||||
import {SignInComponent} from './sign-in/sign-in.component';
|
||||
import {SignOutComponent} from './sign-out/sign-out.component';
|
||||
import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list.component';
|
||||
import { HomeComponent } from './home/home.component';
|
||||
|
||||
export class ThisEnvironment implements AppEnvironment {
|
||||
production = environment.production;
|
||||
@ -69,14 +72,18 @@ export class AppFormlyConfig {
|
||||
DiagramComponent,
|
||||
FileListComponent,
|
||||
FileMetaDialogComponent,
|
||||
FooterComponent,
|
||||
GetIconCodePipe,
|
||||
ModelerComponent,
|
||||
NavbarComponent,
|
||||
NewFileDialogComponent,
|
||||
OpenFileDialogComponent,
|
||||
SessionRedirectComponent,
|
||||
SignInComponent,
|
||||
SignOutComponent,
|
||||
WorkflowSpecDialogComponent,
|
||||
WorkflowSpecListComponent,
|
||||
HomeComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
|
3
src/app/footer/footer.component.html
Normal file
3
src/app/footer/footer.component.html
Normal file
@ -0,0 +1,3 @@
|
||||
<footer>
|
||||
CR Connect - University of Virginia
|
||||
</footer>
|
8
src/app/footer/footer.component.scss
Normal file
8
src/app/footer/footer.component.scss
Normal file
@ -0,0 +1,8 @@
|
||||
@import "../../_config.scss";
|
||||
|
||||
footer {
|
||||
background-color: $brand-gray;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
25
src/app/footer/footer.component.spec.ts
Normal file
25
src/app/footer/footer.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { FooterComponent } from './footer.component';
|
||||
|
||||
describe('FooterComponent', () => {
|
||||
let component: FooterComponent;
|
||||
let fixture: ComponentFixture<FooterComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ FooterComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FooterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
15
src/app/footer/footer.component.ts
Normal file
15
src/app/footer/footer.component.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
templateUrl: './footer.component.html',
|
||||
styleUrls: ['./footer.component.scss']
|
||||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
2
src/app/home/home.component.html
Normal file
2
src/app/home/home.component.html
Normal file
@ -0,0 +1,2 @@
|
||||
<app-sign-in *ngIf="!isSignedIn()"></app-sign-in>
|
||||
<app-workflow-spec-list *ngIf="isSignedIn()"></app-workflow-spec-list>
|
0
src/app/home/home.component.scss
Normal file
0
src/app/home/home.component.scss
Normal file
25
src/app/home/home.component.spec.ts
Normal file
25
src/app/home/home.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HomeComponent } from './home.component';
|
||||
|
||||
describe('HomeComponent', () => {
|
||||
let component: HomeComponent;
|
||||
let fixture: ComponentFixture<HomeComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ HomeComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HomeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
15
src/app/home/home.component.ts
Normal file
15
src/app/home/home.component.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {isSignedIn} from 'sartography-workflow-lib';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss']
|
||||
})
|
||||
export class HomeComponent {
|
||||
isSignedIn = isSignedIn;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
}
|
42
src/app/navbar/navbar.component.html
Normal file
42
src/app/navbar/navbar.component.html
Normal file
@ -0,0 +1,42 @@
|
||||
<nav *ngIf="isSignedIn() && user">
|
||||
<a
|
||||
[routerLink]="['/']"
|
||||
class="site-title mat-display-1"
|
||||
mat-button
|
||||
>CR Connect</a>
|
||||
<div class="flex-spacer"></div>
|
||||
<ng-container *ngFor="let link of navLinks">
|
||||
<a *ngIf="!link.links"
|
||||
[id]="link.id"
|
||||
[ngClass]="{'active': isLinkActive(link.path)}"
|
||||
[routerLink]="link.path"
|
||||
mat-button
|
||||
>
|
||||
{{link.label}}
|
||||
</a>
|
||||
|
||||
<ng-container *ngIf="link.links">
|
||||
<button
|
||||
[attr.aria-label]="link.label"
|
||||
[id]="link.id"
|
||||
[matMenuTriggerFor]="menu"
|
||||
mat-button
|
||||
>
|
||||
<mat-icon>{{link.icon}}</mat-icon>
|
||||
{{link.label}}
|
||||
</button>
|
||||
<mat-menu #menu="matMenu" xPosition="before">
|
||||
<button
|
||||
*ngFor="let menuLink of link.links"
|
||||
[id]="menuLink.id"
|
||||
[ngClass]="{'active': isLinkActive(menuLink.path)}"
|
||||
[routerLink]="menuLink.path"
|
||||
mat-menu-item
|
||||
>
|
||||
<mat-icon *ngIf="menuLink.icon">{{menuLink.icon}}</mat-icon>
|
||||
<span>{{menuLink.label}}</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</nav>
|
35
src/app/navbar/navbar.component.scss
Normal file
35
src/app/navbar/navbar.component.scss
Normal file
@ -0,0 +1,35 @@
|
||||
@import "../../config";
|
||||
|
||||
.flex-spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
background-color: $brand-primary;
|
||||
color: white;
|
||||
height: 48px;
|
||||
|
||||
.site-title {
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
line-height: 48px;
|
||||
margin-bottom: 0;
|
||||
background-color: $brand-primary;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
a.active {
|
||||
background-color: $brand-primary-tint-1;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.mat-menu-item.active {
|
||||
background-color: $brand-gray-tint-2;
|
||||
color: $brand-gray;
|
||||
font-weight: bold;
|
||||
}
|
56
src/app/navbar/navbar.component.spec.ts
Normal file
56
src/app/navbar/navbar.component.spec.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {Router} from '@angular/router';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {ApiService, MockEnvironment, mockUser} from 'sartography-workflow-lib';
|
||||
|
||||
import { NavbarComponent } from './navbar.component';
|
||||
|
||||
describe('NavbarComponent', () => {
|
||||
let component: NavbarComponent;
|
||||
let fixture: ComponentFixture<NavbarComponent>;
|
||||
let httpMock: HttpTestingController;
|
||||
const mockRouter = {navigate: jasmine.createSpy('navigate')};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
NavbarComponent
|
||||
],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
RouterTestingModule,
|
||||
],
|
||||
providers: [
|
||||
ApiService,
|
||||
{
|
||||
provide: Router,
|
||||
useValue: mockRouter
|
||||
},
|
||||
{provide: 'APP_ENVIRONMENT', useClass: MockEnvironment},
|
||||
],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.setItem('token', 'some_token');
|
||||
httpMock = TestBed.get(HttpTestingController);
|
||||
fixture = TestBed.createComponent(NavbarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
||||
const uReq = httpMock.expectOne('apiRoot/user');
|
||||
expect(uReq.request.method).toEqual('GET');
|
||||
uReq.flush(mockUser);
|
||||
expect(component.user).toEqual(mockUser);
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
67
src/app/navbar/navbar.component.ts
Normal file
67
src/app/navbar/navbar.component.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {ApiService, isSignedIn, User} from 'sartography-workflow-lib';
|
||||
|
||||
interface NavItem {
|
||||
path?: string;
|
||||
id: string;
|
||||
label: string;
|
||||
icon?: string;
|
||||
links?: NavItem[];
|
||||
action?: () => void;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-navbar',
|
||||
templateUrl: './navbar.component.html',
|
||||
styleUrls: ['./navbar.component.scss']
|
||||
})
|
||||
export class NavbarComponent implements OnInit {
|
||||
navLinks: NavItem[];
|
||||
user: User;
|
||||
isSignedIn = isSignedIn;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private api: ApiService,
|
||||
) {
|
||||
this._loadUser();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
isLinkActive(path: string) {
|
||||
return path === this.router.url;
|
||||
}
|
||||
|
||||
private _loadUser() {
|
||||
if (isSignedIn()) {
|
||||
this.api.getUser().subscribe(u => {
|
||||
this.user = u;
|
||||
this._loadNavLinks();
|
||||
}, error => {
|
||||
localStorage.removeItem('token');
|
||||
this.api.openUrl('/');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _loadNavLinks() {
|
||||
const displayName = this.user.display_name || this.user.first_name || this.user.last_name;
|
||||
this.navLinks = [
|
||||
{path: '/', id: 'nav_home', label: 'Home'},
|
||||
{path: '/inbox', id: 'nav_inbox', label: 'Inbox'},
|
||||
{path: '/help', id: 'nav_help', label: 'Help'},
|
||||
{
|
||||
id: 'nav_account', label: `${displayName} (${this.user.email_address})`,
|
||||
icon: 'account_circle',
|
||||
links: [
|
||||
{path: '/profile', id: 'nav_profile', label: 'Profile', icon: 'person'},
|
||||
{path: '/notifications', id: 'nav_notifications', label: 'Notifications', icon: 'notifications'},
|
||||
{path: '/sign-out', id: 'nav_sign_out', label: 'Sign out', icon: 'exit_to_app'},
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
278
src/material-theme.scss
Normal file
278
src/material-theme.scss
Normal file
@ -0,0 +1,278 @@
|
||||
@import "material";
|
||||
|
||||
@include mat-core($custom-typography);
|
||||
@include angular-material-theme($cr-connect-theme);
|
||||
|
||||
@mixin cr-connect-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$accent: map-get($theme, accent);
|
||||
|
||||
h1 {
|
||||
text-transform: uppercase;
|
||||
color: $brand-accent;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding-top: 64px;
|
||||
}
|
||||
|
||||
mat-radio-button, .mat-checkbox {
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
formly-field mat-form-field {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
formly-field.textarea-cols {
|
||||
display: flow-root;
|
||||
|
||||
& > formly-wrapper-mat-form-field > mat-form-field {
|
||||
width: auto !important;
|
||||
|
||||
.mat-form-field-infix {
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formly-field.read-only {
|
||||
.mat-form-field-outline {
|
||||
background-color: $brand-gray-tint-2;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
formly-field.vertical-radio-group {
|
||||
mat-radio-button {
|
||||
margin: 5px;
|
||||
padding-right: 16px;
|
||||
display: block;
|
||||
|
||||
label.mat-radio-label {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formly-field.vertical-checkbox-group {
|
||||
.mat-checkbox {
|
||||
margin: 5px;
|
||||
padding-right: 16px;
|
||||
display: block;
|
||||
|
||||
.mat-checkbox-layout {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.full-height {
|
||||
width: 100%;
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
|
||||
.container, .row {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
//.container {
|
||||
// display: grid;
|
||||
// justify-content: center;
|
||||
// justify-items: center;
|
||||
// grid-template-columns: 1fr;
|
||||
// position: relative;
|
||||
// max-width: 100vw;
|
||||
//
|
||||
// .row {
|
||||
// margin-top: 4em;
|
||||
// margin-bottom: 4em;
|
||||
// position: relative;
|
||||
//
|
||||
// max-width: 100vw;
|
||||
// @media (min-width: 576px) {
|
||||
// max-width: calc(100% - 40px);
|
||||
// }
|
||||
// @media (min-width: 768px) {
|
||||
// max-width: calc(100% - 80px);
|
||||
// }
|
||||
// @media (min-width: 992px) {
|
||||
// max-width: calc(100% - 100px);
|
||||
// }
|
||||
// @media (min-width: 1200px) {
|
||||
// max-width: calc(100% - 120px);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
button {
|
||||
&.btn-xl {
|
||||
font-size: 24px;
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
&.btn-lg {
|
||||
font-size: 20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
background-color: $brand-gray !important;
|
||||
}
|
||||
}
|
||||
|
||||
mat-form-field.mat-form-field {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.mat-form-field-wrapper .mat-form-field-subscript-wrapper {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 2em;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 2em;
|
||||
|
||||
&.alert-info {
|
||||
background-color: $brand-primary-light;
|
||||
color: black;
|
||||
}
|
||||
|
||||
&.alert-warn {
|
||||
background-color: $brand-warning;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-drawer .mat-nav-list .mat-list-item {
|
||||
min-width: 320px;
|
||||
}
|
||||
|
||||
.mat-drawer .mat-nav-list .mat-list-item.active {
|
||||
background-color: $brand-primary;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pad-0 {
|
||||
padding: 0px;
|
||||
}
|
||||
.pad-1 {
|
||||
padding: 1em;
|
||||
}
|
||||
.pad-2 {
|
||||
padding: 2em;
|
||||
}
|
||||
.pad-3 {
|
||||
padding: 3em;
|
||||
}
|
||||
.pad-4 {
|
||||
padding: 4em;
|
||||
}
|
||||
.pad-5 {
|
||||
padding: 5em;
|
||||
}
|
||||
.pad-6 {
|
||||
padding: 6em;
|
||||
}
|
||||
.pad-7 {
|
||||
padding: 7em;
|
||||
}
|
||||
.pad-8 {
|
||||
padding: 8em;
|
||||
}
|
||||
|
||||
.margin-top-none, .row.margin-top-none {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
.margin-bottom-none, .row.margin-bottom-none {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
.ghost {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
// XS
|
||||
@media (max-width: 575px) {
|
||||
.cdk-overlay-wrapper .cdk-overlay-pane {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
mat-dialog-container.mat-dialog-container {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// SM
|
||||
@media (min-width: 576px) {
|
||||
.cdk-overlay-wrapper .cdk-overlay-pane {
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
}
|
||||
|
||||
mat-dialog-container.mat-dialog-container {
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
|
||||
// MD
|
||||
@media (min-width: 768px) {
|
||||
.cdk-overlay-wrapper .cdk-overlay-pane {
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
}
|
||||
|
||||
mat-dialog-container.mat-dialog-container {
|
||||
width: 90vw;
|
||||
}
|
||||
}
|
||||
|
||||
// LG
|
||||
@media (min-width: 992px) {
|
||||
.cdk-overlay-wrapper .cdk-overlay-pane {
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
}
|
||||
|
||||
mat-dialog-container.mat-dialog-container {
|
||||
width: 80vw;
|
||||
}
|
||||
}
|
||||
|
||||
// XL
|
||||
@media (min-width: 1200px) {
|
||||
.cdk-overlay-wrapper .cdk-overlay-pane {
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
}
|
||||
|
||||
mat-dialog-container.mat-dialog-container {
|
||||
width: 70vw;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-dialog-content[mat-dialog-content] {
|
||||
max-height: 85vh;
|
||||
}
|
||||
|
||||
.loading {
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +1,6 @@
|
||||
@import '~@angular/material/prebuilt-themes/indigo-pink.css';
|
||||
@import '~bpmn-js/dist/assets/diagram-js.css';
|
||||
@import '~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
|
||||
@import '~bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css';
|
||||
@import '~diagram-js-minimap/assets/diagram-js-minimap.css';
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
.container, .row {
|
||||
padding: 1em;
|
||||
}
|
||||
@import './material-theme.scss';
|
||||
@include cr-connect-theme($cr-connect-theme);
|
Loading…
x
Reference in New Issue
Block a user