mirror of
https://github.com/SuiteCRM/SuiteCRM-Core.git
synced 2025-09-04 10:14:13 +08:00
Add ThemeImages API
- Add ThemeImages Entity - Add ThemeImages DataProvider - Add ThemeImage Service -- Retrieves the list of images for a given theme - Add ThemeImagesFinder -- looks up for images inside a given path -- Add configuration to specify theme image paths -- Add configuration to specify supported image types - Add unit tests - Add theme-images angular facade - Update base metadata resolver to load theme images -- Move UserPreferences loading ot base metadata resolver -- Loaded theme images after user preference and config loading --- uses default_theme, if user preferences weren't loaded -- Add jasmine tests - Add scrm-image component -- Add jasmine tests - Replace usages of svg-icon with image component -- Fix jasmine tests - Adjust UserPreference interface to match the current data
This commit is contained in:
parent
3f133b64db
commit
895ec5b4f7
77 changed files with 2651 additions and 1496 deletions
|
@ -28,6 +28,8 @@ services:
|
|||
$menuItemMap: '%legacy.menu_item_map%'
|
||||
$legacyAssetPaths: '%legacy.asset_paths%'
|
||||
$exposedUserPreferences: '%legacy.exposed_user_preferences%'
|
||||
$themeImagePaths: '%themes.image_paths%'
|
||||
$themeImageSupportedTypes: '%themes.image_supported_types%'
|
||||
_instanceof:
|
||||
App\Service\ProcessHandlerInterface:
|
||||
tags: ['app.process.handler']
|
||||
|
|
|
@ -5,3 +5,4 @@ parameters:
|
|||
forgotpasswordON: true
|
||||
languages: true
|
||||
default_module: true
|
||||
default_theme: true
|
||||
|
|
9
config/services/themes/image_paths.yaml
Normal file
9
config/services/themes/image_paths.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
parameters:
|
||||
themes.image_paths:
|
||||
# Order matters, lower ones will override the above ones
|
||||
- 'legacy/themes/default/images'
|
||||
- 'legacy/custom/themes/default/images'
|
||||
- 'core/app/themes/default/images'
|
||||
- 'legacy/themes/<theme>/images'
|
||||
- 'legacy/custom/themes/<theme>/images'
|
||||
- 'core/app/themes/<theme>/images'
|
8
config/services/themes/image_supported_types.yaml
Normal file
8
config/services/themes/image_supported_types.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
parameters:
|
||||
themes.image_supported_types:
|
||||
# Order matters, top most ones will have priority over the lower ones
|
||||
- 'svg'
|
||||
- 'png'
|
||||
- 'jpg'
|
||||
- 'jpeg'
|
||||
- 'gif'
|
|
@ -1,10 +1,9 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {Routes, RouterModule} from '@angular/router';
|
||||
import {ClassicViewUiComponent} from '@components/classic-view/classic-view.component';
|
||||
import {ClassicViewResolver} from "@services/classic-view/classic-view.resolver";
|
||||
import {ClassicViewResolver} from '@services/classic-view/classic-view.resolver';
|
||||
import {BaseMetadataResolver} from '@services/metadata/base-metadata.resolver';
|
||||
import {UserPreferenceResolver} from '@base/facades/user-preference/user-preference.resolver';
|
||||
import {AuthGuard} from '../services/auth/auth-guard.service';
|
||||
import {AuthGuard} from '@services/auth/auth-guard.service';
|
||||
import {ListComponent} from '@views/list/list.component';
|
||||
|
||||
const routes: Routes = [
|
||||
|
@ -12,8 +11,7 @@ const routes: Routes = [
|
|||
path: 'Listview',
|
||||
component: ListComponent,
|
||||
resolve: {
|
||||
view: BaseMetadataResolver,
|
||||
userPreference: UserPreferenceResolver
|
||||
view: BaseMetadataResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -25,6 +23,7 @@ const routes: Routes = [
|
|||
data: {
|
||||
load: {
|
||||
navigation: false,
|
||||
preferences: false,
|
||||
languageStrings: ['appStrings']
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +33,6 @@ const routes: Routes = [
|
|||
loadChildren: () => import('../components/home/home.module').then(m => m.HomeUiModule),
|
||||
resolve: {
|
||||
metadata: BaseMetadataResolver,
|
||||
userPreference: UserPreferenceResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -45,7 +43,6 @@ const routes: Routes = [
|
|||
resolve: {
|
||||
metadata: BaseMetadataResolver,
|
||||
view: ClassicViewResolver,
|
||||
userPreference: UserPreferenceResolver
|
||||
},
|
||||
data: {
|
||||
reuseRoute: false
|
||||
|
@ -59,7 +56,6 @@ const routes: Routes = [
|
|||
resolve: {
|
||||
metadata: BaseMetadataResolver,
|
||||
view: ClassicViewResolver,
|
||||
userPreference: UserPreferenceResolver
|
||||
},
|
||||
data: {
|
||||
reuseRoute: false
|
||||
|
@ -73,7 +69,6 @@ const routes: Routes = [
|
|||
resolve: {
|
||||
metadata: BaseMetadataResolver,
|
||||
view: ClassicViewResolver,
|
||||
userPreference: UserPreferenceResolver
|
||||
},
|
||||
data: {
|
||||
reuseRoute: false
|
||||
|
|
|
@ -34,6 +34,7 @@ import {FetchPolicy} from 'apollo-client/core/watchQueryOptions';
|
|||
import {FullPageSpinnerComponent} from '@components/full-page-spinner/full-page-spinner.component';
|
||||
import {RouteReuseStrategy} from '@angular/router';
|
||||
import {AppRouteReuseStrategy} from './app-router-reuse-strategy';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -60,6 +61,7 @@ import {AppRouteReuseStrategy} from './app-router-reuse-strategy';
|
|||
ListheaderUiModule,
|
||||
ListcontainerUiModule,
|
||||
ColumnchooserUiModule,
|
||||
ImageModule,
|
||||
BrowserAnimationsModule,
|
||||
NgbModule
|
||||
],
|
||||
|
|
|
@ -1,145 +1,145 @@
|
|||
<!-- Start of action bar section -->
|
||||
|
||||
<div class="global-action-bar row pb-2 pt-2 mr-0">
|
||||
<div class="col-6">
|
||||
<form novalidate class="global-search ng-untouched ng-pristine ng-valid">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="Search..." aria-label="Search">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-default search-button" type="submit" aria-label="Search">
|
||||
<svg-icon class="search-icon sicon" src="public/themes/suite8/images/search.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col justify-content-right">
|
||||
<div class="action-group">
|
||||
<div class="action-alert dropdown">
|
||||
<button class="alerts-button dropdown-toggle" type="button" aria-label="Toggle Alerts">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/alert.svg"></svg-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<li class="alert-list-item">
|
||||
<h4>ASSIGNED TASK</h4>
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/alert.svg">
|
||||
</svg-icon>
|
||||
Create Wireframe
|
||||
</a>
|
||||
</li>
|
||||
<li class="alert-list-item">
|
||||
<h4>DUE DATE EXPIRED</h4>
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/alert.svg">
|
||||
</svg-icon>
|
||||
Call John Doe to discuss...
|
||||
</a>
|
||||
</li>
|
||||
<li class="alert-list-item">
|
||||
<h4>ASSIGNED LEAD</h4>
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/alert.svg">
|
||||
</svg-icon>
|
||||
John Doe
|
||||
</a>
|
||||
</li>
|
||||
<li class="alert-list-item">
|
||||
<h4>NEW CASE</h4>
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/alert.svg">
|
||||
</svg-icon>
|
||||
Johnston & Co
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="action-view-all" href="#">
|
||||
View All >
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-favourite dropdown">
|
||||
<button class="favourites-button dropdown-toggle" type="button" aria-label="Toggle Favourites">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/star.svg"></svg-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<li class="favourite-list-item">
|
||||
<h4>JOHN DOE</h4>
|
||||
<a href="redirect-to-the-record">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/star.svg">
|
||||
</svg-icon>
|
||||
This is a favourited record
|
||||
</a>
|
||||
</li>
|
||||
<li class="favourite-list-item">
|
||||
<h4>JOHNSTON & CO</h4>
|
||||
<a href="redirect-to-the-record">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/star.svg">
|
||||
</svg-icon>
|
||||
This is a favourited record
|
||||
</a>
|
||||
</li>
|
||||
<li class="favourite-list-item">
|
||||
<h4>CREATE WIREFRAME</h4>
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/star.svg">
|
||||
</svg-icon>
|
||||
This is a favourited record
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="action-view-all" href="#">
|
||||
View All >
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-new dropdown">
|
||||
<button class="quickcreate-button dropdown-toggle" type="button" aria-label="Quick Create">
|
||||
NEW
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/plus.svg"></svg-icon>
|
||||
Accounts
|
||||
</a>
|
||||
</li>
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/plus.svg"></svg-icon>
|
||||
Contacts
|
||||
</a>
|
||||
</li>
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/plus.svg"></svg-icon>
|
||||
Leads
|
||||
</a>
|
||||
</li>
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/plus.svg"></svg-icon>
|
||||
Opportunities
|
||||
</a>
|
||||
</li>
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/plus.svg"></svg-icon>
|
||||
Quotes
|
||||
</a>
|
||||
</li>
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<svg-icon class="action-btn-icon" src="public/themes/suite8/images/plus.svg"></svg-icon>
|
||||
Contracts
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<form novalidate class="global-search ng-untouched ng-pristine ng-valid">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="Search..." aria-label="Search">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-default search-button" type="submit" aria-label="Search">
|
||||
<scrm-image class="search-icon sicon" image="search"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col justify-content-right">
|
||||
<div class="action-group">
|
||||
<div class="action-alert dropdown">
|
||||
<button class="alerts-button dropdown-toggle" type="button" aria-label="Toggle Alerts">
|
||||
<scrm-image class="action-btn-icon" image="alert"></scrm-image>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<li class="alert-list-item">
|
||||
<h4>ASSIGNED TASK</h4>
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="alert">
|
||||
</scrm-image>
|
||||
Create Wireframe
|
||||
</a>
|
||||
</li>
|
||||
<li class="alert-list-item">
|
||||
<h4>DUE DATE EXPIRED</h4>
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="alert">
|
||||
</scrm-image>
|
||||
Call John Doe to discuss...
|
||||
</a>
|
||||
</li>
|
||||
<li class="alert-list-item">
|
||||
<h4>ASSIGNED LEAD</h4>
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="alert">
|
||||
</scrm-image>
|
||||
John Doe
|
||||
</a>
|
||||
</li>
|
||||
<li class="alert-list-item">
|
||||
<h4>NEW CASE</h4>
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="alert">
|
||||
</scrm-image>
|
||||
Johnston & Co
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="action-view-all" href="#">
|
||||
View All >
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-favourite dropdown">
|
||||
<button class="favourites-button dropdown-toggle" type="button" aria-label="Toggle Favourites">
|
||||
<scrm-image class="action-btn-icon" image="star"></scrm-image>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<li class="favourite-list-item">
|
||||
<h4>JOHN DOE</h4>
|
||||
<a href="redirect-to-the-record">
|
||||
<scrm-image class="action-btn-icon" image="star">
|
||||
</scrm-image>
|
||||
This is a favourited record
|
||||
</a>
|
||||
</li>
|
||||
<li class="favourite-list-item">
|
||||
<h4>JOHNSTON & CO</h4>
|
||||
<a href="redirect-to-the-record">
|
||||
<scrm-image class="action-btn-icon" image="star">
|
||||
</scrm-image>
|
||||
This is a favourited record
|
||||
</a>
|
||||
</li>
|
||||
<li class="favourite-list-item">
|
||||
<h4>CREATE WIREFRAME</h4>
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="star">
|
||||
</scrm-image>
|
||||
This is a favourited record
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="action-view-all" href="#">
|
||||
View All >
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-new dropdown">
|
||||
<button class="quickcreate-button dropdown-toggle" type="button" aria-label="Quick Create">
|
||||
NEW
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="plus"></scrm-image>
|
||||
Accounts
|
||||
</a>
|
||||
</li>
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="plus"></scrm-image>
|
||||
Contacts
|
||||
</a>
|
||||
</li>
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="plus"></scrm-image>
|
||||
Leads
|
||||
</a>
|
||||
</li>
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="plus"></scrm-image>
|
||||
Opportunities
|
||||
</a>
|
||||
</li>
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="plus"></scrm-image>
|
||||
Quotes
|
||||
</a>
|
||||
</li>
|
||||
<li class="new-list-item">
|
||||
<a href="#">
|
||||
<scrm-image class="action-btn-icon" image="plus"></scrm-image>
|
||||
Contracts
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of action bar section -->
|
|
@ -1,9 +1,13 @@
|
|||
import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
||||
import {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
|
||||
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
|
||||
import {ActionBarUiComponent} from './action-bar.component';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
|
||||
describe('ActionBarUiComponent', () => {
|
||||
let component: ActionBarUiComponent;
|
||||
|
@ -13,6 +17,13 @@ describe('ActionBarUiComponent', () => {
|
|||
TestBed.configureTestingModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
imports: [RouterTestingModule, HttpClientTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
declarations: [ActionBarUiComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
@ -24,7 +35,7 @@ describe('ActionBarUiComponent', () => {
|
|||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it(`should create`, async(inject([HttpTestingController],
|
||||
it('should create', async(inject([HttpTestingController],
|
||||
(httpClient: HttpTestingController) => {
|
||||
expect(component).toBeTruthy();
|
||||
})));
|
||||
|
|
|
@ -2,22 +2,22 @@ import {Component, OnInit} from '@angular/core';
|
|||
import {ActionBarModel} from './action-bar-model';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-action-bar-ui',
|
||||
templateUrl: './action-bar.component.html',
|
||||
styleUrls: []
|
||||
selector: 'scrm-action-bar-ui',
|
||||
templateUrl: './action-bar.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class ActionBarUiComponent implements OnInit {
|
||||
|
||||
actionBar: ActionBarModel = {
|
||||
createLinks: [],
|
||||
favoriteRecords: [],
|
||||
};
|
||||
actionBar: ActionBarModel = {
|
||||
createLinks: [],
|
||||
favoriteRecords: [],
|
||||
};
|
||||
|
||||
constructor() {
|
||||
}
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
import {ActionBarUiComponent} from './action-bar.component';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ActionBarUiComponent],
|
||||
exports: [ActionBarUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(ActionBarUiComponent),
|
||||
AngularSvgIconModule
|
||||
]
|
||||
declarations: [ActionBarUiComponent],
|
||||
exports: [ActionBarUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(ActionBarUiComponent),
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class ActionBarUiModule {
|
||||
}
|
||||
|
|
|
@ -1,52 +1,49 @@
|
|||
<div class="card-body">
|
||||
<div class="chart-create">
|
||||
<button class="quickcreate-button" type="button">
|
||||
<svg-icon class="sicon widget-button-icon"
|
||||
src="public/themes/suite8/images/plus.svg"></svg-icon>
|
||||
New Chart
|
||||
</button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="pipeline-chart col-xs">
|
||||
<div class="accordion">
|
||||
<button class="widget-transparent-button btn-block dropdown-toggle"
|
||||
type="button"
|
||||
data-toggle="collapse" data-target=".pipeline-chart-content">
|
||||
Pipeline By Sales Stage
|
||||
</button>
|
||||
<div class="collapse show pipeline-chart-content"
|
||||
data-parent=".pipeline-chart">
|
||||
<img alt="Pipeline Chart" class="w-100"
|
||||
src="public/themes/suite8/images/pipeline_chart.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="donut-chart col-xs">
|
||||
<div class="accordion">
|
||||
<button class="widget-transparent-button btn-block dropdown-toggle"
|
||||
type="button"
|
||||
data-toggle="collapse" data-target=".donut-chart-content">
|
||||
Display as Donut Chart
|
||||
</button>
|
||||
<div class="collapse show donut-chart-content" data-parent=".donut-chart">
|
||||
<img alt="Donut Chart" class="w-100"
|
||||
src="public/themes/suite8/images/donut_chart.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-create">
|
||||
<button class="quickcreate-button" type="button">
|
||||
<scrm-image class="sicon widget-button-icon" image="plus"></scrm-image>
|
||||
New Chart
|
||||
</button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="pipeline-chart col-xs">
|
||||
<div class="accordion">
|
||||
<button class="widget-transparent-button btn-block dropdown-toggle"
|
||||
type="button"
|
||||
data-toggle="collapse" data-target=".pipeline-chart-content">
|
||||
Pipeline By Sales Stage
|
||||
</button>
|
||||
<div class="collapse show pipeline-chart-content"
|
||||
data-parent=".pipeline-chart">
|
||||
<img alt="Pipeline Chart" class="w-100"
|
||||
src="public/themes/suite8/images/pipeline_chart.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="donut-chart col-xs">
|
||||
<div class="accordion">
|
||||
<button class="widget-transparent-button btn-block dropdown-toggle"
|
||||
type="button"
|
||||
data-toggle="collapse" data-target=".donut-chart-content">
|
||||
Display as Donut Chart
|
||||
</button>
|
||||
<div class="collapse show donut-chart-content" data-parent=".donut-chart">
|
||||
<img alt="Donut Chart" class="w-100"
|
||||
src="public/themes/suite8/images/donut_chart.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-create">
|
||||
<button class="quickcreate-button" type="button">
|
||||
<scrm-image class="sicon widget-button-icon" image="edit"></scrm-image>
|
||||
Edit
|
||||
</button>
|
||||
<button class="remove-button" type="button">
|
||||
<scrm-image class="sicon widget-button-icon" image="cross"></scrm-image>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-create">
|
||||
<button class="quickcreate-button" type="button">
|
||||
<svg-icon class="sicon widget-button-icon"
|
||||
src="public/themes/suite8/images/edit.svg"></svg-icon>
|
||||
Edit
|
||||
</button>
|
||||
<button class="remove-button" type="button">
|
||||
<svg-icon class="sicon widget-button-icon" src="public/themes/suite8/images/cross.svg">
|
||||
</svg-icon>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
|
@ -1,4 +1,4 @@
|
|||
import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
||||
import {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
|
@ -6,6 +6,10 @@ import {RouterTestingModule} from '@angular/router/testing';
|
|||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
|
||||
import {ChartUiComponent} from './chart.component';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
|
||||
describe('ChartComponent', () => {
|
||||
let component: ChartUiComponent;
|
||||
|
@ -15,6 +19,13 @@ describe('ChartComponent', () => {
|
|||
TestBed.configureTestingModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
imports: [RouterTestingModule, HttpClientTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
declarations: [ChartUiComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
@ -26,7 +37,7 @@ describe('ChartComponent', () => {
|
|||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it(`should create`, async(inject([HttpTestingController],
|
||||
it('should create', async(inject([HttpTestingController],
|
||||
(httpClient: HttpTestingController) => {
|
||||
expect(component).toBeTruthy();
|
||||
})));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-chart-ui',
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
import {ChartUiComponent} from './chart.component';
|
||||
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ChartUiComponent],
|
||||
exports: [ChartUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(ChartUiComponent),
|
||||
AngularSvgIconModule
|
||||
]
|
||||
declarations: [ChartUiComponent],
|
||||
exports: [ChartUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(ChartUiComponent),
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class ChartUiModule {
|
||||
}
|
||||
|
|
|
@ -2,84 +2,85 @@
|
|||
|
||||
<ng-template #columnchoosermodal let-modal>
|
||||
|
||||
<!-- Start of modal header section -->
|
||||
<!-- Start of modal header section -->
|
||||
|
||||
<div class="column-chooser-modal">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">{{ modalTitle }}</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close Column Chooser Modal"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/close_modal.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- End of modal header section -->
|
||||
|
||||
<!-- Start of modal body section -->
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="column-chooser-container">
|
||||
<h2 class="column-chooser-title">Displayed</h2>
|
||||
|
||||
<div
|
||||
cdkDropList
|
||||
#displayedList="cdkDropList"
|
||||
[cdkDropListData]="displayed"
|
||||
[cdkDropListConnectedTo]="[hiddenList]"
|
||||
class="column-chooser-list"
|
||||
(cdkDropListDropped)="drop($event)">
|
||||
<div class="column-chooser-item column-displayed" *ngFor="let item of displayed" cdkDrag>{{item}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column-chooser-container">
|
||||
<h2 class="column-chooser-title">Hidden</h2>
|
||||
|
||||
<div
|
||||
cdkDropList
|
||||
#hiddenList="cdkDropList"
|
||||
[cdkDropListData]="hidden"
|
||||
[cdkDropListConnectedTo]="[displayedList]"
|
||||
class="column-chooser-list"
|
||||
(cdkDropListDropped)="drop($event)">
|
||||
<div class="column-chooser-item column-hidden" *ngFor="let item of hidden" cdkDrag>{{item}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- End of modal body section -->
|
||||
|
||||
<!-- Start of modal footer section -->
|
||||
|
||||
<div class="modal-footer">
|
||||
<div class="row w-100">
|
||||
<div class="col-lg-6">
|
||||
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="modal-buttons">
|
||||
<button type="button" class="btn modal-button-cancel" data-dismiss="modal"
|
||||
<div class="column-chooser-modal">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">{{ modalTitle }}</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close Column Chooser Modal"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
Close
|
||||
<scrm-image class="sicon" image="close_modal"></scrm-image>
|
||||
</button>
|
||||
<button type="button" class="btn modal-button-save">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of modal footer section -->
|
||||
<!-- End of modal header section -->
|
||||
|
||||
<!-- Start of modal body section -->
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="column-chooser-container">
|
||||
<h2 class="column-chooser-title">Displayed</h2>
|
||||
|
||||
<div
|
||||
cdkDropList
|
||||
#displayedList="cdkDropList"
|
||||
[cdkDropListData]="displayed"
|
||||
[cdkDropListConnectedTo]="[hiddenList]"
|
||||
class="column-chooser-list"
|
||||
(cdkDropListDropped)="drop($event)">
|
||||
<div class="column-chooser-item column-displayed" *ngFor="let item of displayed"
|
||||
cdkDrag>{{item}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column-chooser-container">
|
||||
<h2 class="column-chooser-title">Hidden</h2>
|
||||
|
||||
<div
|
||||
cdkDropList
|
||||
#hiddenList="cdkDropList"
|
||||
[cdkDropListData]="hidden"
|
||||
[cdkDropListConnectedTo]="[displayedList]"
|
||||
class="column-chooser-list"
|
||||
(cdkDropListDropped)="drop($event)">
|
||||
<div class="column-chooser-item column-hidden" *ngFor="let item of hidden" cdkDrag>{{item}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- End of modal body section -->
|
||||
|
||||
<!-- Start of modal footer section -->
|
||||
|
||||
<div class="modal-footer">
|
||||
<div class="row w-100">
|
||||
<div class="col-lg-6">
|
||||
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="modal-buttons">
|
||||
<button type="button" class="btn modal-button-cancel" data-dismiss="modal"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn modal-button-save">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of modal footer section -->
|
||||
|
||||
</ng-template>
|
||||
|
||||
<button type="button" (click)="open(columnchoosermodal)" class="settings-button">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/column_chooser.svg"></svg-icon>
|
||||
Columns
|
||||
<scrm-image class="sicon" image="column_chooser"></scrm-image>
|
||||
Columns
|
||||
</button>
|
||||
|
||||
<!-- End of modal component section -->
|
|
@ -1,79 +1,79 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop';
|
||||
import {NgbModal, ModalDismissReasons} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ModalDismissReasons, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-columnchooser-ui',
|
||||
templateUrl: './columnchooser.component.html',
|
||||
selector: 'scrm-columnchooser-ui',
|
||||
templateUrl: './columnchooser.component.html',
|
||||
})
|
||||
|
||||
export class ColumnChooserUiComponent implements OnInit {
|
||||
|
||||
modalTitle: string = 'Choose Columns';
|
||||
closeResult: string;
|
||||
modalTitle: string = 'Choose Columns';
|
||||
closeResult: string;
|
||||
|
||||
constructor(private modalService: NgbModal) {
|
||||
}
|
||||
|
||||
open(modal) {
|
||||
this.modalService.open(modal, {
|
||||
ariaLabelledBy: 'modal-basic-title',
|
||||
centered: true,
|
||||
size: 'lg',
|
||||
windowClass: 'column-chooser-modal'
|
||||
}).result.then((result) => {
|
||||
this.closeResult = `Closed with: ${result}`;
|
||||
}, (reason) => {
|
||||
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
|
||||
});
|
||||
}
|
||||
|
||||
private getDismissReason(reason: any): string {
|
||||
if (reason === ModalDismissReasons.ESC) {
|
||||
return 'by pressing ESC';
|
||||
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
|
||||
return 'by clicking on a backdrop';
|
||||
} else {
|
||||
return `with: ${reason}`;
|
||||
constructor(private modalService: NgbModal) {
|
||||
}
|
||||
}
|
||||
|
||||
displayed = [
|
||||
'Name',
|
||||
'City',
|
||||
'Billing Country',
|
||||
'Phone',
|
||||
'User',
|
||||
'Email Address',
|
||||
'Date Created',
|
||||
];
|
||||
|
||||
hidden = [
|
||||
'Annual Revenue',
|
||||
'Phone Fax',
|
||||
'Billing Street',
|
||||
'Billing Post Code',
|
||||
'Shipping Street',
|
||||
'Shipping Postcode',
|
||||
'Rating',
|
||||
'Website',
|
||||
'Ownership',
|
||||
'Employees'
|
||||
];
|
||||
|
||||
drop(event: CdkDragDrop<string[]>) {
|
||||
if (event.previousContainer === event.container) {
|
||||
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
|
||||
} else {
|
||||
transferArrayItem(event.previousContainer.data,
|
||||
event.container.data,
|
||||
event.previousIndex,
|
||||
event.currentIndex);
|
||||
open(modal) {
|
||||
this.modalService.open(modal, {
|
||||
ariaLabelledBy: 'modal-basic-title',
|
||||
centered: true,
|
||||
size: 'lg',
|
||||
windowClass: 'column-chooser-modal'
|
||||
}).result.then((result) => {
|
||||
this.closeResult = `Closed with: ${result}`;
|
||||
}, (reason) => {
|
||||
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
private getDismissReason(reason: any): string {
|
||||
if (reason === ModalDismissReasons.ESC) {
|
||||
return 'by pressing ESC';
|
||||
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
|
||||
return 'by clicking on a backdrop';
|
||||
} else {
|
||||
return `with: ${reason}`;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
displayed = [
|
||||
'Name',
|
||||
'City',
|
||||
'Billing Country',
|
||||
'Phone',
|
||||
'User',
|
||||
'Email Address',
|
||||
'Date Created',
|
||||
];
|
||||
|
||||
hidden = [
|
||||
'Annual Revenue',
|
||||
'Phone Fax',
|
||||
'Billing Street',
|
||||
'Billing Post Code',
|
||||
'Shipping Street',
|
||||
'Shipping Postcode',
|
||||
'Rating',
|
||||
'Website',
|
||||
'Ownership',
|
||||
'Employees'
|
||||
];
|
||||
|
||||
drop(event: CdkDragDrop<string[]>) {
|
||||
if (event.previousContainer === event.container) {
|
||||
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
|
||||
} else {
|
||||
transferArrayItem(event.previousContainer.data,
|
||||
event.container.data,
|
||||
event.previousIndex,
|
||||
event.currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
import {AppManagerModule} from '@base/app-manager/app-manager.module';
|
||||
import {ColumnChooserUiComponent} from './columnchooser.component';
|
||||
import {DragDropModule} from '@angular/cdk/drag-drop';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ColumnChooserUiComponent],
|
||||
|
@ -13,8 +13,8 @@ import {AngularSvgIconModule} from 'angular-svg-icon';
|
|||
CommonModule,
|
||||
DragDropModule,
|
||||
AppManagerModule.forChild(ColumnChooserUiComponent),
|
||||
AngularSvgIconModule
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class ColumnchooserUiModule {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,207 +2,207 @@
|
|||
|
||||
<ng-template #filtermodal let-modal>
|
||||
|
||||
<!-- Start of modal header section -->
|
||||
<!-- Start of modal header section -->
|
||||
|
||||
<div class="filter-modal">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">{{ modalTitle }}</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close Filter Modal"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/close_modal.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- End of modal header section -->
|
||||
|
||||
<!-- Start of modal body section -->
|
||||
|
||||
<div class="modal-body">
|
||||
<ng-template [ngIf]="quickFilter">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Opportunity Name</label>
|
||||
<input class="modal-input" type="text" placeholder="Lorem Ipsum">
|
||||
<label class="filter-checkbox-container">
|
||||
<input type="filter-checkbox">
|
||||
<span class="filter-checkmark"></span>
|
||||
</label>
|
||||
<p class="quick-filter-options">
|
||||
My Items
|
||||
</p>
|
||||
<label class="filter-checkbox-container">
|
||||
<input type="filter-checkbox">
|
||||
<span class="filter-checkmark"></span>
|
||||
</label>
|
||||
<p class="quick-filter-options">
|
||||
Open Items
|
||||
</p>
|
||||
<label class="filter-checkbox-container">
|
||||
<input type="filter-checkbox">
|
||||
<span class="filter-checkmark"></span>
|
||||
</label>
|
||||
<p class="quick-filter-options">
|
||||
My Favourites
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="advancedFilter">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Opportunity Name</label>
|
||||
<input class="modal-input modal-input-large" type="text" placeholder="Lorem Ipsum">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="relate-input-group">
|
||||
<label class="modal-label">Account Name</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<input class="modal-input modal-input-large" type="text" placeholder="Lorem Ipsum">
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<div class="relate-input-group">
|
||||
<button type="button" class="create-popup-button create-popup-arrow">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/cursor.svg"></svg-icon>
|
||||
</button>
|
||||
<button type="button" class="create-popup-button create-popup-cross">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/cross.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Opportunity Amount</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Equals</option>
|
||||
<option>Greater than</option>
|
||||
<option>Less than</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<input class="modal-input modal-input-small" type="text" placeholder="0">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Expected Close Date</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Equals</option>
|
||||
<option>Greater than</option>
|
||||
<option>Less than</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<input class="modal-input modal-date-input" type="text" placeholder="dd/mm/yy">
|
||||
<button type="button" class="create-popup-button create-popup-calendar">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/calendar.svg"></svg-icon>
|
||||
<div class="filter-modal">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">{{ modalTitle }}</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close Filter Modal"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<scrm-image class="sicon" image="close_modal"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Next Step</label>
|
||||
<input class="modal-input modal-input-large" type="text" placeholder="Lorem Ipsum">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<label class="modal-label">Save Filter as:</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Lorem Ipsum</option>
|
||||
<option>Lorem Ipsum</option>
|
||||
<option>Lorem Ipsum</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<button class="save-filter-button">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Order by column</label>
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Name</option>
|
||||
<option>Date Created</option>
|
||||
<option>Date Modified</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<!-- End of modal body section -->
|
||||
<!-- End of modal header section -->
|
||||
|
||||
<!-- Start of modal footer section -->
|
||||
<!-- Start of modal body section -->
|
||||
|
||||
<div class="modal-footer">
|
||||
<div class="row w-100">
|
||||
<div class="col-lg-3">
|
||||
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<div class="modal-buttons">
|
||||
<button type="button" class="btn modal-button-cancel" data-dismiss="modal">
|
||||
Clear
|
||||
</button>
|
||||
<ng-template [ngIf]="advancedFilter">
|
||||
<button type="button" class="modal-button-save dropdown-toggle">
|
||||
My Filters
|
||||
</button>
|
||||
</ng-template>
|
||||
<div class="modal-body">
|
||||
<ng-template [ngIf]="quickFilter">
|
||||
<button type="button" class="btn modal-button-save" (click)="toggleAdvancedFilter()">
|
||||
Advanced
|
||||
</button>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Opportunity Name</label>
|
||||
<input class="modal-input" type="text" placeholder="Lorem Ipsum">
|
||||
<label class="filter-checkbox-container">
|
||||
<input type="filter-checkbox">
|
||||
<span class="filter-checkmark"></span>
|
||||
</label>
|
||||
<p class="quick-filter-options">
|
||||
My Items
|
||||
</p>
|
||||
<label class="filter-checkbox-container">
|
||||
<input type="filter-checkbox">
|
||||
<span class="filter-checkmark"></span>
|
||||
</label>
|
||||
<p class="quick-filter-options">
|
||||
Open Items
|
||||
</p>
|
||||
<label class="filter-checkbox-container">
|
||||
<input type="filter-checkbox">
|
||||
<span class="filter-checkmark"></span>
|
||||
</label>
|
||||
<p class="quick-filter-options">
|
||||
My Favourites
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="advancedFilter">
|
||||
<button type="button" class="btn modal-button-save" (click)="toggleQuickFilter()">
|
||||
Quick
|
||||
</button>
|
||||
</ng-template>
|
||||
<button type="button" class="btn modal-button-save">
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Opportunity Name</label>
|
||||
<input class="modal-input modal-input-large" type="text" placeholder="Lorem Ipsum">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="relate-input-group">
|
||||
<label class="modal-label">Account Name</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<input class="modal-input modal-input-large" type="text" placeholder="Lorem Ipsum">
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<div class="relate-input-group">
|
||||
<button type="button" class="create-popup-button create-popup-arrow">
|
||||
<scrm-image class="sicon" image="cursor"></scrm-image>
|
||||
</button>
|
||||
<button type="button" class="create-popup-button create-popup-cross">
|
||||
<scrm-image class="sicon" image="cross"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Opportunity Amount</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Equals</option>
|
||||
<option>Greater than</option>
|
||||
<option>Less than</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<input class="modal-input modal-input-small" type="text" placeholder="0">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of modal footer section -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Expected Close Date</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Equals</option>
|
||||
<option>Greater than</option>
|
||||
<option>Less than</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<input class="modal-input modal-date-input" type="text" placeholder="dd/mm/yy">
|
||||
<button type="button" class="create-popup-button create-popup-calendar">
|
||||
<scrm-image class="sicon" image="calendar"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Next Step</label>
|
||||
<input class="modal-input modal-input-large" type="text" placeholder="Lorem Ipsum">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<label class="modal-label">Save Filter as:</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Lorem Ipsum</option>
|
||||
<option>Lorem Ipsum</option>
|
||||
<option>Lorem Ipsum</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<button class="save-filter-button">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="modal-label">Order by column</label>
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Name</option>
|
||||
<option>Date Created</option>
|
||||
<option>Date Modified</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<!-- End of modal body section -->
|
||||
|
||||
<!-- Start of modal footer section -->
|
||||
|
||||
<div class="modal-footer">
|
||||
<div class="row w-100">
|
||||
<div class="col-lg-3">
|
||||
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<div class="modal-buttons">
|
||||
<button type="button" class="btn modal-button-cancel" data-dismiss="modal">
|
||||
Clear
|
||||
</button>
|
||||
<ng-template [ngIf]="advancedFilter">
|
||||
<button type="button" class="modal-button-save dropdown-toggle">
|
||||
My Filters
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="quickFilter">
|
||||
<button type="button" class="btn modal-button-save" (click)="toggleAdvancedFilter()">
|
||||
Advanced
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="advancedFilter">
|
||||
<button type="button" class="btn modal-button-save" (click)="toggleQuickFilter()">
|
||||
Quick
|
||||
</button>
|
||||
</ng-template>
|
||||
<button type="button" class="btn modal-button-save">
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of modal footer section -->
|
||||
|
||||
</ng-template>
|
||||
|
||||
<button type="button" (click)="open(filtermodal)" class="settings-button">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/filter.svg"></svg-icon>
|
||||
Filter
|
||||
<scrm-image class="sicon" image="filter"></scrm-image>
|
||||
Filter
|
||||
</button>
|
||||
|
||||
<!-- End of modal component section -->
|
|
@ -1,6 +1,10 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {FilterUiComponent} from './filter.component';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
|
||||
describe('FilterUiComponent', () => {
|
||||
let component: FilterUiComponent;
|
||||
|
@ -8,7 +12,14 @@ describe('FilterUiComponent', () => {
|
|||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [FilterUiComponent]
|
||||
declarations: [FilterUiComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
|
|
@ -1,61 +1,61 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
import {NgbModal, ModalDismissReasons} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ModalDismissReasons, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-filter-ui',
|
||||
templateUrl: './filter.component.html',
|
||||
selector: 'scrm-filter-ui',
|
||||
templateUrl: './filter.component.html',
|
||||
|
||||
})
|
||||
|
||||
export class FilterUiComponent implements OnInit {
|
||||
|
||||
filterModal: boolean = true;
|
||||
modalTitle: string = 'Quick Filter';
|
||||
closeResult: string;
|
||||
quickFilter: boolean = true;
|
||||
advancedFilter: boolean = false;
|
||||
filterModal: boolean = true;
|
||||
modalTitle: string = 'Quick Filter';
|
||||
closeResult: string;
|
||||
quickFilter: boolean = true;
|
||||
advancedFilter: boolean = false;
|
||||
|
||||
constructor(private modalService: NgbModal) {
|
||||
}
|
||||
|
||||
toggleQuickFilter() {
|
||||
this.modalTitle = 'Quick Filter';
|
||||
this.advancedFilter = !this.advancedFilter;
|
||||
this.quickFilter = !this.quickFilter;
|
||||
}
|
||||
|
||||
toggleAdvancedFilter() {
|
||||
this.modalTitle = 'Advanced Filter';
|
||||
this.advancedFilter = !this.advancedFilter;
|
||||
this.quickFilter = !this.quickFilter;
|
||||
}
|
||||
|
||||
open(modal) {
|
||||
this.modalService.open(modal, {
|
||||
ariaLabelledBy: 'modal-basic-title',
|
||||
centered: true,
|
||||
size: 'lg',
|
||||
windowClass: 'filter-modal'
|
||||
}).result.then((result) => {
|
||||
this.closeResult = `Closed with: ${result}`;
|
||||
}, (reason) => {
|
||||
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
|
||||
});
|
||||
}
|
||||
|
||||
private getDismissReason(reason: any): string {
|
||||
if (reason === ModalDismissReasons.ESC) {
|
||||
return 'by pressing ESC';
|
||||
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
|
||||
return 'by clicking on a backdrop';
|
||||
} else {
|
||||
return `with: ${reason}`;
|
||||
constructor(private modalService: NgbModal) {
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
toggleQuickFilter() {
|
||||
this.modalTitle = 'Quick Filter';
|
||||
this.advancedFilter = !this.advancedFilter;
|
||||
this.quickFilter = !this.quickFilter;
|
||||
}
|
||||
|
||||
}
|
||||
toggleAdvancedFilter() {
|
||||
this.modalTitle = 'Advanced Filter';
|
||||
this.advancedFilter = !this.advancedFilter;
|
||||
this.quickFilter = !this.quickFilter;
|
||||
}
|
||||
|
||||
open(modal) {
|
||||
this.modalService.open(modal, {
|
||||
ariaLabelledBy: 'modal-basic-title',
|
||||
centered: true,
|
||||
size: 'lg',
|
||||
windowClass: 'filter-modal'
|
||||
}).result.then((result) => {
|
||||
this.closeResult = `Closed with: ${result}`;
|
||||
}, (reason) => {
|
||||
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
|
||||
});
|
||||
}
|
||||
|
||||
private getDismissReason(reason: any): string {
|
||||
if (reason === ModalDismissReasons.ESC) {
|
||||
return 'by pressing ESC';
|
||||
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
|
||||
return 'by clicking on a backdrop';
|
||||
} else {
|
||||
return `with: ${reason}`;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
import {FilterUiComponent} from './filter.component';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [FilterUiComponent],
|
||||
|
@ -11,8 +11,8 @@ import {AngularSvgIconModule} from 'angular-svg-icon';
|
|||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(FilterUiComponent),
|
||||
AngularSvgIconModule
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class FilterUiModule {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
<!-- Start of footer section -->
|
||||
|
||||
<div class="footer">
|
||||
<div class="copyright-links">
|
||||
<a (click)="openSuiteCopyright(suitecopyright)" class="footer-link" data-toggle="modal"
|
||||
data-target=".copyright-suitecrm">
|
||||
© Supercharged by SuiteCRM
|
||||
</a>
|
||||
<a (click)="openSugarCopyright(sugarcopyright)" class="footer-link" data-toggle="modal"
|
||||
data-target=".copyright-sugarcrm">
|
||||
© Powered By SugarCRM
|
||||
</a>
|
||||
</div>
|
||||
<div class="back-to-top">
|
||||
<a (click)="backToTop()" class="footer-link">
|
||||
<div class="copyright-links">
|
||||
<a (click)="openSuiteCopyright(suitecopyright)" class="footer-link" data-toggle="modal"
|
||||
data-target=".copyright-suitecrm">
|
||||
© Supercharged by SuiteCRM
|
||||
</a>
|
||||
<a (click)="openSugarCopyright(sugarcopyright)" class="footer-link" data-toggle="modal"
|
||||
data-target=".copyright-sugarcrm">
|
||||
© Powered By SugarCRM
|
||||
</a>
|
||||
</div>
|
||||
<div class="back-to-top">
|
||||
<a (click)="backToTop()" class="footer-link">
|
||||
<span>
|
||||
Back To Top
|
||||
<svg-icon class="sicon back-top-icon" src="public/themes/suite8/images/arrow_up_filled.svg"></svg-icon>
|
||||
<scrm-image class="sicon back-top-icon" image="arrow_up_filled"></scrm-image>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of footer section -->
|
||||
|
@ -27,75 +27,75 @@
|
|||
|
||||
<div class="copyright">
|
||||
|
||||
<!-- Start of SugarCRM Copyright notice modal -->
|
||||
<!-- Start of SugarCRM Copyright notice modal -->
|
||||
|
||||
<ng-template #sugarcopyright let-modal>
|
||||
<ng-template #sugarcopyright let-modal>
|
||||
|
||||
<div class="copyright-sugarcrm" role="dialog" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">© Powered By SugarCRM</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/icon_modal_close.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
© 2004-2013 SugarCRM Inc. The Program is provided AS IS, without
|
||||
warranty. Licensed under AGPLv3.
|
||||
</p>
|
||||
<p>
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License version
|
||||
3 as published by the Free Software Foundation, including the
|
||||
additional permission set forth in the source code header.
|
||||
</p>
|
||||
<p>
|
||||
SugarCRM is a trademark of SugarCRM, Inc. All other company and
|
||||
product names may be trademarks of the respective companies with
|
||||
which they are associated.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="copyright-sugarcrm" role="dialog" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">© Powered By SugarCRM</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<scrm-image class="sicon" image="icon_modal_close"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
© 2004-2013 SugarCRM Inc. The Program is provided AS IS, without
|
||||
warranty. Licensed under AGPLv3.
|
||||
</p>
|
||||
<p>
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License version
|
||||
3 as published by the Free Software Foundation, including the
|
||||
additional permission set forth in the source code header.
|
||||
</p>
|
||||
<p>
|
||||
SugarCRM is a trademark of SugarCRM, Inc. All other company and
|
||||
product names may be trademarks of the respective companies with
|
||||
which they are associated.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
||||
<!-- End of SugarCRM Copyright notice modal -->
|
||||
<!-- End of SugarCRM Copyright notice modal -->
|
||||
|
||||
<!-- Start of SuiteCRM Copyright notice modal -->
|
||||
<!-- Start of SuiteCRM Copyright notice modal -->
|
||||
|
||||
<ng-template #suitecopyright let-modal>
|
||||
<ng-template #suitecopyright let-modal>
|
||||
|
||||
<div class="copyright-suitecrm" role="dialog" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">© Supercharged by SuiteCRM</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/icon_modal_close.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
SuiteCRM has been written and assembled by SalesAgility. The Program
|
||||
is provided AS IS, without warranty. Licensed under AGPLv3.
|
||||
</p>
|
||||
<p>
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License version
|
||||
3 as published by the Free Software Foundation, including the
|
||||
additional permission set forth in the source code header.
|
||||
</p>
|
||||
<p>
|
||||
SuiteCRM is a trademark of SalesAgility Ltd. All other company and
|
||||
product names may be trademarks of the respective companies with
|
||||
which they are associated.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="copyright-suitecrm" role="dialog" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">© Supercharged by SuiteCRM</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<scrm-image class="sicon" image="icon_modal_close"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
SuiteCRM has been written and assembled by SalesAgility. The Program
|
||||
is provided AS IS, without warranty. Licensed under AGPLv3.
|
||||
</p>
|
||||
<p>
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License version
|
||||
3 as published by the Free Software Foundation, including the
|
||||
additional permission set forth in the source code header.
|
||||
</p>
|
||||
<p>
|
||||
SuiteCRM is a trademark of SalesAgility Ltd. All other company and
|
||||
product names may be trademarks of the respective companies with
|
||||
which they are associated.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
||||
<!-- End of SuiteCRM Copyright notice modal -->
|
||||
<!-- End of SuiteCRM Copyright notice modal -->
|
||||
</div>
|
||||
|
||||
<!-- End of copyright modal section -->
|
|
@ -1,8 +1,8 @@
|
|||
import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
|
||||
import {FooterUiComponent} from './footer.component';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
import {NgbModal, ModalDismissReasons} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ModalDismissReasons, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-footer-ui',
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
import {FooterUiComponent} from './footer.component';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
|
@ -12,7 +12,7 @@ import {AngularSvgIconModule} from 'angular-svg-icon';
|
|||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(FooterUiComponent),
|
||||
AngularSvgIconModule
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class FooterUiModule {
|
||||
|
|
9
core/app/src/components/image/image.component.html
Normal file
9
core/app/src/components/image/image.component.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<ng-container *ngIf="(vm$ | async) as vm">
|
||||
<ng-container *ngIf="getImage(vm, image) as img">
|
||||
|
||||
<svg-icon *ngIf="img.type === 'svg'" class="sicon" src="{{img.path}}"></svg-icon>
|
||||
|
||||
<img *ngIf="img.type !=='svg'" src="{{img.path}}">
|
||||
|
||||
</ng-container>
|
||||
</ng-container>
|
73
core/app/src/components/image/image.component.spec.ts
Normal file
73
core/app/src/components/image/image.component.spec.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {ImageComponent} from './image.component';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {of} from 'rxjs';
|
||||
import {take} from 'rxjs/operators';
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'host-component',
|
||||
template: '<scrm-image [image]="image"></scrm-image>'
|
||||
})
|
||||
class TestHostComponent {
|
||||
private image = 'line';
|
||||
|
||||
setImage(newImage: string): void {
|
||||
this.image = newImage;
|
||||
}
|
||||
}
|
||||
|
||||
describe('ImageComponent', () => {
|
||||
let testHostComponent: TestHostComponent;
|
||||
let testHostFixture: ComponentFixture<TestHostComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ImageComponent, TestHostComponent],
|
||||
imports: [
|
||||
AngularSvgIconModule,
|
||||
HttpClientTestingModule
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
testHostFixture = TestBed.createComponent(TestHostComponent);
|
||||
testHostComponent = testHostFixture.componentInstance;
|
||||
testHostFixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(testHostComponent).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have image with src', () => {
|
||||
expect(testHostComponent).toBeTruthy();
|
||||
const img = testHostFixture.nativeElement.querySelector('img');
|
||||
|
||||
expect(img).toBeTruthy();
|
||||
expect(img.src).toContain(themeImagesMockData.line.path);
|
||||
});
|
||||
|
||||
it('should have svg', () => {
|
||||
testHostComponent.setImage('download');
|
||||
testHostFixture.detectChanges();
|
||||
|
||||
expect(testHostFixture.nativeElement.querySelector('svg-icon')).toBeTruthy();
|
||||
});
|
||||
});
|
45
core/app/src/components/image/image.component.ts
Normal file
45
core/app/src/components/image/image.component.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import {Component, Input} from '@angular/core';
|
||||
import {combineLatest, Observable} from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
import {ThemeImage, ThemeImageMap, ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-image',
|
||||
templateUrl: './image.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class ImageComponent {
|
||||
|
||||
images$: Observable<ThemeImageMap> = this.themeImagesFacade.images$;
|
||||
|
||||
vm$ = combineLatest([this.images$]).pipe(
|
||||
map(([images]) => ({
|
||||
images
|
||||
})));
|
||||
|
||||
@Input() image: string;
|
||||
|
||||
constructor(protected themeImagesFacade: ThemeImagesFacade) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image from current view model and log if not existent
|
||||
*
|
||||
* @param vm
|
||||
* @param image name
|
||||
* @returns ThemeImage
|
||||
*/
|
||||
getImage(vm: { images: ThemeImageMap }, image: string): ThemeImage {
|
||||
if (!vm || !vm.images || Object.keys(vm.images).length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const img = vm.images[image];
|
||||
|
||||
if (!img) {
|
||||
console.warn(`Image with name '${image}' not found`);
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
}
|
19
core/app/src/components/image/image.module.ts
Normal file
19
core/app/src/components/image/image.module.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ImageComponent} from '@components/image/image.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
ImageComponent
|
||||
],
|
||||
exports: [
|
||||
ImageComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AngularSvgIconModule
|
||||
]
|
||||
})
|
||||
export class ImageModule {
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
<!-- Start List View Container Section -->
|
||||
|
||||
<div class="list-view-container container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-9" [ngClass]="{ 'col-lg-12': !widgetService.displayWidgets }">
|
||||
<scrm-table-ui></scrm-table-ui>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-9" [ngClass]="{ 'col-lg-12': !widgetService.displayWidgets }">
|
||||
<scrm-table-ui></scrm-table-ui>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-3" *ngIf="widgetService.displayWidgets">
|
||||
<scrm-widget-ui></scrm-widget-ui>
|
||||
<div class="col-lg-3" *ngIf="widgetService.displayWidgets">
|
||||
<scrm-widget-ui></scrm-widget-ui>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End List View Container Section -->
|
|
@ -1,11 +1,16 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {ApolloTestingModule} from 'apollo-angular/testing';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ListcontainerUiComponent} from './list-container.component';
|
||||
import {TableUiModule} from '@components/table/table.module';
|
||||
import {WidgetUiModule} from '@components/widget/widget.module';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {of} from 'rxjs';
|
||||
import {take} from 'rxjs/operators';
|
||||
|
||||
|
||||
describe('ListcontainerUiComponent', () => {
|
||||
|
@ -19,7 +24,15 @@ describe('ListcontainerUiComponent', () => {
|
|||
WidgetUiModule,
|
||||
AngularSvgIconModule,
|
||||
HttpClientTestingModule,
|
||||
BrowserAnimationsModule
|
||||
BrowserAnimationsModule,
|
||||
ApolloTestingModule
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
declarations: [ListcontainerUiComponent]
|
||||
})
|
||||
|
@ -33,6 +46,6 @@ describe('ListcontainerUiComponent', () => {
|
|||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,55 +1,53 @@
|
|||
import {Component, HostListener, OnInit, ViewChild} from '@angular/core';
|
||||
import {Component, HostListener, OnInit} from '@angular/core';
|
||||
import {WidgetService} from '../widget/widget.service';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-list-container-ui',
|
||||
templateUrl: 'list-container.component.html'
|
||||
selector: 'scrm-list-container-ui',
|
||||
templateUrl: 'list-container.component.html'
|
||||
|
||||
})
|
||||
|
||||
export class ListcontainerUiComponent implements OnInit {
|
||||
|
||||
displayResponsiveTable: boolean = false;
|
||||
showCollapsed: boolean = false;
|
||||
tableToggleIcon: string = "public/themes/suite8/images/mobile_expand_icon.svg";
|
||||
listViewIconUnsorted: string = "public/themes/suite8/images/sort.svg";
|
||||
listViewIconSorted: string = "public/themes/suite8/images/sort_descend.svg";
|
||||
displayResponsiveTable = false;
|
||||
showCollapsed = false;
|
||||
tableToggleIcon = 'public/themes/suite8/images/mobile_expand_icon.svg';
|
||||
listViewIconUnsorted = 'public/themes/suite8/images/sort.svg';
|
||||
listViewIconSorted = 'public/themes/suite8/images/sort_descend.svg';
|
||||
|
||||
allSelected: boolean = false;
|
||||
allSelected = false;
|
||||
|
||||
@HostListener("window:resize", ["$event"])
|
||||
onResize(event: any) {
|
||||
event.target.innerWidth;
|
||||
if (innerWidth <= 768) {
|
||||
this.displayResponsiveTable = true;
|
||||
} else {
|
||||
this.displayResponsiveTable = false;
|
||||
constructor(private widgetService: WidgetService) {
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private widgetService: WidgetService) {
|
||||
}
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize(event: any): void {
|
||||
event.target.innerWidth;
|
||||
if (innerWidth <= 768) {
|
||||
this.displayResponsiveTable = true;
|
||||
} else {
|
||||
this.displayResponsiveTable = false;
|
||||
}
|
||||
}
|
||||
|
||||
toggleWidgets() {
|
||||
this.widgetService.emitData();
|
||||
}
|
||||
toggleWidgets(): void {
|
||||
this.widgetService.emitData();
|
||||
}
|
||||
|
||||
expandRow(row) {
|
||||
row.expanded = !row.expanded;
|
||||
}
|
||||
expandRow(row): void {
|
||||
row.expanded = !row.expanded;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
ngOnInit(): void {
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
this.allSelected = true;
|
||||
}
|
||||
|
||||
deselectAll() {
|
||||
this.allSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
selectAll(): void {
|
||||
this.allSelected = true;
|
||||
}
|
||||
|
||||
deselectAll(): void {
|
||||
this.allSelected = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
|
@ -9,15 +9,15 @@ import {WidgetUiModule} from '../widget/widget.module';
|
|||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ListcontainerUiComponent],
|
||||
exports: [ListcontainerUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(ListcontainerUiComponent),
|
||||
TableUiModule,
|
||||
WidgetUiModule,
|
||||
AngularSvgIconModule
|
||||
]
|
||||
declarations: [ListcontainerUiComponent],
|
||||
exports: [ListcontainerUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(ListcontainerUiComponent),
|
||||
TableUiModule,
|
||||
WidgetUiModule,
|
||||
AngularSvgIconModule
|
||||
]
|
||||
})
|
||||
export class ListcontainerUiModule {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,13 @@ import {ListheaderUiComponent} from './list-header.component';
|
|||
import {ModuletitleUiModule} from '@components/module-title/module-title.module';
|
||||
import {ActionmenuUiModule} from '@components/action-menu/action-menu.module';
|
||||
import {SettingsmenuUiModule} from '@components/settings-menu/settings-menu.module';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {ApolloTestingModule} from 'apollo-angular/testing';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
|
||||
describe('ListheaderUiComponent', () => {
|
||||
let component: ListheaderUiComponent;
|
||||
|
@ -17,10 +22,18 @@ describe('ListheaderUiComponent', () => {
|
|||
ModuletitleUiModule,
|
||||
ActionmenuUiModule,
|
||||
SettingsmenuUiModule,
|
||||
AngularSvgIconModule,
|
||||
HttpClientTestingModule
|
||||
ApolloTestingModule,
|
||||
HttpClientTestingModule,
|
||||
ImageModule
|
||||
],
|
||||
declarations: [ListheaderUiComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
declarations: [ListheaderUiComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
@ -32,6 +45,6 @@ describe('ListheaderUiComponent', () => {
|
|||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
[@fade]>
|
||||
|
||||
<div class="inner-addon left-addon">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/login_user.svg"></svg-icon>
|
||||
<scrm-image image="login_user"></scrm-image>
|
||||
<input [(ngModel)]="uname"
|
||||
type="text"
|
||||
name="username"
|
||||
|
@ -39,7 +39,7 @@
|
|||
</div>
|
||||
|
||||
<div class="inner-addon left-addon">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/login_password.svg"></svg-icon>
|
||||
<scrm-image image="login_password"></scrm-image>
|
||||
<input [(ngModel)]="passw"
|
||||
type="password"
|
||||
name="password"
|
||||
|
@ -71,7 +71,7 @@
|
|||
*ngIf="cardState==='back'"
|
||||
[@fade]>
|
||||
<div class="inner-addon left-addon">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/login_user.svg"></svg-icon>
|
||||
<scrm-image image="login_user"></scrm-image>
|
||||
<input [(ngModel)]="uname"
|
||||
type="text"
|
||||
name="username"
|
||||
|
@ -86,7 +86,7 @@
|
|||
</div>
|
||||
|
||||
<div class="inner-addon left-addon">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/email.svg"></svg-icon>
|
||||
<scrm-image image="email"></scrm-image>
|
||||
<input [(ngModel)]="email"
|
||||
type="email"
|
||||
name="email"
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
import {async, ComponentFixture, TestBed, inject, tick, fakeAsync} from '@angular/core/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {LoginUiComponent} from './login.component';
|
||||
import {ApiService} from '../../services/api/api.service';
|
||||
import {LanguageFacade} from '@base/facades/language/language.facade';
|
||||
import {SystemConfigFacade} from '@base/facades/system-config/system-config.facade';
|
||||
import {RecoverPasswordService} from '@services/process/processes/recover-password/recover-password';
|
||||
import {languageFacadeMock} from '@base/facades/language/language.facade.spec.mock';
|
||||
import {systemConfigFacadeMock} from '@base/facades/system-config/system-config.facade.spec.mock';
|
||||
import {
|
||||
recoverPasswordMock,
|
||||
recoverPasswordMockData
|
||||
} from '@services/process/processes/recover-password/recover-passoword.spec.mock';
|
||||
import {recoverPasswordMock} from '@services/process/processes/recover-password/recover-passoword.spec.mock';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {Observable, of} from 'rxjs';
|
||||
|
||||
describe('LoginComponent', () => {
|
||||
let component: LoginUiComponent;
|
||||
|
@ -49,42 +44,39 @@ describe('LoginComponent', () => {
|
|||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', async(inject([HttpTestingController],
|
||||
(router: RouterTestingModule, http: HttpTestingController, api: ApiService) => {
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should flip on forgot password click', () => {
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component).toBeTruthy();
|
||||
})));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('[name="email"]'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('.submit-button'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('.back-link'))).toBeNull();
|
||||
|
||||
it('should flip on forgot password click', async(inject([HttpTestingController],
|
||||
(router: RouterTestingModule, http: HttpTestingController, api: ApiService) => {
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component).toBeTruthy();
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('[name="email"]'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('.submit-button'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('.back-link'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('[name="password"]'))).toBeTruthy();
|
||||
expect(fixture.debugElement.query(By.css('#login-button'))).toBeTruthy();
|
||||
component.flipCard();
|
||||
// same test codmockRecoverPasswordServicee here
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('[name="password"]'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('#login-button'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('.forgotten-password'))).toBeNull();
|
||||
|
||||
expect(fixture.debugElement.query(By.css('[name="password"]'))).toBeTruthy();
|
||||
expect(fixture.debugElement.query(By.css('#login-button'))).toBeTruthy();
|
||||
component.flipCard();
|
||||
// same test codmockRecoverPasswordServicee here
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('[name="password"]'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('#login-button'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('.forgotten-password'))).toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('[name="email"]'))).toBeTruthy();
|
||||
expect(fixture.debugElement.query(By.css('.submit-button'))).toBeTruthy();
|
||||
expect(fixture.debugElement.query(By.css('.back-link'))).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
expect(fixture.debugElement.query(By.css('[name="email"]'))).toBeTruthy();
|
||||
expect(fixture.debugElement.query(By.css('.submit-button'))).toBeTruthy();
|
||||
expect(fixture.debugElement.query(By.css('.back-link'))).toBeTruthy();
|
||||
});
|
||||
})));
|
||||
|
||||
it('should output login fail status', async(inject([HttpTestingController],
|
||||
(router: RouterTestingModule, http: HttpTestingController, api: ApiService, caller: LoginUiComponent) => {
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component).toBeTruthy();
|
||||
console.log = jasmine.createSpy("log");
|
||||
component.onLoginError(component);
|
||||
expect(console.log).toHaveBeenCalledWith('Login failed');
|
||||
});
|
||||
})));
|
||||
it('should output login fail status', () => {
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component).toBeTruthy();
|
||||
console.log = jasmine.createSpy('log');
|
||||
component.onLoginError(component);
|
||||
expect(console.log).toHaveBeenCalledWith('Login failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
import {LoginUiComponent} from './login.component';
|
||||
import {LogoUiModule} from '../logo/logo.module';
|
||||
import {LoginUiRoutes} from './login.routes';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -21,7 +22,8 @@ import {AngularSvgIconModule} from 'angular-svg-icon';
|
|||
AppManagerModule.forChild(LoginUiComponent),
|
||||
RouterModule.forChild(LoginUiRoutes),
|
||||
CommonModule,
|
||||
AngularSvgIconModule
|
||||
AngularSvgIconModule,
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class LoginUiModule {
|
||||
|
|
|
@ -1 +1 @@
|
|||
<img src="public/themes/suite8/images/suitecrm_logo.png" alt="SuiteCRM Logo">
|
||||
<scrm-image image="company_logo"></scrm-image>
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import { AppManagerModule } from '../../app-manager/app-manager.module';
|
||||
import { LogoUiComponent } from './logo.component';
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
import {LogoUiComponent} from './logo.component';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [LogoUiComponent],
|
||||
exports: [LogoUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(LogoUiComponent)
|
||||
]
|
||||
declarations: [LogoUiComponent],
|
||||
exports: [LogoUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(LogoUiComponent),
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class LogoUiModule {}
|
||||
export class LogoUiModule {
|
||||
}
|
||||
|
|
|
@ -2,160 +2,160 @@
|
|||
|
||||
<ng-template #modal let-modal>
|
||||
|
||||
<!-- Start of modal header section -->
|
||||
<!-- Start of modal header section -->
|
||||
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">{{ modalTitle }}</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/close_modal.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- End of modal header section -->
|
||||
|
||||
<!-- Start of modal body section -->
|
||||
|
||||
<div class="modal-body">
|
||||
<h5 class="modal-field-header">Overview</h5>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Aw yeah.</option>
|
||||
<option>You should pick me instead.</option>
|
||||
<option>I think I'm the better option!</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">{{ modalTitle }}</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<scrm-image class="sicon" image="close_modal"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<label class="checkbox-container create-popup-checkbox">
|
||||
<input type="checkbox">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="relate-input-group">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
<button type="button" class="create-popup-button create-popup-arrow">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/cursor.svg"></svg-icon>
|
||||
</button>
|
||||
<button type="button" class="create-popup-button create-popup-cross">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/cross.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="calendar-input-group">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
<button type="button" class="create-popup-button create-popup-calendar">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/calendar.svg"></svg-icon>
|
||||
</button>
|
||||
<button type="button" class="create-popup-button create-popup-cross">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/cross.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Aw yeah.</option>
|
||||
<option>You should pick me instead.</option>
|
||||
<option>I think I'm the better option!</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<label class="checkbox-container create-popup-checkbox">
|
||||
<input type="checkbox">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="relate-input-group">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
<button type="button" class="create-popup-button create-popup-arrow">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/cursor.svg"></svg-icon>
|
||||
</button>
|
||||
<button type="button" class="create-popup-button create-popup-cross">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/cross.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="calendar-input-group">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
<button type="button" class="create-popup-button create-popup-calendar">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/calendar.svg"></svg-icon>
|
||||
</button>
|
||||
<button type="button" class="create-popup-button create-popup-cross">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/cross.svg"></svg-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of modal body section -->
|
||||
<!-- End of modal header section -->
|
||||
|
||||
<!-- Start of modal footer section -->
|
||||
<!-- Start of modal body section -->
|
||||
|
||||
<div class="modal-footer">
|
||||
<div class="row w-100">
|
||||
<div class="col-lg-6 pr-0 mr-0">
|
||||
<div class="modal-options">
|
||||
<label class="modal-checkbox-container">
|
||||
<input type="checkbox">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<p class="modal-redirect-text">
|
||||
Redirect on SAVE
|
||||
<svg-icon class="sicon info-icon" src="public/themes/suite8/images/info.svg"></svg-icon>
|
||||
</p>
|
||||
<div class="modal-body">
|
||||
<h5 class="modal-field-header">Overview</h5>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Aw yeah.</option>
|
||||
<option>You should pick me instead.</option>
|
||||
<option>I think I'm the better option!</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="modal-buttons">
|
||||
<button type="button" class="btn modal-button-cancel" data-dismiss="modal"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="button" class="btn modal-button-save">
|
||||
Save and New
|
||||
</button>
|
||||
<button type="button" class="btn modal-button-save">
|
||||
Save
|
||||
</button>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<label class="checkbox-container create-popup-checkbox">
|
||||
<input type="checkbox">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="relate-input-group">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
<button type="button" class="create-popup-button create-popup-arrow">
|
||||
<scrm-image class="sicon" image="cursor"></scrm-image>
|
||||
</button>
|
||||
<button type="button" class="create-popup-button create-popup-cross">
|
||||
<scrm-image class="sicon" image="cross"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="calendar-input-group">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
<button type="button" class="create-popup-button create-popup-calendar">
|
||||
<scrm-image class="sicon" image="calendar"></scrm-image>
|
||||
</button>
|
||||
<button type="button" class="create-popup-button create-popup-cross">
|
||||
<scrm-image class="sicon" image="cross"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Aw yeah.</option>
|
||||
<option>You should pick me instead.</option>
|
||||
<option>I think I'm the better option!</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<label class="modal-label">Label</label>
|
||||
<label class="checkbox-container create-popup-checkbox">
|
||||
<input type="checkbox">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="relate-input-group">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
<button type="button" class="create-popup-button create-popup-arrow">
|
||||
<scrm-image class="sicon" image="cursor"></scrm-image>
|
||||
</button>
|
||||
<button type="button" class="create-popup-button create-popup-cross">
|
||||
<scrm-image class="sicon" image="cross"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="calendar-input-group">
|
||||
<label class="modal-label">Label</label>
|
||||
<input class="modal-input" type="text">
|
||||
<button type="button" class="create-popup-button create-popup-calendar">
|
||||
<scrm-image class="sicon" image="calendar"></scrm-image>
|
||||
</button>
|
||||
<button type="button" class="create-popup-button create-popup-cross">
|
||||
<scrm-image class="sicon" image="cross"></scrm-image>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of modal footer section -->
|
||||
<!-- End of modal body section -->
|
||||
|
||||
<!-- Start of modal footer section -->
|
||||
|
||||
<div class="modal-footer">
|
||||
<div class="row w-100">
|
||||
<div class="col-lg-6 pr-0 mr-0">
|
||||
<div class="modal-options">
|
||||
<label class="modal-checkbox-container">
|
||||
<input type="checkbox">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<p class="modal-redirect-text">
|
||||
Redirect on SAVE
|
||||
<scrm-image class="sicon info-icon" image="info"></scrm-image>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="modal-buttons">
|
||||
<button type="button" class="btn modal-button-cancel" data-dismiss="modal"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="button" class="btn modal-button-save">
|
||||
Save and New
|
||||
</button>
|
||||
<button type="button" class="btn modal-button-save">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of modal footer section -->
|
||||
|
||||
</ng-template>
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {ModalUiComponent} from './modal.component';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
|
||||
describe('ModalUiComponent', () => {
|
||||
let component: ModalUiComponent;
|
||||
|
@ -8,7 +12,14 @@ describe('ModalUiComponent', () => {
|
|||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ModalUiComponent]
|
||||
declarations: [ModalUiComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
|
|
@ -1,61 +1,57 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
import {NgbModal, ModalDismissReasons} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ModalDismissReasons, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
import {
|
||||
trigger,
|
||||
animate,
|
||||
transition,
|
||||
} from '@angular/animations';
|
||||
import {animate, transition, trigger,} from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-modal-ui',
|
||||
templateUrl: './modal.component.html',
|
||||
animations: [
|
||||
trigger('modalFade', [
|
||||
transition('void <=> *', [
|
||||
animate('800ms')
|
||||
]),
|
||||
]),
|
||||
]
|
||||
selector: 'scrm-modal-ui',
|
||||
templateUrl: './modal.component.html',
|
||||
animations: [
|
||||
trigger('modalFade', [
|
||||
transition('void <=> *', [
|
||||
animate('800ms')
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
|
||||
export class ModalUiComponent implements OnInit {
|
||||
closeResult: string;
|
||||
modalTitle: string = 'New Account';
|
||||
createModal: boolean = true;
|
||||
closeResult: string;
|
||||
modalTitle: string = 'New Account';
|
||||
createModal: boolean = true;
|
||||
|
||||
constructor(private modalService: NgbModal) {
|
||||
}
|
||||
|
||||
open(modal) {
|
||||
this.modalService.open(modal, {
|
||||
ariaLabelledBy: 'modal-basic-title',
|
||||
centered: true,
|
||||
size: 'lg'
|
||||
}).result.then((result) => {
|
||||
this.closeResult = `Closed with: ${result}`;
|
||||
}, (reason) => {
|
||||
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
|
||||
});
|
||||
}
|
||||
|
||||
private getDismissReason(reason: any): string {
|
||||
if (reason === ModalDismissReasons.ESC) {
|
||||
return 'by pressing ESC';
|
||||
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
|
||||
return 'by clicking on a backdrop';
|
||||
} else {
|
||||
return `with: ${reason}`;
|
||||
constructor(private modalService: NgbModal) {
|
||||
}
|
||||
}
|
||||
|
||||
newButtonConfig = {
|
||||
text: 'NEW',
|
||||
buttonClass: 'action-button'
|
||||
};
|
||||
open(modal) {
|
||||
this.modalService.open(modal, {
|
||||
ariaLabelledBy: 'modal-basic-title',
|
||||
centered: true,
|
||||
size: 'lg'
|
||||
}).result.then((result) => {
|
||||
this.closeResult = `Closed with: ${result}`;
|
||||
}, (reason) => {
|
||||
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
private getDismissReason(reason: any): string {
|
||||
if (reason === ModalDismissReasons.ESC) {
|
||||
return 'by pressing ESC';
|
||||
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
|
||||
return 'by clicking on a backdrop';
|
||||
} else {
|
||||
return `with: ${reason}`;
|
||||
}
|
||||
}
|
||||
|
||||
newButtonConfig = {
|
||||
text: 'NEW',
|
||||
buttonClass: 'action-button'
|
||||
};
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
|
@ -6,16 +6,18 @@ import {ModalUiComponent} from './modal.component';
|
|||
|
||||
import {ButtonUiModule} from '../button/button.module';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ModalUiComponent],
|
||||
exports: [ModalUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(ModalUiComponent),
|
||||
ButtonUiModule,
|
||||
AngularSvgIconModule
|
||||
]
|
||||
declarations: [ModalUiComponent],
|
||||
exports: [ModalUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(ModalUiComponent),
|
||||
ButtonUiModule,
|
||||
AngularSvgIconModule,
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class ModalUiModule {
|
||||
}
|
||||
|
|
|
@ -1,255 +1,259 @@
|
|||
|
||||
|
||||
<!-- Start of main navbar section -->
|
||||
|
||||
<div class="top-panel fixed-top" *ngIf="(vm$ | async) as vm">
|
||||
|
||||
<!-- Start of empty navbar section until data is loaded -->
|
||||
|
||||
<ng-template [ngIf]="!loaded">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="navbar-collapse collapse order-4 order-md-0 collapsenav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="top-nav nav-item">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</ng-template>
|
||||
<!-- Start of empty navbar section until data is loaded -->
|
||||
|
||||
<!-- End of empty section until data is loaded -->
|
||||
|
||||
<!-- Start of empty navbar with logo -->
|
||||
|
||||
<ng-template [ngIf]="loaded">
|
||||
<ng-template [ngIf]="!navbar.authenticated">
|
||||
<nav class="navbar ml-0 pl-0">
|
||||
<div class="navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="pl-0">
|
||||
<scrm-logo-ui></scrm-logo-ui>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</ng-template>
|
||||
|
||||
<!-- End of empty navbar section with logo -->
|
||||
|
||||
<!-- Start of mobile navigation section -->
|
||||
|
||||
<ng-template [ngIf]="mobileNavbar">
|
||||
<ul class="navbar mobile-nav-block mobilenavbar">
|
||||
<div class="position-static" ngbDropdown #myDrop="ngbDropdown" [autoClose]="false">
|
||||
<button aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation" ngbDropdownToggle
|
||||
class="navbar-toggler" type="button">
|
||||
<svg-icon class="responsive-menu-toggler" src="public/themes/suite8/images/hamburger.svg"></svg-icon>
|
||||
</button>
|
||||
<div class="mobile-nav-dropdown w-100" ngbDropdownMenu [@mobileNavFade]>
|
||||
<ng-template [ngIf]="backLink">
|
||||
<li ngbDropdownItem><a class="mobile-back-link" (click)="navBackLink($event)">< BACK</a></li>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="mainNavLink">
|
||||
<li class="" *ngFor="let item of navbar.menu" ngbDropdownItem>
|
||||
<a class="mobile-nav-link">{{ item.link.label }}</a>
|
||||
<svg-icon class="sicon-xs mobile-nav-arrow" src="public/themes/suite8/images/arrow_right_filled.svg"
|
||||
(click)="changeSubNav($event, item)"></svg-icon>
|
||||
</li>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="mobileSubNav">
|
||||
<li class="" *ngFor="let sub of submenu" ngbDropdownItem>
|
||||
<a class="mobile-nav-link action-link">
|
||||
{{ sub.link.label }}
|
||||
</a>
|
||||
<ul *ngIf="sub.submenu.length" class="">
|
||||
<li *ngFor="let subitem of sub.submenu" class="nav-item">
|
||||
<a class="mobile-nav-link action-link" href="/#{{ subitem.link.route }}">
|
||||
{{ subitem.link.label }}
|
||||
</a>
|
||||
</li>
|
||||
<ng-template [ngIf]="sub.recentRecords && sub.recentRecords.length">
|
||||
<h4 class="recently-viewed-header">RECENTLY VIEWED</h4>
|
||||
<li class="nav-item" *ngFor="let rec of sub.recentRecords">
|
||||
<a class="mobile-nav-link action-link"
|
||||
href="#/{{ rec.moduleName }}/{{ rec.itemId }}">{{ rec.itemSummary }}</a>
|
||||
<ng-template [ngIf]="!loaded">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="navbar-collapse collapse order-4 order-md-0 collapsenav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="top-nav nav-item">
|
||||
</li>
|
||||
</ng-template>
|
||||
</ul>
|
||||
</li>
|
||||
</ng-template>
|
||||
|
||||
<li>
|
||||
<a class="mobile-nav-close" href="#" (click)="myDrop.close()">
|
||||
<svg-icon class="sicon mobile-nav-close" src="public/themes/suite8/images/cross_bold.svg"></svg-icon>
|
||||
<span class="nav-close-text">close menu</span>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
<div class="global-links" ngbDropdown>
|
||||
<li class="global-link-item">
|
||||
<a class="nav-link primary-global-link dropdown-toggle" ngbDropdownToggle>
|
||||
<svg-icon class="global-action-icon sicon-2x" src="public/themes/suite8/images/user.svg"></svg-icon>
|
||||
{{ navbar.currentUser.name }}
|
||||
</a>
|
||||
<div aria-labelledby="navbarDropdownMenuLink"
|
||||
class="dropdown-menu global-links-dropdown" ngbDropdownMenu>
|
||||
<a class="dropdown-item global-links-sublink" ngbDropdownItem
|
||||
href="#/Users/EditView/{{ navbar.currentUser.id }}">Profile</a>
|
||||
<ng-template [ngIf]="navbar.globalActions">
|
||||
<a class="dropdown-item global-links-sublink" ngbDropdownItem
|
||||
*ngFor="let globalAction of navbar.globalActions"
|
||||
target="{{ globalAction.link.target }}"
|
||||
href="{{ globalAction.link.url }}">{{ globalAction.link.label }}
|
||||
</a>
|
||||
</ng-template>
|
||||
<scrm-logout-ui></scrm-logout-ui>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
|
||||
</nav>
|
||||
</ng-template>
|
||||
|
||||
<!-- End of mobile navigation section-->
|
||||
<!-- End of empty section until data is loaded -->
|
||||
|
||||
<!-- Start of navbar section with data once authenticated -->
|
||||
<!-- Start of empty navbar with logo -->
|
||||
|
||||
<ng-template [ngIf]="navbar.authenticated && !mobileNavbar">
|
||||
<nav class="navbar navbar-expand-md navbar-1">
|
||||
<div class="navbar-collapse collapse collapsenav" [ngbCollapse]="mainNavCollapse">
|
||||
<ul class="navbar-nav home-nav">
|
||||
<li class="nav-item home-nav-link">
|
||||
<a class="home-nav-link" href="#/Home">
|
||||
<svg-icon class="home-icon" src="public/themes/suite8/images/home.svg"></svg-icon>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ng-template [ngIf]="loaded">
|
||||
<ng-template [ngIf]="!navbar.authenticated">
|
||||
<nav class="navbar ml-0 pl-0">
|
||||
<div class="navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="pl-0">
|
||||
<scrm-logo-ui></scrm-logo-ui>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</ng-template>
|
||||
|
||||
<!-- Navbar with grouped tabs -->
|
||||
<!-- End of empty navbar section with logo -->
|
||||
|
||||
<ul *ngIf="vm.userPreferences['navigation_paradigm'] == 'gm'" class="navbar-nav grouped">
|
||||
<li class="top-nav nav-item dropdown main-grouped" *ngFor="let item of navbar.menu">
|
||||
<!-- Start of mobile navigation section -->
|
||||
|
||||
<ng-template [ngIf]="mobileNavbar">
|
||||
<ul class="navbar mobile-nav-block mobilenavbar">
|
||||
<div class="position-static" ngbDropdown #myDrop="ngbDropdown" [autoClose]="false">
|
||||
<button aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"
|
||||
ngbDropdownToggle
|
||||
class="navbar-toggler" type="button">
|
||||
<scrm-image class="responsive-menu-toggler" image="hamburger"></scrm-image>
|
||||
</button>
|
||||
<div class="mobile-nav-dropdown w-100" ngbDropdownMenu [@mobileNavFade]>
|
||||
<ng-template [ngIf]="backLink">
|
||||
<li ngbDropdownItem><a class="mobile-back-link" (click)="navBackLink($event)">< BACK</a>
|
||||
</li>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="mainNavLink">
|
||||
<li class="" *ngFor="let item of navbar.menu" ngbDropdownItem>
|
||||
<a class="mobile-nav-link">{{ item.link.label }}</a>
|
||||
<scrm-image class="sicon-xs mobile-nav-arrow" image="arrow_right_filled"
|
||||
(click)="changeSubNav($event, item)"></scrm-image>
|
||||
</li>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="mobileSubNav">
|
||||
<li class="" *ngFor="let sub of submenu" ngbDropdownItem>
|
||||
<a class="mobile-nav-link action-link">
|
||||
{{ sub.link.label }}
|
||||
</a>
|
||||
<ul *ngIf="sub.submenu.length" class="">
|
||||
<li *ngFor="let subitem of sub.submenu" class="nav-item">
|
||||
<a class="mobile-nav-link action-link" href="/#{{ subitem.link.route }}">
|
||||
{{ subitem.link.label }}
|
||||
</a>
|
||||
</li>
|
||||
<ng-template [ngIf]="sub.recentRecords && sub.recentRecords.length">
|
||||
<h4 class="recently-viewed-header">RECENTLY VIEWED</h4>
|
||||
<li class="nav-item" *ngFor="let rec of sub.recentRecords">
|
||||
<a class="mobile-nav-link action-link"
|
||||
href="#/{{ rec.moduleName }}/{{ rec.itemId }}">{{ rec.itemSummary }}</a>
|
||||
</li>
|
||||
</ng-template>
|
||||
</ul>
|
||||
</li>
|
||||
</ng-template>
|
||||
|
||||
<li>
|
||||
<a class="mobile-nav-close" href="#" (click)="myDrop.close()">
|
||||
<scrm-image class="sicon mobile-nav-close" image="cross_bold"></scrm-image>
|
||||
<span class="nav-close-text">close menu</span>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
<div class="global-links" ngbDropdown>
|
||||
<li class="global-link-item">
|
||||
<a class="nav-link primary-global-link dropdown-toggle" ngbDropdownToggle>
|
||||
<scrm-image class="global-action-icon sicon-2x" image="user"></scrm-image>
|
||||
{{ navbar.currentUser.name }}
|
||||
</a>
|
||||
<div aria-labelledby="navbarDropdownMenuLink"
|
||||
class="dropdown-menu global-links-dropdown" ngbDropdownMenu>
|
||||
<a class="dropdown-item global-links-sublink" ngbDropdownItem
|
||||
href="#/Users/EditView/{{ navbar.currentUser.id }}">Profile</a>
|
||||
<ng-template [ngIf]="navbar.globalActions">
|
||||
<a class="dropdown-item global-links-sublink" ngbDropdownItem
|
||||
*ngFor="let globalAction of navbar.globalActions"
|
||||
target="{{ globalAction.link.target }}"
|
||||
href="{{ globalAction.link.url }}">{{ globalAction.link.label }}
|
||||
</a>
|
||||
</ng-template>
|
||||
<scrm-logout-ui></scrm-logout-ui>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
|
||||
</ng-template>
|
||||
|
||||
<!-- End of mobile navigation section-->
|
||||
|
||||
<!-- Start of navbar section with data once authenticated -->
|
||||
|
||||
<ng-template [ngIf]="navbar.authenticated && !mobileNavbar">
|
||||
<nav class="navbar navbar-expand-md navbar-1">
|
||||
<div class="navbar-collapse collapse collapsenav" [ngbCollapse]="mainNavCollapse">
|
||||
<ul class="navbar-nav home-nav">
|
||||
<li class="nav-item home-nav-link">
|
||||
<a class="home-nav-link" href="#/Home">
|
||||
<scrm-image class="home-icon" image="home"></scrm-image>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Navbar with grouped tabs -->
|
||||
|
||||
<ul *ngIf="vm.userPreferences['navigation_paradigm'] == 'gm'" class="navbar-nav grouped">
|
||||
<li class="top-nav nav-item dropdown main-grouped" *ngFor="let item of navbar.menu">
|
||||
<span data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<a class="nav-link-grouped dropdown-toggle" data-toggle="dropdown" href="{{ item.link.route }}">
|
||||
{{ item.link.label }}
|
||||
</a>
|
||||
</span>
|
||||
<ul class="dropdown-menu main" aria-labelledby="navbarDropdownMenuLink" [ngbCollapse]="subNavCollapse">
|
||||
<li class="nav-item dropdown-submenu submenu" *ngFor="let sub of item.submenu">
|
||||
<a class="nav-link action-link" *ngIf="!sub.submenu.length" href="{{sub.link.route}}">
|
||||
{{ sub.link.label }}
|
||||
</a>
|
||||
<a class="nav-link action-link dropdown-item dropdown-toggle" *ngIf="sub.submenu.length"
|
||||
(click)="subItemCollapse = !subItemCollapse">
|
||||
{{ sub.link.label }}
|
||||
</a>
|
||||
<ul *ngIf="sub.submenu.length" class="dropdown-menu submenu">
|
||||
<li *ngFor="let subitem of sub.submenu" class="nav-item">
|
||||
<a class="nav-link action-link" href="#{{ subitem.link.route }}">
|
||||
<svg-icon *ngIf="subitem.icon" src="public/themes/suite8/images/{{ subitem.icon }}.svg">
|
||||
</svg-icon>
|
||||
{{ subitem.link.label }}
|
||||
</a>
|
||||
</li>
|
||||
<ng-template [ngIf]="sub.recentRecords && sub.recentRecords.length">
|
||||
<h4 class="recently-viewed-header">RECENTLY VIEWED</h4>
|
||||
<li class="nav-item" *ngFor="let rec of sub.recentRecords">
|
||||
<a class="nav-link action-link"
|
||||
href="{{ rec.url }}">{{ rec.summary }}</a>
|
||||
</li>
|
||||
</ng-template>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="dropdown-menu main" aria-labelledby="navbarDropdownMenuLink"
|
||||
[ngbCollapse]="subNavCollapse">
|
||||
<li class="nav-item dropdown-submenu submenu" *ngFor="let sub of item.submenu">
|
||||
<a class="nav-link action-link" *ngIf="!sub.submenu.length"
|
||||
href="{{sub.link.route}}">
|
||||
{{ sub.link.label }}
|
||||
</a>
|
||||
<a class="nav-link action-link dropdown-item dropdown-toggle"
|
||||
*ngIf="sub.submenu.length"
|
||||
(click)="subItemCollapse = !subItemCollapse">
|
||||
{{ sub.link.label }}
|
||||
</a>
|
||||
<ul *ngIf="sub.submenu.length" class="dropdown-menu submenu">
|
||||
<li *ngFor="let subitem of sub.submenu" class="nav-item">
|
||||
<a class="nav-link action-link" href="#{{ subitem.link.route }}">
|
||||
<scrm-image *ngIf="subitem.icon" image="{{ subitem.icon }}">
|
||||
</scrm-image>
|
||||
{{ subitem.link.label }}
|
||||
</a>
|
||||
</li>
|
||||
<ng-template [ngIf]="sub.recentRecords && sub.recentRecords.length">
|
||||
<h4 class="recently-viewed-header">RECENTLY VIEWED</h4>
|
||||
<li class="nav-item" *ngFor="let rec of sub.recentRecords">
|
||||
<a class="nav-link action-link"
|
||||
href="{{ rec.url }}">{{ rec.summary }}</a>
|
||||
</li>
|
||||
</ng-template>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Navbar with non-grouped tabs -->
|
||||
<!-- Navbar with non-grouped tabs -->
|
||||
|
||||
<ul *ngIf="vm.userPreferences['navigation_paradigm'] != 'gm'" class="navbar-nav">
|
||||
<li class="top-nav nav-item dropdown non-grouped" *ngFor="let item of navbar.menu">
|
||||
<ul *ngIf="vm.userPreferences['navigation_paradigm'] != 'gm'" class="navbar-nav">
|
||||
<li class="top-nav nav-item dropdown non-grouped" *ngFor="let item of navbar.menu">
|
||||
|
||||
<!-- TODO: Implement a cleaner solution than hard coded ngIf -->
|
||||
<!-- TODO: Implement a cleaner solution than hard coded ngIf -->
|
||||
|
||||
<ng-template [ngIf]="item.link.label !== 'Home'">
|
||||
<ng-template [ngIf]="item.link.label !== 'Home'">
|
||||
<span data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<a class="nav-link-nongrouped dropdown-toggle"
|
||||
[href]="item.link.url"
|
||||
[routerLink]="item.link.route"
|
||||
[queryParams]="item.link.params"
|
||||
routerLinkActive="active">
|
||||
[href]="item.link.url"
|
||||
[routerLink]="item.link.route"
|
||||
[queryParams]="item.link.params"
|
||||
routerLinkActive="active">
|
||||
{{ item.link.label }}
|
||||
</a>
|
||||
</span>
|
||||
<div aria-labelledby="navbarDropdownMenuLink" class="dropdown-menu submenu">
|
||||
<div class="nav-item" *ngFor="let sub of item.submenu">
|
||||
<a class="nav-link action-link"
|
||||
[href]="sub.link.url"
|
||||
[routerLink]="sub.link.route"
|
||||
[queryParams]="sub.link.params">
|
||||
<svg-icon *ngIf="sub.icon"
|
||||
src="public/themes/suite8/images/{{ sub.icon }}.svg"></svg-icon>
|
||||
{{ sub.link.label }}
|
||||
</a>
|
||||
</div>
|
||||
<ng-template [ngIf]="item.recentRecords && item.recentRecords.length">
|
||||
<h4 class="recently-viewed-header">{{vm.appStrings['LBL_LAST_VIEWED']}}</h4>
|
||||
<div *ngFor="let recentRecord of item.recentRecords" class="nav-item">
|
||||
<a class="nav-link action-link" href="#">{{ recentRecord.summary }}</a>
|
||||
<div aria-labelledby="navbarDropdownMenuLink" class="dropdown-menu submenu">
|
||||
<div class="nav-item" *ngFor="let sub of item.submenu">
|
||||
<a class="nav-link action-link"
|
||||
[href]="sub.link.url"
|
||||
[routerLink]="sub.link.route"
|
||||
[queryParams]="sub.link.params">
|
||||
<scrm-image *ngIf="sub.icon"
|
||||
image="{{ sub.icon }}"></scrm-image>
|
||||
{{ sub.link.label }}
|
||||
</a>
|
||||
</div>
|
||||
<ng-template [ngIf]="item.recentRecords && item.recentRecords.length">
|
||||
<h4 class="recently-viewed-header">{{vm.appStrings['LBL_LAST_VIEWED']}}</h4>
|
||||
<div *ngFor="let recentRecord of item.recentRecords" class="nav-item">
|
||||
<a class="nav-link action-link" href="#">{{ recentRecord.summary }}</a>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
<ul *ngIf="vm.userPreferences['navigation_paradigm'] != 'gm' && navbar.all.modules && navbar.all.modules.length > 0"
|
||||
class="navbar-nav">
|
||||
<li class="top-nav nav-item dropdown non-grouped">
|
||||
<a class="nav-link-nongrouped dropdown-toggle">More</a>
|
||||
<div aria-labelledby="navbarDropdownMenuLink" class="dropdown-menu more-menu submenu">
|
||||
<div class="nav-item" *ngFor="let item of navbar.all.modules">
|
||||
<a class="nav-link action-link" [href]="item.link.url"
|
||||
[routerLink]="item.link.route">{{ item.link.label }}</a>
|
||||
</div>
|
||||
<div class="nav-item" *ngFor="let item of navbar.all.extra">
|
||||
<a class="nav-link action-link" [href]="item.link.url"
|
||||
[routerLink]="item.link.route">{{ item.link.label }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="global-links" ngbDropdown>
|
||||
<ul class="navbar-nav">
|
||||
<li class="global-link-item">
|
||||
<a class="nav-link primary-global-link dropdown-toggle" ngbDropdownToggle>
|
||||
<svg-icon class="global-action-icon sicon-2x" src="public/themes/suite8/images/user.svg"></svg-icon>
|
||||
{{ navbar.currentUser.name }}
|
||||
</a>
|
||||
<div aria-labelledby="navbarDropdownMenuLink"
|
||||
class="dropdown-menu global-links-dropdown dropdown-menu-right" ngbDropdownMenu>
|
||||
<a class="dropdown-item global-links-sublink" ngbDropdownItem
|
||||
href="#/Users/EditView/{{ navbar.currentUser.id }}">Profile</a>
|
||||
<ng-template [ngIf]="navbar.globalActions">
|
||||
<a class="dropdown-item global-links-sublink" ngbDropdownItem
|
||||
*ngFor="let globalAction of navbar.globalActions"
|
||||
target="{{ globalAction.link.target }}"
|
||||
href="{{ globalAction.link.url }}">{{ globalAction.link.label }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul *ngIf="vm.userPreferences['navigation_paradigm'] != 'gm' && navbar.all.modules && navbar.all.modules.length > 0"
|
||||
class="navbar-nav">
|
||||
<li class="top-nav nav-item dropdown non-grouped">
|
||||
<a class="nav-link-nongrouped dropdown-toggle">More</a>
|
||||
<div aria-labelledby="navbarDropdownMenuLink" class="dropdown-menu more-menu submenu">
|
||||
<div class="nav-item" *ngFor="let item of navbar.all.modules">
|
||||
<a class="nav-link action-link" [href]="item.link.url"
|
||||
[routerLink]="item.link.route">{{ item.link.label }}</a>
|
||||
</div>
|
||||
<div class="nav-item" *ngFor="let item of navbar.all.extra">
|
||||
<a class="nav-link action-link" [href]="item.link.url"
|
||||
[routerLink]="item.link.route">{{ item.link.label }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="global-links" ngbDropdown>
|
||||
<ul class="navbar-nav">
|
||||
<li class="global-link-item">
|
||||
<a class="nav-link primary-global-link dropdown-toggle" ngbDropdownToggle>
|
||||
<scrm-image class="global-action-icon sicon-2x" image="user"></scrm-image>
|
||||
{{ navbar.currentUser.name }}
|
||||
</a>
|
||||
<div aria-labelledby="navbarDropdownMenuLink"
|
||||
class="dropdown-menu global-links-dropdown dropdown-menu-right" ngbDropdownMenu>
|
||||
<a class="dropdown-item global-links-sublink" ngbDropdownItem
|
||||
href="#/Users/EditView/{{ navbar.currentUser.id }}">Profile</a>
|
||||
<ng-template [ngIf]="navbar.globalActions">
|
||||
<a class="dropdown-item global-links-sublink" ngbDropdownItem
|
||||
*ngFor="let globalAction of navbar.globalActions"
|
||||
target="{{ globalAction.link.target }}"
|
||||
href="{{ globalAction.link.url }}">{{ globalAction.link.label }}
|
||||
</a>
|
||||
</ng-template>
|
||||
<scrm-logout-ui></scrm-logout-ui>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- End of navbar section with data once authenticated -->
|
||||
|
||||
<scrm-action-bar-ui></scrm-action-bar-ui>
|
||||
</ng-template>
|
||||
<scrm-logout-ui></scrm-logout-ui>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- End of navbar section with data once authenticated -->
|
||||
|
||||
<scrm-action-bar-ui></scrm-action-bar-ui>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<!-- End of main navbar section -->
|
|
@ -1,7 +1,7 @@
|
|||
import {async, ComponentFixture, TestBed, inject, fakeAsync, tick} from '@angular/core/testing';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
|
||||
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
|
@ -10,8 +10,8 @@ import {NavigationFacade} from '@base/facades/navigation/navigation.facade';
|
|||
import {LanguageFacade} from '@base/facades/language/language.facade';
|
||||
import {navigationMock} from '@base/facades/navigation/navigation.facade.spec.mock';
|
||||
import {languageFacadeMock} from '@base/facades/language/language.facade.spec.mock';
|
||||
import {UserPreferenceFacade} from "@base/facades/user-preference/user-preference.facade";
|
||||
import {userPreferenceFacadeMock} from "@base/facades/user-preference/user-preference.facade.spec.mock";
|
||||
import {UserPreferenceFacade} from '@base/facades/user-preference/user-preference.facade';
|
||||
import {userPreferenceFacadeMock} from '@base/facades/user-preference/user-preference.facade.spec.mock';
|
||||
|
||||
describe('NavbarUiComponent', () => {
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
|
@ -8,8 +8,8 @@ import {LogoUiModule} from '../logo/logo.module';
|
|||
import {LogoutUiModule} from '../logout/logout.module';
|
||||
import {ActionBarUiModule} from '../action-bar/action-bar.module';
|
||||
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
|
@ -22,8 +22,8 @@ import {RouterModule} from '@angular/router';
|
|||
LogoutUiModule,
|
||||
ActionBarUiModule,
|
||||
NgbModule,
|
||||
AngularSvgIconModule,
|
||||
RouterModule
|
||||
RouterModule,
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class NavbarUiModule {
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
<div class="bulk-action float-right">
|
||||
<button class="pagination-button" aria-label="Navigate to first page">
|
||||
<svg-icon class="sicon-2x pagination-icons" src="public/themes/suite8/images/paginate_previous.svg">
|
||||
</svg-icon>
|
||||
</button>
|
||||
<button class="pagination-button" aria-label="Previous page">
|
||||
<svg-icon class="sicon-2x pagination-icons" src="public/themes/suite8/images/paginate_first.svg">
|
||||
</svg-icon>
|
||||
</button>
|
||||
<span class="pagination-count" [ngClass]="{
|
||||
'hide-pagination-count': displayResponsiveTable
|
||||
}">
|
||||
(1- 20 of 200)
|
||||
</span>
|
||||
<button class="pagination-button" aria-label="Next page">
|
||||
<svg-icon class="sicon-2x pagination-icons" src="public/themes/suite8/images/paginate_last.svg">
|
||||
</svg-icon>
|
||||
</button>
|
||||
<button class="pagination-button" aria-label="Navigate to last page">
|
||||
<svg-icon class="sicon-2x pagination-icons" src="public/themes/suite8/images/paginate_next.svg">
|
||||
</svg-icon>
|
||||
</button>
|
||||
<button class="pagination-button" aria-label="Navigate to first page">
|
||||
<scrm-image class="sicon-2x pagination-icons" image="paginate_previous">
|
||||
</scrm-image>
|
||||
</button>
|
||||
<button class="pagination-button" aria-label="Previous page">
|
||||
<scrm-image class="sicon-2x pagination-icons" image="paginate_first">
|
||||
</scrm-image>
|
||||
</button>
|
||||
<span class="pagination-count"
|
||||
[ngClass]="{
|
||||
'hide-pagination-count': displayResponsiveTable
|
||||
}"
|
||||
>
|
||||
(1- 20 of 200)
|
||||
</span>
|
||||
<button class="pagination-button" aria-label="Next page">
|
||||
<scrm-image class="sicon-2x pagination-icons" image="paginate_last">
|
||||
</scrm-image>
|
||||
</button>
|
||||
<button class="pagination-button" aria-label="Navigate to last page">
|
||||
<scrm-image class="sicon-2x pagination-icons" image="paginate_next">
|
||||
</scrm-image>
|
||||
</button>
|
||||
</div>
|
|
@ -3,6 +3,11 @@ import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
|||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {PaginationUiComponent} from './pagination.component';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
describe('PaginationUiComponent', () => {
|
||||
let component: PaginationUiComponent;
|
||||
|
@ -12,9 +17,17 @@ describe('PaginationUiComponent', () => {
|
|||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
AngularSvgIconModule,
|
||||
HttpClientTestingModule
|
||||
HttpClientTestingModule,
|
||||
ImageModule
|
||||
],
|
||||
declarations: [PaginationUiComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
declarations: [PaginationUiComponent, ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
@ -26,6 +39,6 @@ describe('PaginationUiComponent', () => {
|
|||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-pagination-ui',
|
||||
templateUrl: 'pagination.component.html'
|
||||
selector: 'scrm-pagination-ui',
|
||||
templateUrl: 'pagination.component.html'
|
||||
})
|
||||
|
||||
export class PaginationUiComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
ngOnInit() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
import {PaginationUiComponent} from './pagination.component';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [PaginationUiComponent],
|
||||
|
@ -11,8 +11,8 @@ import {AngularSvgIconModule} from 'angular-svg-icon';
|
|||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(PaginationUiComponent),
|
||||
AngularSvgIconModule
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class PaginationUiModule {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<div class="list-view-settings float-right">
|
||||
<button type="button" class="settings-button dropdown-toggle">
|
||||
Display As
|
||||
</button>
|
||||
<button type="button" class="settings-button dropdown-toggle">
|
||||
My Filters
|
||||
</button>
|
||||
<scrm-filter-ui></scrm-filter-ui>
|
||||
<button type="button" class="settings-button">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/reset.svg"></svg-icon>
|
||||
Reset
|
||||
</button>
|
||||
<scrm-columnchooser-ui></scrm-columnchooser-ui>
|
||||
<button type="button" class="settings-button" (click)="toggleWidgets()"
|
||||
[ngClass]="{ 'charts-active': widgetService.displayWidgets }">
|
||||
<svg-icon class="sicon" src="public/themes/suite8/images/pie.svg"></svg-icon>
|
||||
Charts
|
||||
</button>
|
||||
<button type="button" class="settings-button dropdown-toggle">
|
||||
Display As
|
||||
</button>
|
||||
<button type="button" class="settings-button dropdown-toggle">
|
||||
My Filters
|
||||
</button>
|
||||
<scrm-filter-ui></scrm-filter-ui>
|
||||
<button type="button" class="settings-button">
|
||||
<scrm-image class="sicon" image="reset"></scrm-image>
|
||||
Reset
|
||||
</button>
|
||||
<scrm-columnchooser-ui></scrm-columnchooser-ui>
|
||||
<button type="button" class="settings-button" (click)="toggleWidgets()"
|
||||
[ngClass]="{ 'charts-active': widgetService.displayWidgets }">
|
||||
<scrm-image class="sicon" image="pie"></scrm-image>
|
||||
Charts
|
||||
</button>
|
||||
</div>
|
|
@ -3,8 +3,13 @@ import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
|||
import {SettingsmenuUiComponent} from './settings-menu.component';
|
||||
import {ColumnchooserUiModule} from '@components/columnchooser/columnchooser.module';
|
||||
import {FilterUiModule} from '@components/filter/filter.module';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {ApolloTestingModule} from 'apollo-angular/testing';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
describe('SettingsmenuUiComponent', () => {
|
||||
let component: SettingsmenuUiComponent;
|
||||
|
@ -15,10 +20,18 @@ describe('SettingsmenuUiComponent', () => {
|
|||
imports: [
|
||||
ColumnchooserUiModule,
|
||||
FilterUiModule,
|
||||
AngularSvgIconModule,
|
||||
HttpClientTestingModule
|
||||
HttpClientTestingModule,
|
||||
ApolloTestingModule,
|
||||
ImageModule
|
||||
],
|
||||
declarations: [SettingsmenuUiComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
declarations: [SettingsmenuUiComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
@ -30,6 +43,6 @@ describe('SettingsmenuUiComponent', () => {
|
|||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import { WidgetService } from '../widget/widget.service';
|
||||
import {NgbModal, ModalDismissReasons} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {WidgetService} from '../widget/widget.service';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-settings-menu-ui',
|
||||
templateUrl: 'settings-menu.component.html',
|
||||
selector: 'scrm-settings-menu-ui',
|
||||
templateUrl: 'settings-menu.component.html',
|
||||
|
||||
})
|
||||
|
||||
export class SettingsmenuUiComponent implements OnInit {
|
||||
|
||||
constructor(private widgetService: WidgetService) {}
|
||||
constructor(private widgetService: WidgetService) {
|
||||
}
|
||||
|
||||
toggleWidgets() {
|
||||
this.widgetService.emitData();
|
||||
}
|
||||
toggleWidgets(): void {
|
||||
this.widgetService.emitData();
|
||||
}
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {AppManagerModule} from '../../app-manager/app-manager.module';
|
||||
|
@ -6,18 +6,18 @@ import {SettingsmenuUiComponent} from './settings-menu.component';
|
|||
|
||||
import {ColumnchooserUiModule} from '../columnchooser/columnchooser.module';
|
||||
import {FilterUiModule} from '../filter/filter.module';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SettingsmenuUiComponent],
|
||||
exports: [SettingsmenuUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(SettingsmenuUiComponent),
|
||||
ColumnchooserUiModule,
|
||||
FilterUiModule,
|
||||
AngularSvgIconModule
|
||||
]
|
||||
declarations: [SettingsmenuUiComponent],
|
||||
exports: [SettingsmenuUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppManagerModule.forChild(SettingsmenuUiComponent),
|
||||
ColumnchooserUiModule,
|
||||
FilterUiModule,
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class SettingsmenuUiModule {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,12 @@ import {PaginationUiModule} from '@components/pagination/pagination.module';
|
|||
import {BulkactionmenuUiModule} from '@components/bulk-action-menu/bulk-action-menu.module';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {ApolloTestingModule} from 'apollo-angular/testing';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
|
||||
describe('TablefooterUiComponent', () => {
|
||||
let component: TablefooterUiComponent;
|
||||
|
@ -16,9 +22,18 @@ describe('TablefooterUiComponent', () => {
|
|||
PaginationUiModule,
|
||||
BulkactionmenuUiModule,
|
||||
AngularSvgIconModule,
|
||||
HttpClientTestingModule
|
||||
HttpClientTestingModule,
|
||||
ApolloTestingModule,
|
||||
ImageModule
|
||||
],
|
||||
declarations: [TablefooterUiComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
declarations: [TablefooterUiComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
@ -30,6 +45,6 @@ describe('TablefooterUiComponent', () => {
|
|||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,6 +5,12 @@ import {PaginationUiModule} from '@components/pagination/pagination.module';
|
|||
import {BulkactionmenuUiModule} from '@components/bulk-action-menu/bulk-action-menu.module';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {ApolloTestingModule} from 'apollo-angular/testing';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
|
||||
describe('TableheaderUiComponent', () => {
|
||||
let component: TableheaderUiComponent;
|
||||
|
@ -16,9 +22,18 @@ describe('TableheaderUiComponent', () => {
|
|||
PaginationUiModule,
|
||||
BulkactionmenuUiModule,
|
||||
AngularSvgIconModule,
|
||||
HttpClientTestingModule
|
||||
HttpClientTestingModule,
|
||||
ApolloTestingModule,
|
||||
ImageModule
|
||||
],
|
||||
declarations: [TableheaderUiComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
declarations: [TableheaderUiComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
@ -30,6 +45,6 @@ describe('TableheaderUiComponent', () => {
|
|||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,12 @@ import {TablebodyUiModule} from '@components/table/table-body/table-body.module'
|
|||
import {TablefooterUiModule} from '@components/table/table-footer/table-footer.module';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {ApolloTestingModule} from 'apollo-angular/testing';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
|
||||
describe('TableUiComponent', () => {
|
||||
let component: TableUiComponent;
|
||||
|
@ -18,9 +24,18 @@ describe('TableUiComponent', () => {
|
|||
TablebodyUiModule,
|
||||
TablefooterUiModule,
|
||||
AngularSvgIconModule,
|
||||
HttpClientTestingModule
|
||||
HttpClientTestingModule,
|
||||
ApolloTestingModule,
|
||||
ImageModule
|
||||
],
|
||||
declarations: [TableUiComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
declarations: [TableUiComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
@ -32,6 +47,6 @@ describe('TableUiComponent', () => {
|
|||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
<!-- Start List View Widget Section -->
|
||||
|
||||
<div class="accordion list-view-widget mr-0">
|
||||
<div class="widget-header">
|
||||
<h3>
|
||||
Quick Charts
|
||||
<svg-icon class="sicon float-right widget-close-icon"
|
||||
(click)="toggleWidgetContent()"
|
||||
src="{{ widgetHeaderToggleIcon }}">
|
||||
</svg-icon>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="widget-header">
|
||||
<h3>
|
||||
Quick Charts
|
||||
<scrm-image class="sicon float-right widget-close-icon"
|
||||
(click)="toggleWidgetContent()"
|
||||
image="{{ widgetHeaderToggleIcon }}">
|
||||
</scrm-image>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div *ngIf="displayWidgetContent" class="widget collapse show" data-parent=".list-view-widget"
|
||||
[@widgetContentFade]="displayWidgetContent ? 'true' : 'false'" class="open-close-container">
|
||||
<scrm-chart-ui></scrm-chart-ui>
|
||||
</div>
|
||||
<div *ngIf="displayWidgetContent" class="widget collapse show" data-parent=".list-view-widget"
|
||||
[@widgetContentFade]="displayWidgetContent ? 'true' : 'false'" class="open-close-container">
|
||||
<scrm-chart-ui></scrm-chart-ui>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End List View Widget Section -->
|
|
@ -1,35 +1,50 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {WidgetUiComponent} from './widget.component';
|
||||
import {AngularSvgIconModule} from "angular-svg-icon";
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ChartUiModule} from '@components/chart/chart.module';
|
||||
import {HttpClientTestingModule} from "@angular/common/http/testing";
|
||||
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {ApolloTestingModule} from 'apollo-angular/testing';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {of} from 'rxjs';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
describe('WidgetUiComponent', () => {
|
||||
let component: WidgetUiComponent;
|
||||
let fixture: ComponentFixture<WidgetUiComponent>;
|
||||
let component: WidgetUiComponent;
|
||||
let fixture: ComponentFixture<WidgetUiComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
AngularSvgIconModule,
|
||||
ChartUiModule,
|
||||
HttpClientTestingModule,
|
||||
BrowserAnimationsModule
|
||||
],
|
||||
declarations: [WidgetUiComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
AngularSvgIconModule,
|
||||
ChartUiModule,
|
||||
HttpClientTestingModule,
|
||||
BrowserAnimationsModule,
|
||||
ApolloTestingModule,
|
||||
ImageModule
|
||||
],
|
||||
declarations: [WidgetUiComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WidgetUiComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WidgetUiComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import { ChartUiComponent } from "@components/chart/chart.component";
|
||||
import {animate, style, transition, trigger} from "@angular/animations";
|
||||
import {animate, style, transition, trigger} from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-widget-ui',
|
||||
templateUrl: 'widget.component.html',
|
||||
animations: [
|
||||
trigger("widgetFade", [
|
||||
transition("void => *", [
|
||||
style({transform: "translateX(100%)", opacity: 0}),
|
||||
animate("500ms", style({transform: "translateX(0)", opacity: 1}))
|
||||
trigger('widgetFade', [
|
||||
transition('void => *', [
|
||||
style({transform: 'translateX(100%)', opacity: 0}),
|
||||
animate('500ms', style({transform: 'translateX(0)', opacity: 1}))
|
||||
]),
|
||||
transition("* => void", [
|
||||
style({transform: "translateX(0)", opacity: 1}),
|
||||
animate("500ms", style({transform: "translateX(100%)", opacity: 0}))
|
||||
transition('* => void', [
|
||||
style({transform: 'translateX(0)', opacity: 1}),
|
||||
animate('500ms', style({transform: 'translateX(100%)', opacity: 0}))
|
||||
])
|
||||
]),
|
||||
trigger("widgetContentFade", [
|
||||
transition("void => *", [
|
||||
style({transform: "translateY(-5%)", opacity: 0}),
|
||||
animate("500ms", style({transform: "translateY(0)", opacity: 1}))
|
||||
trigger('widgetContentFade', [
|
||||
transition('void => *', [
|
||||
style({transform: 'translateY(-5%)', opacity: 0}),
|
||||
animate('500ms', style({transform: 'translateY(0)', opacity: 1}))
|
||||
]),
|
||||
transition("* => void", [
|
||||
style({transform: "translateY(0)", opacity: 1}),
|
||||
animate("500ms", style({transform: "translateY(-5%)", opacity: 0}))
|
||||
transition('* => void', [
|
||||
style({transform: 'translateY(0)', opacity: 1}),
|
||||
animate('500ms', style({transform: 'translateY(-5%)', opacity: 0}))
|
||||
])
|
||||
])
|
||||
]
|
||||
|
@ -32,15 +31,15 @@ import {animate, style, transition, trigger} from "@angular/animations";
|
|||
export class WidgetUiComponent implements OnInit {
|
||||
|
||||
|
||||
displayWidgetContent: boolean = true;
|
||||
widgetHeaderToggleIcon: string = "public/themes/suite8/images/minimise_circled.svg";
|
||||
displayWidgetContent = true;
|
||||
widgetHeaderToggleIcon = 'minimise_circled';
|
||||
|
||||
toggleWidgetContent() {
|
||||
if (this.widgetHeaderToggleIcon == "public/themes/suite8/images/minimise_circled.svg") {
|
||||
this.widgetHeaderToggleIcon = "public/themes/suite8/images/plus_thin.svg";
|
||||
if (this.widgetHeaderToggleIcon === 'minimise_circled') {
|
||||
this.widgetHeaderToggleIcon = 'plus_thin';
|
||||
this.displayWidgetContent = false;
|
||||
} else {
|
||||
this.widgetHeaderToggleIcon = "public/themes/suite8/images/minimise_circled.svg";
|
||||
this.widgetHeaderToggleIcon = 'minimise_circled';
|
||||
this.displayWidgetContent = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {WidgetUiComponent} from './widget.component';
|
||||
|
||||
import {ChartUiModule} from '../chart/chart.module';
|
||||
import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||
import {ChartUiModule} from '@components/chart/chart.module';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [WidgetUiComponent],
|
||||
exports: [WidgetUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ChartUiModule,
|
||||
AngularSvgIconModule
|
||||
]
|
||||
declarations: [WidgetUiComponent],
|
||||
exports: [WidgetUiComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ChartUiModule,
|
||||
ImageModule
|
||||
]
|
||||
})
|
||||
export class WidgetUiModule {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import {HostListener, Injectable} from '@angular/core';
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class WidgetService {
|
||||
|
||||
displayWidgets: boolean = true;
|
||||
widgetHeaderToggleIcon: string = "public/themes/suite8/images/minimise_circled.svg";
|
||||
listViewFullWidth: boolean = true;
|
||||
displayWidgets = true;
|
||||
widgetHeaderToggleIcon = 'public/themes/suite8/images/minimise_circled.svg';
|
||||
listViewFullWidth = true;
|
||||
|
||||
emitData() {
|
||||
this.displayWidgets = !this.displayWidgets;
|
||||
this.listViewFullWidth = !this.listViewFullWidth;
|
||||
}
|
||||
constructor() { }
|
||||
constructor() {
|
||||
}
|
||||
|
||||
}
|
||||
emitData() {
|
||||
this.displayWidgets = !this.displayWidgets;
|
||||
this.listViewFullWidth = !this.listViewFullWidth;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
import {BehaviorSubject, Observable, of} from 'rxjs';
|
||||
import {shareReplay} from 'rxjs/operators';
|
||||
import {ThemeImages, ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {RecordGQL} from '@services/api/graphql-api/api.record.get';
|
||||
import {appStateFacadeMock} from '@base/facades/app-state/app-state.facade.spec.mock';
|
||||
import {AppStateFacade} from '@base/facades/app-state/app-state.facade';
|
||||
|
||||
export const themeImagesMockData = {
|
||||
bgOcher: {
|
||||
path: 'legacy/themes/default/images/bgOcher.gif',
|
||||
name: 'bgOcher',
|
||||
type: 'gif'
|
||||
},
|
||||
Createjjwg_Maps: {
|
||||
path: 'legacy/themes/default/images/Createjjwg_Maps.gif',
|
||||
name: 'Createjjwg_Maps',
|
||||
type: 'gif'
|
||||
},
|
||||
close: {
|
||||
path: 'legacy/themes/suite8/images/close.png',
|
||||
name: 'close',
|
||||
type: 'png'
|
||||
},
|
||||
ProjectCopy: {
|
||||
path: 'legacy/themes/default/images/ProjectCopy.gif',
|
||||
name: 'ProjectCopy',
|
||||
type: 'gif'
|
||||
},
|
||||
line: {
|
||||
path: 'legacy/themes/default/images/line.gif',
|
||||
name: 'line',
|
||||
type: 'gif'
|
||||
},
|
||||
download: {
|
||||
path: 'core/app/themes/suite8/images/download.svg',
|
||||
name: 'download',
|
||||
type: 'svg'
|
||||
},
|
||||
paginate_next: {
|
||||
path: 'core/app/themes/suite8/images/paginate_next.svg',
|
||||
name: 'paginate_next',
|
||||
type: 'svg'
|
||||
},
|
||||
filter: {
|
||||
path: 'core/app/themes/suite8/images/filter.svg',
|
||||
name: 'filter',
|
||||
type: 'svg'
|
||||
},
|
||||
reset: {
|
||||
path: 'core/app/themes/suite8/images/reset.svg',
|
||||
name: 'reset',
|
||||
type: 'svg'
|
||||
},
|
||||
column_chooser: {
|
||||
path: 'core/app/themes/suite8/images/column_chooser.svg',
|
||||
name: 'column_chooser',
|
||||
type: 'svg'
|
||||
},
|
||||
pie: {
|
||||
path: 'core/app/themes/suite8/images/pie.svg',
|
||||
name: 'pie',
|
||||
type: 'svg'
|
||||
},
|
||||
cross: {
|
||||
path: 'core/app/themes/suite8/images/cross.svg',
|
||||
name: 'cross',
|
||||
type: 'svg'
|
||||
},
|
||||
plus: {
|
||||
path: 'core/app/themes/suite8/images/plus.svg',
|
||||
name: 'plus',
|
||||
type: 'svg'
|
||||
},
|
||||
edit: {
|
||||
path: 'core/app/themes/suite8/images/edit.svg',
|
||||
name: 'edit',
|
||||
type: 'svg'
|
||||
},
|
||||
minimise_circled: {
|
||||
path: 'core/app/themes/suite8/images/minimise_circled.svg',
|
||||
name: 'minimise_circled',
|
||||
type: 'svg'
|
||||
},
|
||||
paginate_previous: {
|
||||
path: 'core/app/themes/suite8/images/paginate_previous.svg',
|
||||
name: 'paginate_previous',
|
||||
type: 'svg'
|
||||
},
|
||||
paginate_first: {
|
||||
path: 'core/app/themes/suite8/images/paginate_first.svg',
|
||||
name: 'paginate_first',
|
||||
type: 'svg'
|
||||
},
|
||||
paginate_last: {
|
||||
path: 'core/app/themes/suite8/images/paginate_last.svg',
|
||||
name: 'paginate_last',
|
||||
type: 'svg'
|
||||
}
|
||||
};
|
||||
|
||||
class ThemeImagesRecordGQLSpy extends RecordGQL {
|
||||
|
||||
constructor() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public fetch(module: string, id: string, metadata: { fields: string[] }): Observable<any> {
|
||||
|
||||
return of({
|
||||
data: {
|
||||
themeImages: {
|
||||
_id: 'suite8',
|
||||
items: themeImagesMockData
|
||||
}
|
||||
}
|
||||
}).pipe(shareReplay(1));
|
||||
}
|
||||
}
|
||||
|
||||
class MockThemeImagesFacade extends ThemeImagesFacade {
|
||||
protected store = new BehaviorSubject<ThemeImages>({
|
||||
theme: 'suite8',
|
||||
images: themeImagesMockData
|
||||
});
|
||||
|
||||
constructor(protected recordGQL: RecordGQL, protected appStateFacade: AppStateFacade) {
|
||||
super(recordGQL, appStateFacade);
|
||||
}
|
||||
}
|
||||
|
||||
export const themeImagesFacadeMock = new MockThemeImagesFacade(new ThemeImagesRecordGQLSpy(), appStateFacadeMock);
|
|
@ -0,0 +1,21 @@
|
|||
import {getTestBed, TestBed} from '@angular/core/testing';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {themeImagesFacadeMock, themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
|
||||
describe('ThemeImages Facade', () => {
|
||||
const service: ThemeImagesFacade = themeImagesFacadeMock;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
});
|
||||
|
||||
it('#load',
|
||||
(done: DoneFn) => {
|
||||
|
||||
service.load('suite8').subscribe(data => {
|
||||
expect(data).toEqual(jasmine.objectContaining(themeImagesMockData));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
152
core/app/src/facades/theme-images/theme-images.facade.ts
Normal file
152
core/app/src/facades/theme-images/theme-images.facade.ts
Normal file
|
@ -0,0 +1,152 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {map, distinctUntilChanged, tap, shareReplay} from 'rxjs/operators';
|
||||
|
||||
import {RecordGQL} from '@services/api/graphql-api/api.record.get';
|
||||
import {AppStateFacade} from '@base/facades/app-state/app-state.facade';
|
||||
|
||||
export interface ThemeImage {
|
||||
path: string;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface ThemeImages {
|
||||
theme: string;
|
||||
images: ThemeImageMap;
|
||||
}
|
||||
|
||||
export interface ThemeImageMap {
|
||||
[key: string]: ThemeImage;
|
||||
}
|
||||
|
||||
let internalState: ThemeImages = {
|
||||
theme: null,
|
||||
images: {}
|
||||
};
|
||||
|
||||
let cachedTheme = null;
|
||||
let cache$: Observable<any> = null;
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ThemeImagesFacade {
|
||||
|
||||
protected store = new BehaviorSubject<ThemeImages>(internalState);
|
||||
protected state$ = this.store.asObservable();
|
||||
protected resourceName = 'themeImages';
|
||||
protected frontendName = 'theme-images';
|
||||
protected fieldsMetadata = {
|
||||
fields: [
|
||||
'id',
|
||||
'_id',
|
||||
'items'
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* Public long-lived observable streams
|
||||
*/
|
||||
images$ = this.state$.pipe(map(state => state.images), distinctUntilChanged());
|
||||
|
||||
constructor(protected recordGQL: RecordGQL, protected appStateFacade: AppStateFacade) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Public Api
|
||||
*/
|
||||
|
||||
/**
|
||||
* Change the current theme
|
||||
*
|
||||
* @param theme
|
||||
*/
|
||||
public changeTheme(theme: string): void {
|
||||
|
||||
this.appStateFacade.updateLoading(true);
|
||||
|
||||
this.load(theme).pipe(
|
||||
tap(() => this.appStateFacade.updateLoading(false))
|
||||
).subscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently active image theme
|
||||
*
|
||||
* @returns string
|
||||
*/
|
||||
public getTheme(): string {
|
||||
return internalState.theme;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initial ThemeImages load if not cached and update state.
|
||||
* Returns observable to be used in resolver if needed
|
||||
*
|
||||
* @returns Observable<any>
|
||||
*/
|
||||
public load(theme: string): Observable<any> {
|
||||
|
||||
return this.getThemeImages(theme).pipe(
|
||||
tap(images => {
|
||||
this.updateState({...internalState, images, theme});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update the state
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
protected updateState(state: ThemeImages) {
|
||||
this.store.next(internalState = state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get theme images cached Observable or call the backend
|
||||
*
|
||||
* @return Observable<any>
|
||||
*/
|
||||
protected getThemeImages(theme: string): Observable<any> {
|
||||
|
||||
if (cachedTheme !== theme || cache$ === null) {
|
||||
cachedTheme = theme;
|
||||
cache$ = this.fetch(theme).pipe(
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
return cache$;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the theme images from the backend
|
||||
*
|
||||
* @returns Observable<any>
|
||||
*/
|
||||
protected fetch(theme: string): Observable<any> {
|
||||
|
||||
return this.recordGQL
|
||||
.fetch(this.resourceName, `/api/${this.frontendName}/${theme}`, this.fieldsMetadata)
|
||||
.pipe(
|
||||
map(({data}) => {
|
||||
let images = {};
|
||||
|
||||
if (data && data.themeImages) {
|
||||
images = data.themeImages.items;
|
||||
}
|
||||
|
||||
return images;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,15 +4,8 @@ import {map, distinctUntilChanged, tap, shareReplay} from 'rxjs/operators';
|
|||
|
||||
import {CollectionGQL} from '@services/api/graphql-api/api.collection.get';
|
||||
|
||||
export interface UserPreference {
|
||||
id: string;
|
||||
_id: string;
|
||||
value: string;
|
||||
items: { [key: string]: any };
|
||||
}
|
||||
|
||||
export interface UserPreferenceMap {
|
||||
[key: string]: UserPreference;
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface UserPreferences {
|
||||
|
@ -20,7 +13,6 @@ export interface UserPreferences {
|
|||
loading: boolean;
|
||||
}
|
||||
|
||||
|
||||
let internalState: UserPreferences = {
|
||||
userPreferences: {},
|
||||
loading: false
|
||||
|
@ -57,6 +49,20 @@ export class UserPreferenceFacade {
|
|||
* Public Api
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get user preferences value by key
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public getUserPreference(key: string): string {
|
||||
|
||||
if (!internalState.userPreferences || !internalState.userPreferences[key]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return internalState.userPreferences[key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initial UserPreferences load if not cached and update state.
|
||||
|
@ -117,9 +123,14 @@ export class UserPreferenceFacade {
|
|||
|
||||
if (data.userPreferences && data.userPreferences.edges) {
|
||||
data.userPreferences.edges.forEach((edge) => {
|
||||
for (const key in edge.node.items) {
|
||||
userPreferences[key] = edge.node.items[key];
|
||||
|
||||
if (!edge.node.items) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(edge.node.items).forEach(key => {
|
||||
userPreferences[key] = edge.node.items[key];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {
|
||||
Resolve,
|
||||
ActivatedRouteSnapshot,
|
||||
RouterStateSnapshot
|
||||
} from '@angular/router';
|
||||
|
||||
import {UserPreferenceFacade} from '@base/facades/user-preference/user-preference.facade';
|
||||
import {flatMap} from 'rxjs/operators';
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class UserPreferenceResolver implements Resolve<any> {
|
||||
|
||||
constructor(private userPreferenceFacade: UserPreferenceFacade) {
|
||||
}
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
return this.userPreferenceFacade.load();
|
||||
}
|
||||
}
|
|
@ -1,15 +1,13 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {
|
||||
Resolve,
|
||||
ActivatedRouteSnapshot,
|
||||
RouterStateSnapshot
|
||||
} from '@angular/router';
|
||||
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router';
|
||||
import {concatAll, map, toArray} from 'rxjs/operators';
|
||||
import {forkJoin, Observable} from 'rxjs';
|
||||
|
||||
import {SystemConfigFacade} from '@base/facades/system-config/system-config.facade';
|
||||
import {LanguageFacade} from '@base/facades/language/language.facade';
|
||||
import {concatAll, first, flatMap, map, mergeMap, tap, toArray} from 'rxjs/operators';
|
||||
import {forkJoin, Observable} from 'rxjs';
|
||||
import {NavigationFacade} from '@base/facades/navigation/navigation.facade';
|
||||
import {UserPreferenceFacade} from '@base/facades/user-preference/user-preference.facade';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
|
@ -17,7 +15,9 @@ export class BaseMetadataResolver implements Resolve<any> {
|
|||
|
||||
constructor(private systemConfigFacade: SystemConfigFacade,
|
||||
private languageFacade: LanguageFacade,
|
||||
private navigationFacade: NavigationFacade) {
|
||||
private navigationFacade: NavigationFacade,
|
||||
private userPreferenceFacade: UserPreferenceFacade,
|
||||
private themeImagesFacade: ThemeImagesFacade) {
|
||||
}
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
|
@ -55,8 +55,40 @@ export class BaseMetadataResolver implements Resolve<any> {
|
|||
streams$.configs = configs$;
|
||||
}
|
||||
|
||||
if (this.isToLoadUserPreferences(route)) {
|
||||
|
||||
return forkJoin(streams$);
|
||||
streams$.preferences = this.userPreferenceFacade.load();
|
||||
}
|
||||
|
||||
|
||||
const parallelStream$ = forkJoin(streams$);
|
||||
|
||||
return parallelStream$.pipe(
|
||||
map((data: any) => {
|
||||
|
||||
let theme = null;
|
||||
|
||||
if (this.systemConfigFacade.getConfigValue('default_theme')) {
|
||||
theme = this.systemConfigFacade.getConfigValue('default_theme');
|
||||
}
|
||||
|
||||
if (this.userPreferenceFacade.getUserPreference('user_theme')) {
|
||||
theme = this.userPreferenceFacade.getUserPreference('user_theme');
|
||||
}
|
||||
|
||||
if (this.themeImagesFacade.getTheme()) {
|
||||
theme = this.themeImagesFacade.getTheme();
|
||||
}
|
||||
|
||||
if (theme !== null) {
|
||||
return this.themeImagesFacade.load(theme);
|
||||
}
|
||||
|
||||
return data;
|
||||
}),
|
||||
concatAll(),
|
||||
toArray()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,4 +160,18 @@ export class BaseMetadataResolver implements Resolve<any> {
|
|||
|
||||
return route.data.load.navigation !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should load user preferences. If not set defaults to true
|
||||
*
|
||||
* @param route
|
||||
* @returns boolean
|
||||
*/
|
||||
protected isToLoadUserPreferences(route: ActivatedRouteSnapshot): boolean {
|
||||
if (!route.data || !route.data.load) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return route.data.load.preferences !== false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<!-- Start List View Section -->
|
||||
|
||||
<div class="list-view">
|
||||
<scrm-list-header-ui></scrm-list-header-ui>
|
||||
<scrm-list-container-ui></scrm-list-container-ui>
|
||||
<scrm-list-header-ui></scrm-list-header-ui>
|
||||
<scrm-list-container-ui></scrm-list-container-ui>
|
||||
</div>
|
||||
|
||||
<!-- End List View Section -->
|
|
@ -1,37 +1,52 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import { ListComponent } from './list.component';
|
||||
import {ListComponent} from './list.component';
|
||||
import {ListheaderUiModule} from '@components/list-header/list-header.module';
|
||||
import {ListcontainerUiModule} from '@components/list-container/list-container.module';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {ImageModule} from '@components/image/image.module';
|
||||
import {ApolloTestingModule} from 'apollo-angular/testing';
|
||||
import {ThemeImagesFacade} from '@base/facades/theme-images/theme-images.facade';
|
||||
import {themeImagesMockData} from '@base/facades/theme-images/theme-images.facade.spec.mock';
|
||||
import {take} from 'rxjs/operators';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
describe('ListComponent', () => {
|
||||
let component: ListComponent;
|
||||
let fixture: ComponentFixture<ListComponent>;
|
||||
let component: ListComponent;
|
||||
let fixture: ComponentFixture<ListComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ListheaderUiModule,
|
||||
ListcontainerUiModule,
|
||||
HttpClientTestingModule,
|
||||
RouterTestingModule,
|
||||
BrowserAnimationsModule
|
||||
],
|
||||
declarations: [ ListComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ListheaderUiModule,
|
||||
ListcontainerUiModule,
|
||||
HttpClientTestingModule,
|
||||
RouterTestingModule,
|
||||
BrowserAnimationsModule,
|
||||
ImageModule,
|
||||
ApolloTestingModule
|
||||
],
|
||||
declarations: [ListComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ThemeImagesFacade, useValue: {
|
||||
images$: of(themeImagesMockData).pipe(take(1))
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'scrm-list',
|
||||
templateUrl: './list.component.html',
|
||||
styleUrls: []
|
||||
selector: 'scrm-list',
|
||||
templateUrl: './list.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class ListComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ListComponent } from './list.component';
|
||||
import { ListheaderUiModule } from '../../src/components/list-header/list-header.module';
|
||||
import { ListcontainerUiModule } from '../../src/components/list-container/list-container.module';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ListComponent} from './list.component';
|
||||
import {ListheaderUiModule} from '@components/list-header/list-header.module';
|
||||
import {ListcontainerUiModule} from '@components/list-container/list-container.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [ListComponent],
|
||||
exports: [ListComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ListheaderUiModule,
|
||||
ListcontainerUiModule
|
||||
]
|
||||
declarations: [ListComponent],
|
||||
exports: [ListComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ListheaderUiModule,
|
||||
ListcontainerUiModule
|
||||
]
|
||||
})
|
||||
export class ListModule {}
|
||||
export class ListModule {
|
||||
}
|
||||
|
|
59
core/src/DataProvider/ThemeImagesItemDataProvider.php
Normal file
59
core/src/DataProvider/ThemeImagesItemDataProvider.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\DataProvider;
|
||||
|
||||
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
|
||||
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
|
||||
use App\Entity\ThemeImages;
|
||||
use App\Service\ThemeImageService;
|
||||
|
||||
/**
|
||||
* Class ThemeImagesItemDataProvider
|
||||
*/
|
||||
final class ThemeImagesItemDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var ThemeImageService
|
||||
*/
|
||||
private $themeImageService;
|
||||
|
||||
|
||||
/**
|
||||
* ThemeImagesItemDataProvider constructor.
|
||||
* @param ThemeImageService $themeImageService
|
||||
*/
|
||||
public function __construct(ThemeImageService $themeImageService)
|
||||
{
|
||||
$this->themeImageService = $themeImageService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defined supported resources
|
||||
* @param string $resourceClass
|
||||
* @param string|null $operationName
|
||||
* @param array $context
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
|
||||
{
|
||||
return ThemeImages::class === $resourceClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get theme image information for given theme
|
||||
* @param string $resourceClass
|
||||
* @param array|int|string $id
|
||||
* @param string|null $operationName
|
||||
* @param array $context
|
||||
* @return ThemeImages|null
|
||||
*/
|
||||
public function getItem(
|
||||
string $resourceClass,
|
||||
$id,
|
||||
string $operationName = null,
|
||||
array $context = []
|
||||
): ?ThemeImages {
|
||||
|
||||
return $this->themeImageService->get($id);
|
||||
}
|
||||
}
|
77
core/src/Entity/ThemeImages.php
Normal file
77
core/src/Entity/ThemeImages.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
|
||||
use ApiPlatform\Core\Annotation\ApiProperty;
|
||||
use ApiPlatform\Core\Annotation\ApiResource;
|
||||
|
||||
/**
|
||||
* @ApiResource(
|
||||
* itemOperations={
|
||||
* "get"
|
||||
* },
|
||||
* collectionOperations={
|
||||
* },
|
||||
* graphql={
|
||||
* "item_query",
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class ThemeImages
|
||||
{
|
||||
/**
|
||||
* @ApiProperty(identifier=true)
|
||||
* @var string|null
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @ApiProperty
|
||||
* @var array|null
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* Get Id
|
||||
* @return string|null
|
||||
*/
|
||||
public function getId(): ?string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Id
|
||||
* @param string|null $id
|
||||
* @return ThemeImages
|
||||
*/
|
||||
public function setId(?string $id): ThemeImages
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items
|
||||
* @return array|null
|
||||
*/
|
||||
public function getItems(): ?array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Items
|
||||
* @param array|null $items
|
||||
* @return ThemeImages
|
||||
*/
|
||||
public function setItems(?array $items): ThemeImages
|
||||
{
|
||||
$this->items = $items;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
50
core/src/Service/ThemeImageFinder.php
Normal file
50
core/src/Service/ThemeImageFinder.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
class ThemeImageFinder
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $themeImageSupportedTypes;
|
||||
|
||||
/**
|
||||
* ThemeImageFinder constructor.
|
||||
* @param array $themeImageSupportedTypes
|
||||
*/
|
||||
public function __construct(array $themeImageSupportedTypes)
|
||||
{
|
||||
$this->themeImageSupportedTypes = $themeImageSupportedTypes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of existing images
|
||||
* @param $fullPath
|
||||
* @return SplFileInfo[]
|
||||
*/
|
||||
public function find($fullPath): iterable
|
||||
{
|
||||
if (!is_dir($fullPath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$finder = new Finder();
|
||||
$finder->files();
|
||||
|
||||
foreach ($this->themeImageSupportedTypes as $extension) {
|
||||
$finder->name("*.$extension");
|
||||
}
|
||||
|
||||
$finder->in($fullPath);
|
||||
|
||||
return $finder->getIterator();
|
||||
}
|
||||
}
|
156
core/src/Service/ThemeImageService.php
Normal file
156
core/src/Service/ThemeImageService.php
Normal file
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\ThemeImages;
|
||||
|
||||
class ThemeImageService
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $themeImagePaths;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $themeImageSupportedTypes;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $themeImageTypePriority;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $projectDir;
|
||||
/**
|
||||
* @var ThemeImageFinder
|
||||
*/
|
||||
private $themeImageFinder;
|
||||
|
||||
/**
|
||||
* ThemeImageService constructor.
|
||||
* @param string[] $themeImagePaths
|
||||
* @param array $themeImageSupportedTypes
|
||||
* @param string $projectDir
|
||||
* @param ThemeImageFinder $themeImageFinder
|
||||
*/
|
||||
public function __construct(
|
||||
array $themeImagePaths,
|
||||
array $themeImageSupportedTypes,
|
||||
string $projectDir,
|
||||
ThemeImageFinder $themeImageFinder
|
||||
) {
|
||||
$this->themeImagePaths = $themeImagePaths;
|
||||
$this->themeImageSupportedTypes = $themeImageSupportedTypes;
|
||||
$this->projectDir = $projectDir;
|
||||
$this->themeImageFinder = $themeImageFinder;
|
||||
|
||||
$this->themeImageTypePriority = $this->buildTypePriorityMap($themeImageSupportedTypes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get images for given $theme
|
||||
* @param string $theme
|
||||
* @return ThemeImages
|
||||
*/
|
||||
public function get(string $theme): ThemeImages
|
||||
{
|
||||
$images = new ThemeImages();
|
||||
$images->setId($theme);
|
||||
$items = [];
|
||||
|
||||
foreach ($this->themeImagePaths as $themeImagePath) {
|
||||
$themeImages = $this->getImages($themeImagePath, $theme);
|
||||
$items = array_merge($items, $themeImages);
|
||||
}
|
||||
|
||||
$images->setItems($items);
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get images information for given path and theme
|
||||
* @param string $imagePath
|
||||
* @param string $theme
|
||||
* @return array
|
||||
*/
|
||||
protected function getImages(string $imagePath, string $theme): array
|
||||
{
|
||||
|
||||
$path = $this->buildPath($imagePath, $theme);
|
||||
$fullPath = $this->projectDir . '/' . $path;
|
||||
|
||||
$it = $this->themeImageFinder->find($fullPath);
|
||||
|
||||
$images = [];
|
||||
if (empty($it)) {
|
||||
return $images;
|
||||
}
|
||||
|
||||
foreach ($it as $file) {
|
||||
$name = $file->getFilenameWithoutExtension();
|
||||
$filePath = "$path/{$file->getFilename()}";
|
||||
$extension = $file->getExtension();
|
||||
|
||||
if (!$this->hasPriority($images, $name, $extension)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$images[$name] = [
|
||||
'path' => $filePath,
|
||||
'name' => $name,
|
||||
'type' => $extension
|
||||
];
|
||||
}
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build path
|
||||
* @param string $imagePath
|
||||
* @param string $theme
|
||||
* @return string
|
||||
*/
|
||||
protected function buildPath(string $imagePath, string $theme): string
|
||||
{
|
||||
return str_replace('<theme>', $theme, $imagePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the priority map
|
||||
* @param array $types
|
||||
* @return array
|
||||
*/
|
||||
protected function buildTypePriorityMap(array $types): array
|
||||
{
|
||||
return array_flip($types);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if current image has priority over already store image
|
||||
* @param array $images
|
||||
* @param string $name
|
||||
* @param string $currentType
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasPriority(array $images, string $name, string $currentType): bool
|
||||
{
|
||||
if (empty($images[$name])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$existingType = $images[$name]['type'];
|
||||
|
||||
$existingPriority = $this->themeImageTypePriority[$existingType];
|
||||
$currentPriority = $this->themeImageTypePriority[$currentType];
|
||||
|
||||
return $existingPriority > $currentPriority;
|
||||
}
|
||||
}
|
140
tests/unit/service/ThemeImageServiceTest.php
Normal file
140
tests/unit/service/ThemeImageServiceTest.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php namespace App\Tests;
|
||||
|
||||
use App\Service\ThemeImageFinder;
|
||||
use App\Service\ThemeImageService;
|
||||
use Codeception\Test\Unit;
|
||||
use Exception;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
class ThemeImageServiceTest extends Unit
|
||||
{
|
||||
/**
|
||||
* @var UnitTester
|
||||
*/
|
||||
protected $tester;
|
||||
|
||||
/**
|
||||
* @var ThemeImageService
|
||||
*/
|
||||
protected $themeImageService;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function _before()
|
||||
{
|
||||
$themeImagePaths = [
|
||||
'legacy/themes/default/images',
|
||||
'legacy/custom/themes/default/images',
|
||||
'core/app/themes/default/images',
|
||||
'legacy/themes/<theme>/images',
|
||||
'legacy/custom/themes/<theme>/images',
|
||||
'core/app/themes/<theme>/images',
|
||||
];
|
||||
$themeImageSupportedTypes = [
|
||||
'svg',
|
||||
'png',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'gif',
|
||||
];
|
||||
|
||||
$mockImages = [
|
||||
'/legacy/themes/default/images' => [
|
||||
new SplFileInfo('logo.png', 'legacy/themes/default/images', 'legacy/themes/default/images'),
|
||||
new SplFileInfo('legacy_image.png', 'legacy/themes/default/images', 'legacy/themes/default/images'),
|
||||
new SplFileInfo('to_be_overwritten.png', 'legacy/themes/default/images',
|
||||
'legacy/themes/default/images'),
|
||||
new SplFileInfo('to_be_overwritten_with_different_extension.png', 'legacy/themes/default/images',
|
||||
'legacy/themes/default/images')
|
||||
],
|
||||
'/legacy/themes/suite8/images' => [
|
||||
new SplFileInfo('to_be_overwritten.png', 'legacy/themes/suite8/images', 'legacy/themes/suite8/images'),
|
||||
new SplFileInfo('to_be_overwritten_with_different_extension.svg', 'legacy/themes/suite8/images',
|
||||
'legacy/themes/suite8/images')
|
||||
],
|
||||
'/core/app/themes/suite8/images' => [
|
||||
new SplFileInfo('logo.png', 'core/app/themes/suite8/images', 'core/app/themes/suite8/images'),
|
||||
]
|
||||
];
|
||||
|
||||
/** @var ThemeImageFinder $themeImageFinder */
|
||||
$themeImageFinder = $this->make(
|
||||
ThemeImageFinder::class,
|
||||
[
|
||||
'find' => static function ($fullPath) use ($mockImages) {
|
||||
if (empty($mockImages[$fullPath])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $mockImages[$fullPath];
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
$this->themeImageService = new ThemeImageService(
|
||||
$themeImagePaths,
|
||||
$themeImageSupportedTypes,
|
||||
'',
|
||||
$themeImageFinder
|
||||
);
|
||||
}
|
||||
|
||||
protected function _after()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the format of the returned items is the expected
|
||||
*/
|
||||
public function testItemFormat(): void
|
||||
{
|
||||
$images = $this->themeImageService->get('suite8');
|
||||
static::assertNotNull($images);
|
||||
static::assertNotEmpty($images->getItems());
|
||||
static::assertArrayHasKey('logo', $images->getItems());
|
||||
$item = $images->getItems()['logo'];
|
||||
static::assertNotEmpty($item);
|
||||
|
||||
static::assertCount(3, $item);
|
||||
static::assertArrayHasKey('path', $item);
|
||||
static::assertArrayHasKey('name', $item);
|
||||
static::assertArrayHasKey('type', $item);
|
||||
static::assertEquals('logo', $item['name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test image override order
|
||||
*/
|
||||
public function testImageOverrides(): void
|
||||
{
|
||||
$expected = [
|
||||
'logo' => [
|
||||
'path' => 'core/app/themes/suite8/images/logo.png',
|
||||
'name' => 'logo',
|
||||
'type' => 'png'
|
||||
],
|
||||
'legacy_image' => [
|
||||
'path' => 'legacy/themes/default/images/legacy_image.png',
|
||||
'name' => 'legacy_image',
|
||||
'type' => 'png'
|
||||
],
|
||||
'to_be_overwritten_with_different_extension' => [
|
||||
'path' => 'legacy/themes/suite8/images/to_be_overwritten_with_different_extension.svg',
|
||||
'name' => 'to_be_overwritten_with_different_extension',
|
||||
'type' => 'svg'
|
||||
],
|
||||
'to_be_overwritten' => [
|
||||
'path' => 'legacy/themes/suite8/images/to_be_overwritten.png',
|
||||
'name' => 'to_be_overwritten',
|
||||
'type' => 'png'
|
||||
],
|
||||
];
|
||||
|
||||
$images = $this->themeImageService->get('suite8');
|
||||
static::assertNotNull($images);
|
||||
static::assertNotEmpty($images->getItems());
|
||||
|
||||
static::assertEquals($images->getItems(), $expected);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue