Commit cc83b456 authored by Hamish Hossack's avatar Hamish Hossack

Add seed data files for directory

parents
// Core modules for the application
import { NgModule } from '@angular/core';
import { Http, RequestOptions } from '@angular/http';
import { BrowserModule } from '@angular/platform-browser';
// Libraries
import { AuthHttp, AuthConfig } from 'angular2-jwt';
import { AngularFireModule, FirebaseAppConfig } from 'angularfire2';
// Modules
import { appRouting, appRoutingProviders } from './app.routing';
import { AppCommonModule } from './commons/modules/common.module';
import { HeaderModule } from './components/header/header.module';
// Providers
import { AdminGuard } from './commons/guards/admin.guard';
import { AclGuard } from './commons/guards/acl.guard';
import { FlashMessageEvent } from './commons/services/flashmessage/flashmessage.event';
// Services
import { FlashMessageService } from './commons/services/flashmessage/flashmessage.service';
import { TitleService } from './commons/services/title/title.service';
import { AclService } from './commons/services/acl/acl.service';
import { FirebaseService } from './commons/services/firebase/firebase.service';
import { OperatorGuard } from './commons/guards/operator.guard';
import { IntercomService } from './commons/services/plugins/intercom.service';
import { HotjarService } from './commons/services/plugins/hotjar.service';
// Components
import { AppComponent } from './app.component';
import { NoContentComponent } from './modules/no-content/no-content.component';
import { FooterComponent } from './components/footer/footer.component';
import { MainAppComponent } from './main.app.component';
export function authHttpServiceFactory(http: Http, options: RequestOptions) {
return new AuthHttp(new AuthConfig({
tokenName: 'token',
tokenGetter: () => localStorage.getItem('token') ? localStorage.getItem('token').split(' ')[1] : null,
globalHeaders: [ { 'Content-Type': 'application/json' } ],
noJwtError: true
}), http, options);
}
@NgModule({
imports: [
BrowserModule,
appRouting,
HeaderModule,
AngularFireModule.initializeApp(<FirebaseAppConfig>FIREBASE),
AppCommonModule.forRoot(),
],
declarations: [ AppComponent, FooterComponent, NoContentComponent, MainAppComponent ],
providers: [
AdminGuard,
OperatorGuard,
AclService,
AclGuard,
FlashMessageService,
FlashMessageEvent,
appRoutingProviders,
TitleService,
FirebaseService,
IntercomService,
HotjarService,
{
provide: AuthHttp,
useFactory: authHttpServiceFactory,
deps: [ Http, RequestOptions ]
}
],
bootstrap: [ AppComponent ]
})
export class AppModule {
}
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';
// Library Modules
// Services
import { UserService } from '../services/user/user.service';
import { LocalStorageService } from '../services/local-storage/localStorage.service';
import { Utils } from '../utils/common.util';
// Components
import { SpinnerComponent } from '../../elements/spinner/spinner.component';
import { SubmitButtonComponent } from '../../elements/submit-button/submit-button.component';
// Misc
import { AclDirective, OwnerDirective } from '../directives/acl.directive';
import { UserRelationService } from '../services/user/user.relation';
import { FormattedDatePipeModule } from '../pipes/date/date.pipe.module';
import { FindNamePipeModule } from '../pipes/findName/findName.pipe.module';
import { LoadingBarModule } from '../../elements/loading-bar/loading-bar.module';
/**
* common.module
*/
@NgModule({
imports: [
CommonModule,
RouterModule,
ReactiveFormsModule,
],
declarations: [
SpinnerComponent,
SubmitButtonComponent,
AclDirective,
OwnerDirective,
],
providers: [
Utils
],
exports: [
CommonModule,
FormsModule,
RouterModule,
HttpModule,
SpinnerComponent,
SubmitButtonComponent,
ReactiveFormsModule,
AclDirective,
OwnerDirective,
FormattedDatePipeModule,
FindNamePipeModule,
LoadingBarModule,
]
})
export class AppCommonModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: AppCommonModule,
providers: [ UserService, UserRelationService, LocalStorageService ],
};
}
}
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/Rx';
import { SearchService } from '../../../commons/services/search/search.service';
import { Group } from '../../../commons/interfaces/group/group.interface';
import { Company } from '../../../commons/interfaces/company/company.interface';
import { User } from '../../../commons/interfaces/user/user.interface';
export abstract class AbstractDirectory {
protected searchService: SearchService;
protected currentSearch: string = '';
protected queryInProgress = false;
protected offset = 0;
protected limit = 20;
query = {};
protected route;
activeItem: boolean | User | Company | Group = false;
searchResults = [];
protected hasNext = true;
constructor(searchService: SearchService) {
}
autoSelectItem() {
this.route.params.subscribe((params: any) => {
this.currentSearch = params[ 'search' ] || '';
this.findSelectedItemById(params[ 'id' ]);
});
}
hasMore() {
return this.hasNext || this.offset === 0;
}
load() {
}
loadPagination() {
if (this.queryInProgress) return;
this.load();
}
newSearch(search) {
if (search === this.currentSearch) return;
this.currentSearch = search;
this.resetSearch();
this.load();
}
mapResults(results) {
this.queryInProgress = false;
this.searchResults = this.searchResults.concat(...results);
this.offset += results.length;
this.hasNext = results.length === this.limit;
return Observable.of(true);
}
resetSearch() {
this.searchResults = [];
this.activeItem = false;
this.hasNext = true;
this.offset = 0;
}
newSelectedItem(item) {
this.activeItem = item;
}
findSelectedItemById(id) {
this.activeItem = this.searchResults.filter((result: User | Company | Group) => result.id === id)[0];
}
}
import { Component, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { AbstractDirectory } from '../abstract-directory';
import { AclService } from '../../../../commons/services/acl/acl.service';
import { SubMenuService } from '../../../../components/sub-menu/sub-menu.service';
import { SearchService } from '../../../../commons/services/search/search.service';
import { AuthenticationService } from '../../../../commons/services/authentication/authentication.service';
import { Filter } from '../../../../commons/interfaces/filters/filters.interface';
@Component({
selector: 'company-directory',
templateUrl: './company-directory.template.html'
})
export class CompanyDirectoryComponent extends AbstractDirectory implements OnDestroy {
filterName: string = 'companies';
searchFilters: Array<Filter>;
loadingMoreItems: boolean = false;
list = [
{ name: 'sites' }
// { name: 'services', isProperty: true } // TODO (gabs) : Remove attributes relating to the new data types
];
constructor(protected searchService: SearchService,
private router: Router,
protected route: ActivatedRoute,
private subMenuService: SubMenuService,
private authenticationService: AuthenticationService,
aclService: AclService) {
super(searchService);
route.data.subscribe((data: any) => {
this.searchResults = data.searchFilters.companies;
this.searchFilters = data.searchFilters.filter;
this.offset = this.searchFilters.length;
this.hasNext = this.offset === this.limit;
});
if (aclService.isOperator()) this.buildSubMenu();
this.autoSelectItem();
}
ngOnDestroy() {
this.subMenuService.initialMenuState();
}
updateQuery(query) {
this.query = query;
this.resetSearch();
this.load();
}
load() {
if (!this.hasMore()) return;
this.loadingMoreItems = true;
this.queryInProgress = true;
const currentSearch = JSON.parse(JSON.stringify(this.currentSearch));
const currentQuery = JSON.stringify(this.query);
this.searchService.searchFiltersCompanies(this.query, { limit: this.limit, offset: this.offset, query: this.currentSearch })
.filter(() => currentSearch === this.currentSearch && currentQuery === JSON.stringify(this.query))
.switchMap(results => this.mapResults(results.companies))
.subscribe(() => this.loadingMoreItems = false);
}
buildSubMenu() {
const actions = [
{ id: 1, action: this.onClickCreate.bind(this), title: 'Create a company' },
];
this.subMenuService.replaceActionItem(actions);
}
onClickCreate() {
this.router.navigate([ '/company', 'create' ]);
}
}
import { NgModule } from '@angular/core';
// Routing
import { companyDirectoryRouting } from './company-directory.routing';
// Libraries
import { InfiniteScrollModule } from 'angular2-infinite-scroll/angular2-infinite-scroll';
// Modules
import { AppCommonModule } from '../../../../commons/modules/common.module';
import { FiltersModule } from '../filters/filters.module';
import { ResultListModule } from '../result-list/result-list-module';
import { ProfilePicModule } from '../../../../elements/profile-pic/profile-pic.module';
import { CoverPicModule } from '../../../../components/cover-pic/cover-pic.module';
// Providers
import { SearchCompanyResolver, SearchFiltersCompanyResolver } from '../../../../commons/services/search/search.resolver';
// Components
import { CompanyDirectoryComponent } from './company-directory.component';
@NgModule({
imports: [
companyDirectoryRouting,
AppCommonModule,
FiltersModule,
ResultListModule,
ProfilePicModule,
CoverPicModule
],
declarations: [
CompanyDirectoryComponent
],
providers: [
SearchCompanyResolver,
SearchFiltersCompanyResolver
]
})
export class CompanyDirectoryModule {}
import { RouterModule, Routes } from '@angular/router';
import { CompanyDirectoryComponent } from './company-directory.component';
import { SearchCompanyResolver, SearchFiltersCompanyResolver } from '../../../../commons/services/search/search.resolver';
export const companyDirectoryRoutes: Routes = [
{
path: '', component: CompanyDirectoryComponent,
data: {
title: 'Directory Company'
},
resolve: {
searchFilters: SearchFiltersCompanyResolver
}
}
];
export const companyDirectoryRouting = RouterModule.forChild(companyDirectoryRoutes);
<div class="content directory">
<div class="directory-filters">
<filters [filters]="searchFilters" (updateQuery)="updateQuery($event)" [list]="list" type="companies"></filters>
</div>
<div class="directory-list">
<result-list [(searchResults)]="searchResults"
[filterName]="filterName"
[queryInProgress]="queryInProgress"
(activeItem)="activeItem"
(newSearch)="newSearch($event)"
(loadPage)="loadPagination($event)"
(newSelectedItem)="newSelectedItem($event)">
</result-list>
</div>
<div class="directory-display">
<div class="panel">
<div class="display-holder u-textCenter u-spacerTop" *ngIf="!activeItem">
<div class="display-holder_content">
<img class="u-spacerBottom" src="/assets/images/directory/holder.png" alt="">
<p class="t-bold">Select a company to view more detail about it</p>
</div>
</div>
<div class="display-content" *ngIf="activeItem">
<div class="cover u-posRelative directory-profile u-spacerBottom">
<cover-pic size="auto" [img]="activeItem.coverImageUrl"></cover-pic>
<div class="directory-details u-textCenter u-posRelative">
<div class="profile-pic">
<profile-pic [img]="activeItem.profileImageUrl" [size]="'medium'"></profile-pic>
</div>
<h2 class="t-white">{{activeItem.name}}</h2>
<p class="t-white t-large u-noMargin">{{activeItem.businessArea}}</p>
<p class="t-white t-light u-noMargin">{{activeItem.company?.name}}</p>
</div>
</div>
<div class="directory-info u-spacerBottom u-spacerTop">
<div class="directory-address u-spacerTop b-color" *ngIf="activeItem.address">
<p>
{{activeItem.address.line1}}<br>
<span *ngIf="activeItem.address.line2">{{activeItem.address.line2}}<br></span>
{{activeItem.address.city}}<br>
{{activeItem.address.postalCode}}<br>
{{activeItem.address.country}}
</p>
</div>
<div class="directory-other u-spacerTop">
<ul *ngIf="activeItem.website || activeItem.phoneNumber" class="u-spacerTop">
<li *ngIf="activeItem.phoneNumber" class="phone u-posRelative">
<i class="icon o-call-solid t-primary"></i>
<a href="tel:{{activeItem.phoneNumber}}" class="u-posRelative">{{activeItem.phoneNumber}}</a>
</li>
<li *ngIf="activeItem.website" class="t-bold">
<a href="{{activeItem.website}}" target="_blank">Visit Website</a>
</li>
</ul>
</div>
</div>
<div class="directory-intro clearfix">
<h3 class="t-light">Introduction</h3>
<h4 class="t-underline b-color">About {{activeItem.name}}</h4>
<p>{{activeItem.introduction}}</p>
</div>
<div class="directory-options clearfix u-spacerBottom">
<a [routerLink]="['/company', activeItem.id]">
<div class="profile-pic u-inlineBlock">
<profile-pic [img]="activeItem.profileImageUrl"></profile-pic>
</div>
View Company Page
</a>
</div>
</div>
</div>
</div>
</div>
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { FiltersEventAdd } from '../filters.event';
import { SearchService } from '../../../../../commons/services/search/search.service';
@Component({
selector: 'filter-group',
templateUrl: 'filter-group.template.html'
})
export class FilterGroupComponent {
@Input()
get items() {
return this._items;
}
set items(items) {
this._items = items.map((item) => {
this.filtered.map((filter) => {
if (filter.name === item.name) {
item.checked = true;
}
});
return item;
});
}
@Input() isCollapsed = false;
@Input() remove;
@Input() query;
@Input() name: string;
@Input() type: string;
@Input() isProperty = false;
@Output() applyFilter = new EventEmitter();
hasMore: boolean = false;
limit = 10;
offset = 0;
filtered = [];
private _items;
constructor(private searchService: SearchService) {
}
ngOnInit() {
this.offset = this.items.length;
this.hasMore = this.items.length === this.limit;
}
isHidden(filter) {
return this.query && this.query.filter((f) => f.name === filter.name).length === 1;
}
filterAction(filter) {
const event = new FiltersEventAdd();
let action;
if ((this.isProperty && this.remove) || (!this.isProperty && filter.checked)) {
action = 'remove';
this.filtered = this.filtered.filter((filteredFilter) => {
return filter.name !== filteredFilter.name;
});
} else {
this.filtered.push(filter);
event.name = this.name;
action = 'add';
}
filter.checked = !filter.checked;
filter.type = filter.type || this.type;
event.action = action;
event.item = filter;
event.isProperty = this.isProperty;
event.name = this.name || filter.name;
window.scrollTo(0, 0);
this.applyFilter.emit(event);
}
loadMore() {
let loadMethod;
if (this.isProperty) {
loadMethod = this.searchService.moreProperties(this.type, this.name, {
limit: this.limit,
offset: this.offset
});
} else {
loadMethod = this.searchService.moreFilters(this.type, this.name, {
limit: this.limit,
offset: this.offset
});
}
loadMethod
.subscribe((filters) => {
this.items.push(...filters);
this.hasMore = filters.length === this.limit;
this.offset += this.limit;
});
}
isFilterCollapsed() {
return this.isCollapsed;
}
toggleFilterCollapsed() {
this.isCollapsed = !this.isCollapsed;
}
}
<div class="filter u-spacerBottom" *ngIf="items.length">
<div [ngClass]="isFilterCollapsed() ? 'u-plusSymbol t-primary' : 'u-minusSymbol'" class="u-textRight u-floatRight t-large u-cursorPointer t-bold" (click)="toggleFilterCollapsed()"></div>
<h3 class="u-textCapitalize b-color">{{name}}</h3>
<ul *ngIf="!isFilterCollapsed()">
<li *ngFor="let filter of items"(click)="filterAction(filter)" class="u-cursorPointer">
<div *ngIf="!isHidden(filter)">
<span class="filter-name t-black u-textTruncate">
<i class="icon t-primary" [class.o-checkbox-unchecked]="!filter.checked" [class.o-checkbox-checked]="filter.checked" *ngIf="!isProperty"></i>
<i class="icon o-close" *ngIf="remove"></i>
{{filter.name}}
</span>
<span class="filter-count t-light u-block u-textRight u-floatRight">{{filter.count}}</span>
</div>
</li>
</ul>
<div class="u-textCenter u-cursorPointer t-bold t-primary" *ngIf="hasMore" (click)="loadMore()">More</div>
</div>
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { FiltersEventAdd } from './filters.event';
@Component({
selector: 'filters',
templateUrl: 'filters.template.html'
})
export class FiltersComponent {
@Input() filters;
@Input() list = [];
@Input() type: string;
@Output('updateQuery') updateQuery = new EventEmitter();
query = { properties: {} };
moreVisible: boolean = false;
applyFilter(filter: FiltersEventAdd) {
switch (filter.action) {
case 'add':
this.addFilter(filter);
break;
case 'remove':
this.removeFilter(filter);
break;
default:
break;
}
this.updateQuery.emit(this.query);
}
addFilter(filter) {
if (filter.isProperty && filter.name) {
const name = filter.item.name;
const type = filter.name;
this.query.properties[ type ] = this.query.properties[ type ] || [];
this.query.properties[ type ].push({ name, type, checked: true });
} else {
const id = filter.item.id;
const type = filter.name;
this.query[ type ] = this.query[ type ] || [];
this.query[ type ].push({ id });
}
}
getProperties() {
let properties = [];
Object.keys(this.query.properties).forEach((property) => {
Object.keys(this.query.properties[ property ]).forEach((p) => {
if (!properties.includes(this.query.properties[ property ][ p ])) {
properties.push(this.query.properties[ property ][ p ]);
}
});
});
return properties;
}
removeFilter(filter) {
if (filter.isProperty) {
const name = filter.item.name;
const type = filter.item.type;
this.query.properties[ type ] = this.query.properties[ type ].filter((propertyFilter) => propertyFilter.name !== name);
} else {
const id = filter.item.id;
const type = filter.name;
this.query[ type ] = this.query[ type ].filter((propertyFilter) => propertyFilter.id !== id);
}
}
onToggleMore() {
this.moreVisible = !this.moreVisible;
}
}
import { Injectable } from '@angular/core';
/**
* FlashmessageEvent
*/
@Injectable()
export class FiltersEventAdd {
public item;
public action;
public name;
public isProperty;
}
import { NgModule } from '@angular/core';
// Modules
import { AppCommonModule } from '../../../../commons/modules/common.module';
import { KeysPipeModule } from '../../../../commons/pipes/keys/keys.pipe.module';
// Components
import { FiltersComponent } from './filters.component';
import { FilterGroupComponent } from './filter-group/filter-group.component';
import { FiltersEventAdd } from './filters.event';
@NgModule({
imports: [
AppCommonModule,
KeysPipeModule
],
declarations: [
FiltersComponent,
FilterGroupComponent