@ -0,0 +1,223 @@ | |||
## Ignore Visual Studio temporary files, build results, and | |||
## files generated by popular Visual Studio add-ons. | |||
coverage/**/** | |||
client/**/*.js | |||
/doc | |||
client/**/*.js.map | |||
npm-debug.log* | |||
# User-specific files | |||
*.suo | |||
*.user | |||
*.userosscache | |||
*.sln.docstates | |||
# User-specific files (MonoDevelop/Xamarin Studio) | |||
*.userprefs | |||
# Build results | |||
[Dd]ebug/ | |||
[Dd]ebugPublic/ | |||
[Rr]elease/ | |||
[Rr]eleases/ | |||
x64/ | |||
x86/ | |||
bld/ | |||
[Bb]in/ | |||
[Oo]bj/ | |||
# Visual Studio 2015 cache/options directory | |||
.vs/ | |||
# MSTest test Results | |||
[Tt]est[Rr]esult*/ | |||
[Bb]uild[Ll]og.* | |||
# NUNIT | |||
*.VisualState.xml | |||
TestResult.xml | |||
# Build Results of an ATL Project | |||
[Dd]ebugPS/ | |||
[Rr]eleasePS/ | |||
dlldata.c | |||
# DNX | |||
project.lock.json | |||
artifacts/ | |||
*_i.c | |||
*_p.c | |||
*_i.h | |||
*.ilk | |||
*.meta | |||
*.obj | |||
*.pch | |||
*.pdb | |||
*.pgc | |||
*.pgd | |||
*.rsp | |||
*.sbr | |||
*.tlb | |||
*.tli | |||
*.tlh | |||
*.tmp | |||
*.tmp_proj | |||
*.log | |||
*.vspscc | |||
*.vssscc | |||
.builds | |||
*.pidb | |||
*.svclog | |||
*.scc | |||
# Chutzpah Test files | |||
_Chutzpah* | |||
# Visual C++ cache files | |||
ipch/ | |||
*.aps | |||
*.ncb | |||
*.opensdf | |||
*.sdf | |||
*.cachefile | |||
# Visual Studio profiler | |||
*.psess | |||
*.vsp | |||
*.vspx | |||
# TFS 2012 Local Workspace | |||
$tf/ | |||
# Guidance Automation Toolkit | |||
*.gpState | |||
# ReSharper is a .NET coding add-in | |||
_ReSharper*/ | |||
*.[Rr]e[Ss]harper | |||
*.DotSettings.user | |||
# JustCode is a .NET coding add-in | |||
.JustCode | |||
# TeamCity is a build add-in | |||
_TeamCity* | |||
# DotCover is a Code Coverage Tool | |||
*.dotCover | |||
# NCrunch | |||
_NCrunch_* | |||
.*crunch*.local.xml | |||
# MightyMoose | |||
*.mm.* | |||
AutoTest.Net/ | |||
# Web workbench (sass) | |||
.sass-cache/ | |||
# Installshield output folder | |||
[Ee]xpress/ | |||
# DocProject is a documentation generator add-in | |||
DocProject/buildhelp/ | |||
DocProject/Help/*.HxT | |||
DocProject/Help/*.HxC | |||
DocProject/Help/*.hhc | |||
DocProject/Help/*.hhk | |||
DocProject/Help/*.hhp | |||
DocProject/Help/Html2 | |||
DocProject/Help/html | |||
# Click-Once directory | |||
publish/ | |||
# Publish Web Output | |||
*.[Pp]ublish.xml | |||
*.azurePubxml | |||
## TODO: Comment the next line if you want to checkin your | |||
## web deploy settings but do note that will include unencrypted | |||
## passwords | |||
#*.pubxml | |||
*.publishproj | |||
# NuGet Packages | |||
*.nupkg | |||
# The packages folder can be ignored because of Package Restore | |||
**/packages/* | |||
# except build/, which is used as an MSBuild target. | |||
!**/packages/build/ | |||
# Uncomment if necessary however generally it will be regenerated when needed | |||
#!**/packages/repositories.config | |||
# Windows Azure Build Output | |||
csx/ | |||
*.build.csdef | |||
# Windows Store app package directory | |||
AppPackages/ | |||
# Visual Studio cache files | |||
# files ending in .cache can be ignored | |||
*.[Cc]ache | |||
# but keep track of directories ending in .cache | |||
!*.[Cc]ache/ | |||
# Others | |||
ClientBin/ | |||
[Ss]tyle[Cc]op.* | |||
~$* | |||
*~ | |||
*.dbmdl | |||
*.dbproj.schemaview | |||
*.pfx | |||
*.publishsettings | |||
node_modules/ | |||
bower_components/ | |||
**/wwwroot/tmp/ | |||
**/wwwroot/*.bundle.map | |||
**/wwwroot/*.js | |||
/wwwroot/dist/ | |||
orleans.codegen.cs | |||
# RIA/Silverlight projects | |||
Generated_Code/ | |||
# Backup & report files from converting an old project file | |||
# to a newer Visual Studio version. Backup files are not needed, | |||
# because we have git ;-) | |||
_UpgradeReport_Files/ | |||
Backup*/ | |||
UpgradeLog*.XML | |||
UpgradeLog*.htm | |||
# SQL Server files | |||
*.mdf | |||
*.ldf | |||
# Business Intelligence projects | |||
*.rdl.data | |||
*.bim.layout | |||
*.bim_*.settings | |||
# Microsoft Fakes | |||
FakesAssemblies/ | |||
# Node.js Tools for Visual Studio | |||
.ntvs_analysis.dat | |||
# Visual Studio 6 build log | |||
*.plg | |||
# Visual Studio 6 workspace options file | |||
*.opt | |||
# LightSwitch generated files | |||
GeneratedArtifacts/ | |||
_Pvt_Extensions/ | |||
ModelManifest.xml |
@ -0,0 +1,237 @@ | |||
/Properties/launchSettings.json | |||
## Ignore Visual Studio temporary files, build results, and | |||
## files generated by popular Visual Studio add-ons. | |||
# User-specific files | |||
*.suo | |||
*.user | |||
*.userosscache | |||
*.sln.docstates | |||
# User-specific files (MonoDevelop/Xamarin Studio) | |||
*.userprefs | |||
# Build results | |||
[Dd]ebug/ | |||
[Dd]ebugPublic/ | |||
[Rr]elease/ | |||
[Rr]eleases/ | |||
x64/ | |||
x86/ | |||
build/ | |||
bld/ | |||
bin/ | |||
Bin/ | |||
obj/ | |||
Obj/ | |||
# Visual Studio 2015 cache/options directory | |||
.vs/ | |||
/wwwroot/dist/ | |||
# MSTest test Results | |||
[Tt]est[Rr]esult*/ | |||
[Bb]uild[Ll]og.* | |||
# NUNIT | |||
*.VisualState.xml | |||
TestResult.xml | |||
# Build Results of an ATL Project | |||
[Dd]ebugPS/ | |||
[Rr]eleasePS/ | |||
dlldata.c | |||
# DNX | |||
project.lock.json | |||
artifacts/ | |||
*_i.c | |||
*_p.c | |||
*_i.h | |||
*.ilk | |||
*.meta | |||
*.obj | |||
*.pch | |||
*.pdb | |||
*.pgc | |||
*.pgd | |||
*.rsp | |||
*.sbr | |||
*.tlb | |||
*.tli | |||
*.tlh | |||
*.tmp | |||
*.tmp_proj | |||
*.log | |||
*.vspscc | |||
*.vssscc | |||
.builds | |||
*.pidb | |||
*.svclog | |||
*.scc | |||
# Chutzpah Test files | |||
_Chutzpah* | |||
# Visual C++ cache files | |||
ipch/ | |||
*.aps | |||
*.ncb | |||
*.opendb | |||
*.opensdf | |||
*.sdf | |||
*.cachefile | |||
# Visual Studio profiler | |||
*.psess | |||
*.vsp | |||
*.vspx | |||
*.sap | |||
# TFS 2012 Local Workspace | |||
$tf/ | |||
# Guidance Automation Toolkit | |||
*.gpState | |||
# ReSharper is a .NET coding add-in | |||
_ReSharper*/ | |||
*.[Rr]e[Ss]harper | |||
*.DotSettings.user | |||
# JustCode is a .NET coding add-in | |||
.JustCode | |||
# TeamCity is a build add-in | |||
_TeamCity* | |||
# DotCover is a Code Coverage Tool | |||
*.dotCover | |||
# NCrunch | |||
_NCrunch_* | |||
.*crunch*.local.xml | |||
nCrunchTemp_* | |||
# MightyMoose | |||
*.mm.* | |||
AutoTest.Net/ | |||
# Web workbench (sass) | |||
.sass-cache/ | |||
# Installshield output folder | |||
[Ee]xpress/ | |||
# DocProject is a documentation generator add-in | |||
DocProject/buildhelp/ | |||
DocProject/Help/*.HxT | |||
DocProject/Help/*.HxC | |||
DocProject/Help/*.hhc | |||
DocProject/Help/*.hhk | |||
DocProject/Help/*.hhp | |||
DocProject/Help/Html2 | |||
DocProject/Help/html | |||
# Click-Once directory | |||
publish/ | |||
# Publish Web Output | |||
*.[Pp]ublish.xml | |||
*.azurePubxml | |||
# TODO: Comment the next line if you want to checkin your web deploy settings | |||
# but database connection strings (with potential passwords) will be unencrypted | |||
*.pubxml | |||
*.publishproj | |||
# NuGet Packages | |||
*.nupkg | |||
# The packages folder can be ignored because of Package Restore | |||
**/packages/* | |||
# except build/, which is used as an MSBuild target. | |||
!**/packages/build/ | |||
# Uncomment if necessary however generally it will be regenerated when needed | |||
#!**/packages/repositories.config | |||
# Microsoft Azure Build Output | |||
csx/ | |||
*.build.csdef | |||
# Microsoft Azure Emulator | |||
ecf/ | |||
rcf/ | |||
# Microsoft Azure ApplicationInsights config file | |||
ApplicationInsights.config | |||
# Windows Store app package directory | |||
AppPackages/ | |||
BundleArtifacts/ | |||
# Visual Studio cache files | |||
# files ending in .cache can be ignored | |||
*.[Cc]ache | |||
# but keep track of directories ending in .cache | |||
!*.[Cc]ache/ | |||
# Others | |||
ClientBin/ | |||
~$* | |||
*~ | |||
*.dbmdl | |||
*.dbproj.schemaview | |||
*.pfx | |||
*.publishsettings | |||
node_modules/ | |||
orleans.codegen.cs | |||
# RIA/Silverlight projects | |||
Generated_Code/ | |||
# Backup & report files from converting an old project file | |||
# to a newer Visual Studio version. Backup files are not needed, | |||
# because we have git ;-) | |||
_UpgradeReport_Files/ | |||
Backup*/ | |||
UpgradeLog*.XML | |||
UpgradeLog*.htm | |||
# SQL Server files | |||
*.mdf | |||
*.ldf | |||
# Business Intelligence projects | |||
*.rdl.data | |||
*.bim.layout | |||
*.bim_*.settings | |||
# Microsoft Fakes | |||
FakesAssemblies/ | |||
# GhostDoc plugin setting file | |||
*.GhostDoc.xml | |||
# Node.js Tools for Visual Studio | |||
.ntvs_analysis.dat | |||
# Visual Studio 6 build log | |||
*.plg | |||
# Visual Studio 6 workspace options file | |||
*.opt | |||
# Visual Studio LightSwitch build output | |||
**/*.HTMLClient/GeneratedArtifacts | |||
**/*.DesktopClient/GeneratedArtifacts | |||
**/*.DesktopClient/ModelManifest.xml | |||
**/*.Server/GeneratedArtifacts | |||
**/*.Server/ModelManifest.xml | |||
_Pvt_Extensions | |||
# Paket dependency manager | |||
.paket/paket.exe | |||
# FAKE - F# Make | |||
.fake/ |
@ -0,0 +1,2 @@ | |||
// Extra variables that live on Global that will be replaced by webpack DefinePlugin | |||
// declare var process: any; |
@ -0,0 +1,23 @@ | |||
import './polyfills'; | |||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; | |||
import { enableProdMode } from '@angular/core'; | |||
import { AppModule } from './modules/app.module'; | |||
if (process.env.ENV === 'Development') { | |||
// Development | |||
} else { | |||
// Production | |||
enableProdMode(); | |||
} | |||
platformBrowserDynamic().bootstrapModule(AppModule); | |||
// Basic hot reloading support. Automatically reloads and restarts the Angular 2 app each time | |||
// you modify source files. This will not preserve any application state other than the URL. | |||
declare var module: any; | |||
if (module.hot) { | |||
module.hot.accept(); | |||
} |
@ -0,0 +1,9 @@ | |||
$primary-colour: #00A69C; | |||
$primary-accent: #83D01B; | |||
$white-colour: #FFFFFF; | |||
$grey-colour: #E2E2E2; | |||
$text-colour: #757575; | |||
$grey-box-shadow: 10px 10px 20px #F2F2F2; | |||
$grey-box-border: 1px solid #DDDDDD; |
@ -0,0 +1,14 @@ | |||
<!-- header component --> | |||
<appc-header></appc-header> | |||
<div class="container"> | |||
<!-- component routing placeholder --> | |||
<router-outlet></router-outlet> | |||
</div> | |||
<div class="container"> | |||
<!-- footer component --> | |||
<appc-footer></appc-footer> | |||
</div> |
@ -0,0 +1,2 @@ | |||
@import './_variables.scss'; | |||
@ -0,0 +1,37 @@ | |||
import { Title } from '@angular/platform-browser'; | |||
import { Component, ViewEncapsulation, OnInit } from '@angular/core'; | |||
import { RouterModule } from '@angular/router'; | |||
import { TranslateService } from 'ng2-translate/ng2-translate'; | |||
import { DataService } from './shared/services/data.service'; | |||
/* | |||
* App Component | |||
* Top Level Component | |||
*/ | |||
@Component({ | |||
selector: 'appc-app', | |||
styleUrls: ['./app.component.scss'], | |||
templateUrl: './app.component.html' | |||
}) | |||
export class AppComponent implements OnInit { | |||
constructor(private translate: TranslateService, private titleService: Title) { | |||
// this language will be used as a fallback when a translation isn't found in the current language | |||
translate.setDefaultLang('en'); | |||
// the lang to use, if the lang isn't available, it will use the current loader to get them | |||
translate.use('en'); | |||
} | |||
ngOnInit() { | |||
this.translate.get('title') | |||
.subscribe(title => this.setTitle(title)); | |||
} | |||
public setTitle(newTitle: string) { | |||
this.titleService.setTitle(newTitle); | |||
} | |||
} |
@ -0,0 +1,29 @@ | |||
import { NgModule, NgModuleFactoryLoader } from '@angular/core'; | |||
import { BrowserModule } from '@angular/platform-browser'; | |||
// import { FormsModule } from '@angular/forms'; | |||
import { HttpModule } from '@angular/http'; | |||
import { RouterModule } from '@angular/Router'; | |||
import { routing } from './app.routes'; | |||
import { AppService } from './app.service'; | |||
import { AppComponent } from './app.component'; | |||
import { SharedModule } from './shared/shared.module'; | |||
import { CatalogModule } from './catalog/catalog.module'; | |||
@NgModule({ | |||
declarations: [AppComponent], | |||
imports: [ | |||
BrowserModule, | |||
routing, | |||
// FormsModule, | |||
HttpModule, | |||
// Only module that app module loads | |||
SharedModule.forRoot(), | |||
CatalogModule | |||
], | |||
providers: [ | |||
AppService | |||
], | |||
bootstrap: [AppComponent] | |||
}) | |||
export class AppModule { } |
@ -0,0 +1,15 @@ | |||
import { Routes, RouterModule } from '@angular/router'; | |||
export const routes: Routes = [ | |||
{ path: '', redirectTo: 'catalog', pathMatch: 'full' } | |||
// Lazy async modules | |||
// { | |||
// path: 'login', loadChildren: () => new Promise(resolve => { | |||
// (require as any).ensure([], (require: any) => { | |||
// resolve(require('./+login/login.module').LoginModule); | |||
// }); | |||
// }) | |||
// } | |||
]; | |||
export const routing = RouterModule.forRoot(routes); |
@ -0,0 +1,6 @@ | |||
import { Injectable } from '@angular/core'; | |||
@Injectable() | |||
export class AppService { | |||
constructor() { } | |||
} |
@ -0,0 +1,42 @@ | |||
<div class="catalog-banner"> | |||
<img class="catalog-banner-image" src="../../images/main_banner.png" /> | |||
<div class="container"> | |||
<img src="../../images/main_banner_text.png" class="catalog-banner-text" /> | |||
</div> | |||
</div> | |||
<div class="catalog-filter"> | |||
<div class="catalog-filter-container"> | |||
<div class="container"> | |||
<select> | |||
<option>Opción 1</option> | |||
<option>Opción 2</option> | |||
</select> | |||
<select> | |||
<option>Opción 1</option> | |||
<option>Opción 2</option> | |||
</select> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="catalog-content row"> | |||
<div class="col-md-4 catalog-content-item"> | |||
<img src="https://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt"/> | |||
<button class="catalog-content-item-button">[ ADD TO CART ]</button> | |||
</div> | |||
<div class="col-md-4 catalog-content-item"> | |||
<img src="https://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" /> | |||
<button class="catalog-content-item-button">[ ADD TO CART ]</button> | |||
</div> | |||
<div class="col-md-4 catalog-content-item"> | |||
<img src="https://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" /> | |||
<button class="catalog-content-item-button">[ ADD TO CART ]</button> | |||
</div> | |||
<div class="col-md-4 catalog-content-item"> | |||
<img src="https://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" /> | |||
<button class="catalog-content-item-button">[ ADD TO CART ]</button> | |||
</div> | |||
<div class="col-md-4 catalog-content-item"> | |||
<img src="https://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" /> | |||
<button class="catalog-content-item-button">[ ADD TO CART ]</button> | |||
</div> | |||
</div> |
@ -0,0 +1,55 @@ | |||
@import '../_variables.scss'; | |||
.catalog{ | |||
&-banner { | |||
height: 258px; | |||
vertical-align:middle; | |||
&-image { | |||
width: 100%; | |||
position: absolute; | |||
left: 0; | |||
height: 258px; | |||
} | |||
&-text { | |||
position:relative; | |||
top: 75px; | |||
} | |||
} | |||
&-filter { | |||
height: 65px; | |||
&-container { | |||
position:absolute; | |||
width:100%; | |||
background-color: $primary-colour; | |||
left:0; | |||
height: 65px; | |||
} | |||
} | |||
&-content{ | |||
margin-top: 10px; | |||
&-item { | |||
text-align: center; | |||
&-image{ | |||
} | |||
&-button { | |||
width: 255px; | |||
height: 45px; | |||
padding: 10px 20px 10px 20px; | |||
background-color: $primary-accent; | |||
color: white; | |||
font-size: 16px; | |||
margin: 10px 0; | |||
border:none; | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,15 @@ | |||
import { Component, OnInit } from '@angular/core'; | |||
@Component({ | |||
selector: 'appc-catalog', | |||
styleUrls: ['./catalog.component.scss'], | |||
templateUrl: './catalog.component.html' | |||
}) | |||
export class CatalogComponent implements OnInit { | |||
constructor() { } | |||
ngOnInit() { | |||
console.log('catalog component loaded'); | |||
} | |||
} |
@ -0,0 +1,11 @@ | |||
import { NgModule } from '@angular/core'; | |||
import { CatalogComponent } from './catalog.component'; | |||
import { routing } from './catalog.routes'; | |||
@NgModule({ | |||
imports: [routing], | |||
declarations: [CatalogComponent] | |||
}) | |||
export class CatalogModule { } |
@ -0,0 +1,9 @@ | |||
import { Routes, RouterModule } from '@angular/router'; | |||
import { CatalogComponent } from './catalog.component'; | |||
const routes: Routes = [ | |||
{ path: 'catalog', component: CatalogComponent } | |||
]; | |||
export const routing = RouterModule.forChild(routes); |
@ -0,0 +1,3 @@ | |||
<h1>404!</h1> | |||
<p>Page you are looking for does not exists.</p> |
@ -0,0 +1,11 @@ | |||
/* tslint:disable:no-unused-variable */ | |||
import { TestBed, async } from '@angular/core/testing'; | |||
import { PageNotFoundComponent } from './page-not-found.component'; | |||
describe('Component: PageNotFound', () => { | |||
it('should create an instance', () => { | |||
let component = new PageNotFoundComponent(); | |||
expect(component).toBeTruthy(); | |||
}); | |||
}); |
@ -0,0 +1,15 @@ | |||
import { Component, OnInit } from '@angular/core'; | |||
@Component({ | |||
selector: 'appc-page-not-found', | |||
templateUrl: './page-not-found.component.html', | |||
styleUrls: ['./page-not-found.component.scss'] | |||
}) | |||
export class PageNotFoundComponent implements OnInit { | |||
constructor() { } | |||
ngOnInit() { | |||
} | |||
} |
@ -0,0 +1,10 @@ | |||
import { Component, Input } from '@angular/core'; | |||
@Component({ | |||
selector: 'appc-page-heading', | |||
template: `<h4>{{text}}</h4>` | |||
}) | |||
export class PageHeadingComponent { | |||
@Input() text: string; | |||
constructor() { } | |||
} |
@ -0,0 +1,41 @@ | |||
import { | |||
fakeAsync, | |||
tick, | |||
TestBed | |||
} from '@angular/core/testing'; | |||
import { Component } from '@angular/core'; | |||
import { By } from '@angular/platform-browser/src/dom/debug/by'; | |||
// Load the implementations that should be tested | |||
import { XLargeDirective } from './x-large.directive'; | |||
describe('x-large directive', () => { | |||
// Create a test component to test directives | |||
@Component({ | |||
template: '<div x-large>Content</div>' | |||
}) | |||
class TestComponent { } | |||
beforeEach(() => { | |||
TestBed.configureTestingModule({ | |||
declarations: [ | |||
XLargeDirective, | |||
TestComponent | |||
] | |||
}); | |||
}); | |||
it('should sent font-size to x-large', fakeAsync(() => { | |||
TestBed.compileComponents().then(() => { | |||
const fixture = TestBed.createComponent(TestComponent); | |||
fixture.detectChanges(); | |||
tick(); | |||
const element = fixture.debugElement.query(By.css('div')); | |||
// expect(element.nativeElement.style.fontSize).toBe('x-large'); | |||
}); | |||
})); | |||
}); |
@ -0,0 +1,18 @@ | |||
import { Directive, ElementRef, Renderer } from '@angular/core'; | |||
/* | |||
* Directive | |||
* XLarge is a simple directive to show how one is made | |||
*/ | |||
@Directive({ | |||
selector: '[appdXlarge]' // using [ ] means selecting attributes | |||
}) | |||
export class XLargeDirective { | |||
constructor(element: ElementRef, renderer: Renderer) { | |||
// simple DOM manipulation to set font size to x-large | |||
// `nativeElement` is the direct reference to the DOM element | |||
// element.nativeElement.style.fontSize = 'x-large'; | |||
// for server/webworker support use the renderer | |||
renderer.setElementStyle(element.nativeElement, 'fontSize', 'x-large'); | |||
} | |||
} |
@ -0,0 +1,36 @@ | |||
export class ControlBase<T>{ | |||
value: T; | |||
key: string; | |||
label: string; | |||
placeholder: string; | |||
required: boolean; | |||
minlength: number; | |||
maxlength: number; | |||
order: number; | |||
type: string; | |||
class: string; | |||
constructor(options: { | |||
value?: T, | |||
key?: string, | |||
label?: string, | |||
placeholder?: string, | |||
required?: boolean, | |||
minlength?: number, | |||
maxlength?: number, | |||
order?: number, | |||
type?: string, | |||
class?: string; | |||
} = {}) { | |||
this.value = options.value; | |||
this.key = options.key || ''; | |||
this.label = options.label || ''; | |||
this.placeholder = options.placeholder || ''; | |||
this.required = !!options.required; | |||
this.minlength = options.minlength; | |||
this.maxlength = options.maxlength; | |||
this.order = options.order === undefined ? 1 : options.order; | |||
this.type = options.type || ''; | |||
this.class = options.class || ''; | |||
} | |||
} |
@ -0,0 +1,11 @@ | |||
import { ControlBase } from './control-base'; | |||
export class ControlCheckbox extends ControlBase<string> { | |||
type: string; | |||
constructor(options: any = {}) { | |||
super(options); | |||
this.type = 'checkbox'; | |||
this.value = options.value || false; | |||
} | |||
} |
@ -0,0 +1,11 @@ | |||
import { ControlBase } from './control-base'; | |||
export class ControlDropdown extends ControlBase<string> { | |||
options: { key: string, value: string }[] = []; | |||
constructor(options: any = {}) { | |||
super(options); | |||
this.type = 'dropdown'; | |||
this.options = options.options || []; | |||
} | |||
} |
@ -0,0 +1,8 @@ | |||
import { ControlBase } from './control-base'; | |||
export class ControlTextbox extends ControlBase<boolean> { | |||
constructor(options: any = {}) { | |||
super(options); | |||
this.type = options.type || 'textbox'; | |||
} | |||
} |
@ -0,0 +1,26 @@ | |||
<!--{{f.controls[control.key] | json}}--> | |||
<div #f="ngForm" [formGroup]="form" [ngSwitch]="control.type" class="form-group {{control.class}}" [class.has-danger]="invalid" | |||
[class.has-success]="valid" [class.form-check]="control.type === 'checkbox'"> | |||
<label *ngSwitchCase="'dropdown'" [attr.for]="control.key" class="col-form-label">{{control.label}}</label> | |||
<select *ngSwitchCase="'dropdown'" [id]="control.key" [formControlName]="control.key" [class.form-control-success]="valid" | |||
[class.form-control-danger]="invalid" class="form-control"> | |||
<option *ngFor="let opt of control.options" [value]="opt.key">{{opt.value}}</option> | |||
</select> | |||
<label *ngSwitchCase="'checkbox'" [attr.for]="control.key" class="form-check-label"> | |||
<input #ck *ngSwitchCase="'checkbox'" (change)="control.value = ck.checked" [id]="control.key" [formControlName]="control.key" | |||
[type]="control.type" class="form-check-input"> | |||
{{control.label}} | |||
</label> | |||
<!--This is not the switch case because of multiple control types--> | |||
<label *ngIf="control.type === 'textbox' || control.type === 'email' || control.type === 'password'" [attr.for]="control.key" class="col-form-label">{{control.label}}</label> | |||
<input *ngIf="control.type === 'textbox' || control.type === 'email' || control.type === 'password'" [id]="control.key" [formControlName]="control.key" [type]="control.type" | |||
[placeholder]="control.placeholder" [class.form-control-success]="valid" [class.form-control-danger]="invalid" class="form-control"> | |||
<appc-control-error-message [form]="f" [control]="control"></appc-control-error-message> | |||
</div> |
@ -0,0 +1,26 @@ | |||
import { Component, Input } from '@angular/core'; | |||
import { FormGroup } from '@angular/forms'; | |||
import { ControlBase } from './control-base'; | |||
import { ErrorMessageComponent } from './error-message.component'; | |||
@Component({ | |||
selector: 'appc-dynamic-control', | |||
templateUrl: './dynamic-form-control.component.html' | |||
}) | |||
export class DynamicFormControlComponent { | |||
@Input() control; | |||
@Input() form; | |||
constructor() { | |||
this.control = undefined; | |||
this.form = undefined; | |||
} | |||
get valid() { | |||
return this.form.controls[this.control.key].valid; | |||
} | |||
get invalid() { | |||
return !this.form.controls[this.control.key].valid && this.form.controls[this.control.key].touched; | |||
} | |||
} |
@ -0,0 +1,5 @@ | |||
<form class="{{formClass}}" (ngSubmit)="onSubmit()" [formGroup]="form" novalidate role="form"> | |||
<appc-dynamic-control *ngFor="let ctrl of controls" [control]="ctrl" [form]="form"></appc-dynamic-control> | |||
<button type="submit" class="btn btn-primary pull-right" [disabled]="!form.valid">{{btnText}}</button> | |||
</form> |
@ -0,0 +1,30 @@ | |||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; | |||
import { FormGroup } from '@angular/forms'; | |||
import { ControlBase } from './control-base'; | |||
import { FormControlService } from './form-control.service'; | |||
@Component({ | |||
selector: 'appc-dynamic-form', | |||
templateUrl: './dynamic-form.component.html' | |||
}) | |||
export class DynamicFormComponent implements OnInit { | |||
@Input() controls: ControlBase<any>[] = []; | |||
@Input() btnText: string = 'Submit'; // Default value at least | |||
@Input() formClass: string = 'form-horizontal'; | |||
// Note: don't keep name of output events as same as native events such as submit etc. | |||
@Output() formsubmit: EventEmitter<any> = new EventEmitter<any>(); | |||
form: FormGroup; | |||
constructor(private _controlService: FormControlService) { } | |||
ngOnInit() { | |||
let sortedControls = this.controls.sort((a, b) => a.order - b.order); | |||
this.form = this._controlService.toControlGroup(sortedControls); | |||
} | |||
onSubmit() { | |||
this.formsubmit.emit(this.form.value); | |||
} | |||
} |
@ -0,0 +1,25 @@ | |||
import { Component, Host, Input } from '@angular/core'; | |||
import { FormGroupDirective } from '@angular/forms'; | |||
import { ControlBase } from './control-base'; | |||
import { ValidationService } from './validation.service'; | |||
@Component({ | |||
selector: 'appc-control-error-message', | |||
template: `<div *ngIf="errorMessage" class="form-control-feedback"> {{errorMessage}} </div>` | |||
}) | |||
export class ErrorMessageComponent { | |||
@Input() control: ControlBase<any>; | |||
@Input() form: FormGroupDirective; | |||
constructor() { } | |||
get errorMessage() { | |||
let c = this.form.form.get(this.control.key); | |||
for (let propertyName in c.errors) { | |||
if (c.errors.hasOwnProperty(propertyName) && c.touched) { | |||
return ValidationService.getValidatorErrorMessage(propertyName, this.control.minlength || this.control.maxlength); | |||
} | |||
} | |||
return undefined; | |||
} | |||
} |
@ -0,0 +1,7 @@ | |||
<div class="alert alert-danger" *ngIf="errors?.length > 0"> | |||
<ul> | |||
<li *ngFor="let error of errors"> | |||
{{error}} | |||
</li> | |||
</ul> | |||
</div> |
@ -0,0 +1,11 @@ | |||
import { Component, Input } from '@angular/core'; | |||
@Component({ | |||
selector: 'appc-error-summary', | |||
templateUrl: './error-summary.component.html' | |||
}) | |||
export class ErrorSummaryComponent { | |||
@Input() errors: string | string[]; | |||
constructor() { } | |||
} |
@ -0,0 +1,41 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { FormControl, FormGroup, Validators } from '@angular/forms'; | |||
import { ControlBase } from './control-base'; | |||
import { ValidationService } from './validation.service'; | |||
@Injectable() | |||
export class FormControlService { | |||
constructor() { } | |||
toControlGroup(controls: ControlBase<any>[]) { | |||
let group: any = {}; | |||
controls.forEach(control => { | |||
let validators = []; | |||
// Required | |||
if (control.required) { | |||
validators.push(Validators.required); | |||
} | |||
// Minlength | |||
if (control.minlength) { | |||
validators.push(Validators.minLength(control.minlength)); | |||
} | |||
// Maxlength | |||
if (control.maxlength) { | |||
validators.push(Validators.minLength(control.maxlength)); | |||
} | |||
if (control.type === 'email') { | |||
validators.push(ValidationService.emailValidator); | |||
} | |||
// Password | |||
if (control.type === 'password') { | |||
validators.push(ValidationService.passwordValidator); | |||
} | |||
group[control.key] = new FormControl(control.value || '', validators); | |||
}); | |||
return new FormGroup(group); | |||
} | |||
} |
@ -0,0 +1,42 @@ | |||
export class ValidationService { | |||
static getValidatorErrorMessage(code: string, fieldLength: number) { | |||
let config: any = { | |||
'required': 'This is a required field', | |||
'minlength': 'Minimum length is ' + fieldLength, | |||
'maxlength': 'Maximum length is ' + fieldLength, | |||
'invalidCreditCard': 'Invalid credit card number', | |||
'invalidEmailAddress': 'Invalid email address', | |||
'invalidPassword': 'Password must be at least 6 characters long, and contain a number and special character.' | |||
}; | |||
return config[code]; | |||
} | |||
static creditCardValidator(control: any) { | |||
// Visa, MasterCard, American Express, Diners Club, Discover, JCB | |||
if (control.value.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/)) { | |||
return undefined; | |||
} else { | |||
return { 'invalidCreditCard': true }; | |||
} | |||
} | |||
static emailValidator(control: any) { | |||
// RFC 2822 compliant regex | |||
if (control.value.match(/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/)) { | |||
return undefined; | |||
} else { | |||
return { 'invalidEmailAddress': true }; | |||
} | |||
} | |||
static passwordValidator(control: any) { | |||
// {6,100} - Assert password is between 6 and 100 characters | |||
// (?=.*[0-9]) - Assert a string has at least one number | |||
if (control.value.match(/^(?=.*[0-9])[a-zA-Z0-9!"@#$%^&*]{6,100}$/)) { | |||
return undefined; | |||
} else { | |||
return { 'invalidPassword': true }; | |||
} | |||
} | |||
} |
@ -0,0 +1,8 @@ | |||
<footer class="text-muted"> | |||
<div class="container"> | |||
<hr> | |||
<p class="text-muted"> | |||
© 2015-2016 {{'title' | translate}} Company | |||
</p> | |||
</div> | |||
</footer> |
@ -0,0 +1,8 @@ | |||
@import '../../_variables.scss'; | |||
.footer { | |||
padding-top: 40px; | |||
padding-bottom: 40px; | |||
margin-top: 40px; | |||
border-top: 1px solid #eee; | |||
} |
@ -0,0 +1,10 @@ | |||
import { Component } from '@angular/core'; | |||
@Component({ | |||
selector: 'appc-footer', | |||
styleUrls: ['./footer.component.scss'], | |||
templateUrl: './footer.component.html' | |||
}) | |||
export class FooterComponent { | |||
constructor() { } | |||
} |
@ -0,0 +1,62 @@ | |||
<header class="navbar navbar-light navbar-static-top"> | |||
<div class="container"> | |||
<nav> | |||
<div class="clearfix"> | |||
<!--<button class="navbar-toggler float-xs-right hidden-sm-up collapsed" type="button" data-toggle="collapse" data-target="#bd-main-nav" | |||
aria-controls="bd-main-nav" aria-expanded="false" aria-label="Toggle navigation" (click)="toggleNav()"> | |||
☰ | |||
</button> | |||
<a class="navbar-brand hidden-sm-up header-brand" routerLink="home"> | |||
Ng2fbBootstrap | |||
<img src="../../../images/brand.png" /> | |||
</a>--> | |||
</div> | |||
<div class="" id="bd-main-nav" aria-expanded="false" style="height: 0px;"> | |||
<a class="navbar-brand" routerLink="home"> | |||
<img src="../../../images/brand.png" /> | |||
</a> | |||
<!--<ul class="nav navbar-nav"> | |||
<li class="nav-item"> | |||
<a class="nav-item nav-link" routerLinkActive="active" routerLink="home">Home</a> | |||
</li> | |||
<li class="nav-item"> | |||
<a class="nav-item nav-link" routerLinkActive="active" routerLink="examples">Examples</a> | |||
</li> | |||
<li> | |||
<ul class="nav float-xs-left float-md-right"> | |||
<li class="nav-item" *ngIf="authService.isLoggedIn() && authService.user()"> | |||
<a class="nav-item nav-link" routerLinkActive="active" routerLink="profile"> | |||
<i class="fa fa-user"></i> {{authService.user().displayName}} | |||
</a> | |||
</li> | |||
<li class="nav-item" *ngIf="!authService.isLoggedIn()"> | |||
<a class="nav-item nav-link" routerLinkActive="active" routerLink="register"> | |||
<i class="fa fa-user"></i> Register | |||
</a> | |||
</li> | |||
<li class="nav-item" *ngIf="!authService.isLoggedIn()"> | |||
<a class="nav-item nav-link" routerLinkActive="active" routerLink="login"> | |||
<i class="fa fa-sign-in"></i>Login | |||
</a> | |||
</li> | |||
<li class="nav-item" *ngIf="authService.isLoggedIn() && authService.user()?.roles?.indexOf('Admin') > -1"> | |||
<a class="nav-item nav-link" routerLinkActive="active" routerLink="admin"> | |||
<i class="fa fa-gear"></i> Admin | |||
</a> | |||
</li> | |||
<li class="nav-item" *ngIf="authService.isLoggedIn()"> | |||
<a class="nav-item nav-link" (click)="authService.logout()" routerLinkActive="active" href="javascript:void(null);"> | |||
<i class="fa fa-sign-out"></i> Logout | |||
</a> | |||
</li> | |||
</ul> | |||
</li> | |||
</ul>--> | |||
</div> | |||
</nav> | |||
</div> | |||
</header> |
@ -0,0 +1,5 @@ | |||
@import '../../_variables.scss'; | |||
.header-brand { | |||
background-image:url('../../../images/brand.png') | |||
} |
@ -0,0 +1,18 @@ | |||
import { Component, Inject } from '@angular/core'; | |||
import { Router } from '@angular/router'; | |||
import { AuthService } from '../services/auth.service'; | |||
@Component({ | |||
selector: 'appc-header', | |||
styleUrls: ['./header.component.scss'], | |||
templateUrl: './header.component.html' | |||
}) | |||
export class HeaderComponent { | |||
isCollapsed: boolean = true; | |||
constructor(private router: Router, private authService: AuthService) { } | |||
toggleNav() { | |||
this.isCollapsed = !this.isCollapsed; | |||
} | |||
} |
@ -0,0 +1,3 @@ | |||
export class OperationResult { | |||
constructor(public succeeded: boolean, public message: string) { } | |||
} |
@ -0,0 +1,13 @@ | |||
import { User } from './user.model'; | |||
// todo: I dont think user follows angular style guides | |||
describe('User Model', () => { | |||
it('has displayName', () => { | |||
let userModel: User = {displayName: 'test', roles: ['1']}; | |||
expect(userModel.displayName).toEqual('test'); | |||
}); | |||
it('has displayName', () => { | |||
let userModel: User = {displayName: 'test', roles: ['admin']}; | |||
expect(userModel.roles[0]).toEqual('admin'); | |||
}); | |||
}); |
@ -0,0 +1,4 @@ | |||
export class User { | |||
constructor(public displayName: string, public roles: string[]) { | |||
} | |||
} |
@ -0,0 +1,21 @@ | |||
import { UppercasePipe } from './uppercase.pipe'; | |||
describe('Pipe appfUppercase', () => { | |||
let pipe: UppercasePipe; | |||
beforeEach(() => { | |||
pipe = new UppercasePipe(); | |||
}); | |||
it('transforms "abc" to "ABC"', () => { | |||
expect(pipe.transform('abc')).toEqual('ABC'); | |||
}); | |||
it('transforms "abc def" to "ABC DEF"', () => { | |||
expect(pipe.transform('abc def')).toEqual('ABC DEF'); | |||
}); | |||
it('leaves "ABC DEF" unchanged', () => { | |||
expect(pipe.transform('ABC DEF')).toEqual('ABC DEF'); | |||
}); | |||
}); |
@ -0,0 +1,10 @@ | |||
import { Pipe, PipeTransform } from '@angular/core'; | |||
@Pipe({ | |||
name: 'appfUppercase' | |||
}) | |||
export class UppercasePipe implements PipeTransform { | |||
transform(value: string) { | |||
return value.toUpperCase(); | |||
} | |||
} |
@ -0,0 +1,207 @@ | |||
// CREDIT: | |||
// The vast majority of this code came right from Ben Nadel's post: | |||
// http://www.bennadel.com/blog/3047-creating-specialized-http-clients-in-angular-2-beta-8.htm | |||
// | |||
// My updates are mostly adapting it for Typescript: | |||
// 1. Importing required modules | |||
// 2. Adding type notations | |||
// 3. Using the 'fat-arrow' syntax to properly scope in-line functions | |||
// | |||
import 'rxjs/add/operator/map'; | |||
import 'rxjs/add/operator/catch'; | |||
import 'rxjs/add/operator/finally'; | |||
import { Injectable } from '@angular/core'; | |||
import { Http, Response, RequestOptions, RequestMethod, URLSearchParams } from '@angular/http'; | |||
import { Observable } from 'rxjs/Observable'; | |||
import { Subject } from 'rxjs/Subject'; | |||
import { HttpErrorHandlerService } from './http-error-handler.service'; | |||
// Import the rxjs operators we need (in a production app you'll | |||
// probably want to import only the operators you actually use) | |||
// | |||
export class ApiGatewayOptions { | |||
method: RequestMethod; | |||
url: string; | |||
headers: any = {}; | |||
params = {}; | |||
data = {}; | |||
} | |||
@Injectable() | |||
export class ApiGatewayService { | |||
// Define the internal Subject we'll use to push the command count | |||
private pendingCommandsSubject = new Subject<number>(); | |||
private pendingCommandCount = 0; | |||
// Provide the *public* Observable that clients can subscribe to | |||
private pendingCommands$: Observable<number>; | |||
constructor(private http: Http, private httpErrorHandler: HttpErrorHandlerService) { | |||
this.pendingCommands$ = this.pendingCommandsSubject.asObservable(); | |||
} | |||
// I perform a GET request to the API, appending the given params | |||
// as URL search parameters. Returns a stream. | |||
get(url: string, params: any): Observable<Response> { | |||
let options = new ApiGatewayOptions(); | |||
options.method = RequestMethod.Get; | |||
options.url = url; | |||
options.params = params; | |||
return this.request(options); | |||
} | |||
// I perform a POST request to the API. If both the params and data | |||
// are present, the params will be appended as URL search parameters | |||
// and the data will be serialized as a JSON payload. If only the | |||
// data is present, it will be serialized as a JSON payload. Returns | |||
// a stream. | |||
post(url: string, data: any, params: any): Observable<Response> { | |||
if (!data) { | |||
data = params; | |||
params = {}; | |||
} | |||
let options = new ApiGatewayOptions(); | |||
options.method = RequestMethod.Post; | |||
options.url = url; | |||
options.params = params; | |||
options.data = data; | |||
return this.request(options); | |||
} | |||
private request(options: ApiGatewayOptions): Observable<any> { | |||
options.method = (options.method || RequestMethod.Get); | |||
options.url = (options.url || ''); | |||
options.headers = (options.headers || {}); | |||
options.params = (options.params || {}); | |||
options.data = (options.data || {}); | |||
this.interpolateUrl(options); | |||
this.addXsrfToken(options); | |||
this.addContentType(options); | |||
// TODO add auth token when available | |||
// this.addAuthToken(options); | |||
let requestOptions = new RequestOptions(); | |||
requestOptions.method = options.method; | |||
requestOptions.url = options.url; | |||
requestOptions.headers = options.headers; | |||
requestOptions.search = this.buildUrlSearchParams(options.params); | |||
requestOptions.body = JSON.stringify(options.data); | |||
let isCommand = (options.method !== RequestMethod.Get); | |||
if (isCommand) { | |||
this.pendingCommandsSubject.next(++this.pendingCommandCount); | |||
} | |||
let stream = this.http.request(options.url, requestOptions) | |||
.catch((error: any) => { | |||
this.httpErrorHandler.handle(error); | |||
return Observable.throw(error); | |||
}) | |||
.map(this.unwrapHttpValue) | |||
.catch((error: any) => { | |||
return Observable.throw(this.unwrapHttpError(error)); | |||
}) | |||
.finally(() => { | |||
if (isCommand) { | |||
this.pendingCommandsSubject.next(--this.pendingCommandCount); | |||
} | |||
}); | |||
return stream; | |||
} | |||
private addContentType(options: ApiGatewayOptions): ApiGatewayOptions { | |||
if (options.method !== RequestMethod.Get) { | |||
options.headers['Content-Type'] = 'application/json; charset=UTF-8'; | |||
} | |||
return options; | |||
} | |||
private addAuthToken(options: ApiGatewayOptions): ApiGatewayOptions { | |||
options.headers.Authorization = 'Bearer ' + JSON.parse(sessionStorage.getItem('accessToken')); | |||
return options; | |||
} | |||
private extractValue(collection: any, key: string): any { | |||
let value = collection[key]; | |||
delete (collection[key]); | |||
return value; | |||
} | |||
private addXsrfToken(options: ApiGatewayOptions): ApiGatewayOptions { | |||
let xsrfToken = this.getXsrfCookie(); | |||
if (xsrfToken) { | |||
options.headers['X-XSRF-TOKEN'] = xsrfToken; | |||
} | |||
return options; | |||
} | |||
private getXsrfCookie(): string { | |||
let matches = document.cookie.match(/\bXSRF-TOKEN=([^\s;]+)/); | |||
try { | |||
return (matches && decodeURIComponent(matches[1])); | |||
} catch (decodeError) { | |||
return (''); | |||
} | |||
} | |||
private addCors(options: ApiGatewayOptions): ApiGatewayOptions { | |||
options.headers['Access-Control-Allow-Origin'] = '*'; | |||
return options; | |||
} | |||
private buildUrlSearchParams(params: any): URLSearchParams { | |||
let searchParams = new URLSearchParams(); | |||
for (let key in params) { | |||
if (params.hasOwnProperty(key)) { | |||
searchParams.append(key, params[key]); | |||
} | |||
} | |||
return searchParams; | |||
} | |||
private interpolateUrl(options: ApiGatewayOptions): ApiGatewayOptions { | |||
options.url = options.url.replace(/:([a-zA-Z]+[\w-]*)/g, ($0, token) => { | |||
// Try to move matching token from the params collection. | |||
if (options.params.hasOwnProperty(token)) { | |||
return (this.extractValue(options.params, token)); | |||
} | |||
// Try to move matching token from the data collection. | |||
if (options.data.hasOwnProperty(token)) { | |||
return (this.extractValue(options.data, token)); | |||
} | |||
// If a matching value couldn't be found, just replace | |||
// the token with the empty string. | |||
return (''); | |||
}); | |||
// Clean up any repeating slashes. | |||
options.url = options.url.replace(/\/{2,}/g, '/'); | |||
// Clean up any trailing slashes. | |||
options.url = options.url.replace(/\/+$/g, ''); | |||
return options; | |||
} | |||
private unwrapHttpError(error: any): any { | |||
try { | |||
return (error.json()); | |||
} catch (jsonError) { | |||
return ({ | |||
code: -1, | |||
message: 'An unexpected error occurred.' | |||
}); | |||
} | |||
} | |||
private unwrapHttpValue(value: Response): any { | |||
return (value.json()); | |||
} | |||
} |
@ -0,0 +1,23 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { Observable } from 'rxjs/Rx'; | |||
import { TranslateLoader } from 'ng2-translate/ng2-translate'; | |||
import { MissingTranslationHandler, MissingTranslationHandlerParams } from 'ng2-translate/ng2-translate'; | |||
import { ContentService } from './content.service'; | |||
@Injectable() | |||
export class ApiTranslationLoader implements TranslateLoader { | |||
constructor(private cs: ContentService) { } | |||
getTranslation(lang: string): Observable<any> { | |||
return this.cs.get(lang); | |||
} | |||
} | |||
@Injectable() | |||
export class CustomMissingTranslationHandler implements MissingTranslationHandler { | |||
handle(params: MissingTranslationHandlerParams) { | |||
return params.key; | |||
} | |||
} |
@ -0,0 +1,37 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { Router } from '@angular/router'; | |||
import { DataService } from './data.service'; | |||
import { User } from '../models/user.model'; | |||
@Injectable() | |||
export class AuthService { | |||
constructor(private router: Router) { } | |||
logout() { | |||
sessionStorage.clear(); | |||
this.router.navigate(['/login']); | |||
} | |||
isLoggedIn(): boolean { | |||
return this.user(undefined) !== undefined; | |||
} | |||
user(user: User): User { | |||
if (user) { | |||
sessionStorage.setItem('user', JSON.stringify(user)); | |||
} | |||
let userData = JSON.parse(sessionStorage.getItem('user')); | |||
if (userData) { | |||
user = new User(userData.displayName, userData.roles); | |||
} | |||
return user ? user : undefined; | |||
} | |||
setAuth(res: any): void { | |||
if (res && res.user) { | |||
sessionStorage.setItem('user', JSON.stringify(res.user)); | |||
} | |||
} | |||
} |
@ -0,0 +1,13 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { DataService } from './data.service'; | |||
@Injectable() | |||
export class ContentService { | |||
constructor(public dataService: DataService) { } | |||
get(lang?: string): any { | |||
return this.dataService.get('api/content?lang=' + (lang ? lang : 'en')); | |||
} | |||
} |
@ -0,0 +1,17 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { ApiGatewayService } from './api-gateway.service'; | |||
@Injectable() | |||
export class DataService { | |||
constructor(public http: ApiGatewayService) { } | |||
get(url: string, params?: any) { | |||
return this.http.get(url, undefined); | |||
} | |||
post(url: string, data: any, params?: any) { | |||
return this.http.post(url, data, params); | |||
} | |||
} |
@ -0,0 +1,25 @@ | |||
// CREDIT: | |||
// The vast majority of this code came right from Ben Nadel's post: | |||
// http://www.bennadel.com/blog/3047-creating-specialized-http-clients-in-angular-2-beta-8.htm | |||
// | |||
// My updates are mostly adapting it for Typescript: | |||
// 1. Importing required modules | |||
// 2. Adding type notations | |||
// 3. Using the 'fat-arrow' syntax to properly scope in-line functions | |||
// | |||
import { Injectable } from '@angular/core'; | |||
import { Router } from '@angular/router'; | |||
@Injectable() | |||
export class HttpErrorHandlerService { | |||
constructor(private _router: Router) { } | |||
handle(error: any) { | |||
if (error.status === 401) { | |||
sessionStorage.clear(); | |||
// window.location.href = 'login'; | |||
this._router.navigate(['Login']); | |||
} | |||
} | |||
} |
@ -0,0 +1,13 @@ | |||
import { Injectable } from '@angular/core'; | |||
@Injectable() | |||
export class NotificationService { | |||
printSuccessMessage(message: string) { | |||
console.log(message); | |||
} | |||
printErrorMessage(message: string) { | |||
console.error(message); | |||
} | |||
} |
@ -0,0 +1,25 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { Router } from '@angular/router'; | |||
@Injectable() | |||
export class UtilityService { | |||
private _router: Router; | |||
constructor(router: Router) { | |||
this._router = router; | |||
} | |||
convertDateTime(date: Date) { | |||
let _formattedDate = new Date(date.toString()); | |||
return _formattedDate.toDateString(); | |||
} | |||
navigate(path: string) { | |||
this._router.navigate([path]); | |||
} | |||
navigateToSignIn() { | |||
this.navigate('/login'); | |||
} | |||
} |
@ -0,0 +1,86 @@ | |||
import { NgModule, ModuleWithProviders } from '@angular/core'; | |||
import { CommonModule } from '@angular/common'; | |||
import { FormsModule, ReactiveFormsModule, FormBuilder } from '@angular/forms'; | |||
import { RouterModule } from '@angular/router'; | |||
import { HttpModule, JsonpModule } from '@angular/http'; | |||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; | |||
import { TranslateModule, TranslateLoader } from 'ng2-translate/ng2-translate'; | |||
import { PageHeadingComponent } from './directives/page-heading.directive'; | |||
import { DynamicFormComponent } from './forms/dynamic-form.component'; | |||
import { DynamicFormControlComponent } from './forms/dynamic-form-control.component'; | |||
import { ErrorMessageComponent } from './forms/error-message.component'; | |||
import { ErrorSummaryComponent } from './forms/error-summary.component'; | |||
import { FormControlService } from './forms/form-control.service'; | |||
import { HeaderComponent } from './layout/header.component'; | |||
import { FooterComponent } from './layout/footer.component'; | |||
// Services | |||
import { DataService } from './services/data.service'; | |||
import { ApiGatewayService } from './services/api-gateway.service'; | |||
import { AuthService } from './services/auth.service'; | |||
import { HttpErrorHandlerService } from './services/http-error-handler.service'; | |||
import { ApiTranslationLoader } from './services/api-translation-loader.service'; | |||
import { ContentService } from './services/content.service'; | |||
import { UtilityService } from './services/utility.service'; | |||
import { UppercasePipe } from './pipes/uppercase.pipe'; | |||
@NgModule({ | |||
imports: [ | |||
CommonModule, | |||
FormsModule, | |||
ReactiveFormsModule, | |||
RouterModule, | |||
NgbModule.forRoot(), | |||
// No need to export as these modules don't expose any components/directive etc' | |||
HttpModule, | |||
JsonpModule, | |||
TranslateModule.forRoot({ provide: TranslateLoader, useClass: ApiTranslationLoader }) | |||
], | |||
declarations: [ | |||
DynamicFormComponent, | |||
DynamicFormControlComponent, | |||
ErrorMessageComponent, | |||
ErrorSummaryComponent, | |||
FooterComponent, | |||
HeaderComponent, | |||
PageHeadingComponent, | |||
UppercasePipe | |||
], | |||
exports: [ | |||
// Modules | |||
CommonModule, | |||
FormsModule, | |||
ReactiveFormsModule, | |||
RouterModule, | |||
NgbModule, | |||
TranslateModule, | |||
// Providers, Components, directive, pipes | |||
DynamicFormComponent, | |||
DynamicFormControlComponent, | |||
ErrorSummaryComponent, | |||
ErrorMessageComponent, | |||
FooterComponent, | |||
HeaderComponent, | |||
PageHeadingComponent, | |||
UppercasePipe | |||
] | |||
}) | |||
export class SharedModule { | |||
static forRoot(): ModuleWithProviders { | |||
return { | |||
ngModule: SharedModule, | |||
providers: [ | |||
// Providers | |||
HttpErrorHandlerService, | |||
ApiGatewayService, | |||
AuthService, | |||
DataService, | |||
ContentService, | |||
FormControlService, | |||
UtilityService | |||
] | |||
}; | |||
} | |||
} |
@ -0,0 +1,23 @@ | |||
// Added parts of es6 which are necessary for your project or your browser support requirements. | |||
import 'core-js/es6/symbol'; | |||
import 'core-js/es6/object'; | |||
import 'core-js/es6/function'; | |||
import 'core-js/es6/parse-int'; | |||
import 'core-js/es6/parse-float'; | |||
import 'core-js/es6/number'; | |||
import 'core-js/es6/math'; | |||
import 'core-js/es6/string'; | |||
import 'core-js/es6/date'; | |||
import 'core-js/es6/array'; | |||
import 'core-js/es6/regexp'; | |||
import 'core-js/es6/map'; | |||
import 'core-js/es6/set'; | |||
import 'core-js/es6/weak-map'; | |||
import 'core-js/es6/weak-set'; | |||
import 'core-js/es6/typed'; | |||
import 'core-js/es6/reflect'; | |||
// see issue https://github.com/AngularClass/angular2-webpack-starter/issues/709 | |||
// import 'core-js/es6/promise'; | |||
import 'core-js/es7/reflect'; | |||
import 'zone.js/dist/zone'; |
@ -0,0 +1,20 @@ | |||
// For vendors for example jQuery, Lodash, angular2-jwt just import them here unless you plan on | |||
// chunking vendors files for async loading. You would need to import the async loaded vendors | |||
// at the entry point of the async loaded file. Also see custom-typings.d.ts as you also need to | |||
// run `typings install x` where `x` is your module | |||
// Angular 2 | |||
import '@angular/platform-browser'; | |||
import '@angular/platform-browser-dynamic'; | |||
import '@angular/core'; | |||
import '@angular/common'; | |||
import '@angular/forms'; | |||
import '@angular/http'; | |||
import '@angular/router'; | |||
// RxJS | |||
import 'rxjs/add/operator/map'; | |||
import 'rxjs/add/operator/mergeMap'; | |||
import 'rxjs/add/operator/catch'; | |||
import 'rxjs/add/operator/finally'; | |||
import 'rxjs/add/observable/throw'; |
@ -0,0 +1,14 @@ | |||
# FROM microsoft/aspnet:1.0.0-rc1-update1 | |||
# COPY . /app | |||
# WORKDIR /app | |||
# RUN ["dnu", "restore"] | |||
# EXPOSE 5104/tcp | |||
# ENTRYPOINT ["dnx", "-p", "project.json", "web"] | |||
FROM microsoft/aspnetcore:1.0.1 | |||
ENTRYPOINT ["dotnet", "eShopOnContainers.WebSPA.dll"] | |||
ARG source=. | |||
WORKDIR /app | |||
ENV ASPNETCORE_URLS http://*:80 | |||
EXPOSE 80 | |||
COPY $source . |
@ -0,0 +1,27 @@ | |||
using System.IO; | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.Extensions.Configuration; | |||
namespace eShopConContainers.WebSPA | |||
{ | |||
public class Program | |||
{ | |||
public static void Main(string[] args) | |||
{ | |||
var config = new ConfigurationBuilder() | |||
.SetBasePath(Directory.GetCurrentDirectory()) | |||
.AddJsonFile("hosting.json", optional: true) | |||
.Build(); | |||
var host = new WebHostBuilder() | |||
.UseKestrel() | |||
.UseConfiguration(config) | |||
.UseContentRoot(Directory.GetCurrentDirectory()) | |||
.UseIISIntegration() | |||
.UseStartup<Startup>() | |||
.Build(); | |||
host.Run(); | |||
} | |||
} | |||
} |
@ -0,0 +1,18 @@ | |||
{ | |||
"iisSettings": { | |||
"windowsAuthentication": false, | |||
"anonymousAuthentication": true, | |||
"iisExpress": { | |||
"applicationUrl": "http://localhost:1250/", | |||
"sslPort": 0 | |||
} | |||
}, | |||
"profiles": { | |||
"IIS Express": { | |||
"commandName": "IISExpress", | |||
"environmentVariables": { | |||
"ASPNETCORE_ENVIRONMENT": "Development" | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,35 @@ | |||
// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 | |||
using System.Linq; | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.AspNetCore.Mvc; | |||
namespace eShopConContainers.WebSPA.Server.Controllers | |||
{ | |||
public class HomeController : Controller | |||
{ | |||
private readonly IHostingEnvironment _env; | |||
public HomeController(IHostingEnvironment env) | |||
{ | |||
_env = env; | |||
} | |||
public IActionResult Index() | |||
{ | |||
ViewBag.HashedMain = GetHashedMainDotJs(); | |||
return View(); | |||
} | |||
public string GetHashedMainDotJs() | |||
{ | |||
var basePath = _env.WebRootPath + "//dist//"; | |||
var info = new System.IO.DirectoryInfo(basePath); | |||
var file = info.GetFiles().Where(f => f.Name.StartsWith("main.") && !f.Name.EndsWith("bundle.map")).FirstOrDefault(); | |||
return file.Name; | |||
} | |||
} | |||
} |
@ -0,0 +1,93 @@ | |||
using System; | |||
using Microsoft.AspNetCore.Antiforgery; | |||
using Microsoft.AspNetCore.Builder; | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.AspNetCore.Http; | |||
using Microsoft.AspNetCore.SpaServices.Webpack; | |||
using Microsoft.Extensions.Configuration; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Logging; | |||
using Newtonsoft.Json.Serialization; | |||
namespace eShopConContainers.WebSPA | |||
{ | |||
public class Startup | |||
{ | |||
private IHostingEnvironment _hostingEnv; | |||
public Startup(IHostingEnvironment env) | |||
{ | |||
_hostingEnv = env; | |||
var builder = new ConfigurationBuilder() | |||
.SetBasePath(env.ContentRootPath) | |||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) | |||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) | |||
.AddEnvironmentVariables(); | |||
if (env.IsDevelopment()) | |||
{ | |||
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709 | |||
builder.AddUserSecrets(); | |||
} | |||
Configuration = builder.Build(); | |||
} | |||
public static IConfigurationRoot Configuration { get; set; } | |||
// This method gets called by the runtime. Use this method to add services to the container. | |||
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 | |||
public void ConfigureServices(IServiceCollection services) | |||
{ | |||
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN"); | |||
services.AddMvc() | |||
.AddJsonOptions(options => | |||
{ | |||
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); | |||
}); | |||
} | |||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IAntiforgery antiforgery) | |||
{ | |||
if (env.IsDevelopment()) | |||
{ | |||
app.UseDeveloperExceptionPage(); | |||
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions | |||
{ | |||
HotModuleReplacement = true, | |||
ConfigFile = "config/webpack.config.js" | |||
}); | |||
} | |||
// Configure XSRF middleware, This pattern is for SPA style applications where XSRF token is added on Index page | |||
// load and passed back token on every subsequent async request | |||
app.Use(async (context, next) => | |||
{ | |||
if (string.Equals(context.Request.Path.Value, "/", StringComparison.OrdinalIgnoreCase)) | |||
{ | |||
var tokens = antiforgery.GetAndStoreTokens(context); | |||
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false }); | |||
} | |||
await next.Invoke(); | |||
}); | |||
app.UseStaticFiles(); | |||
app.UseMvc(routes => | |||
{ | |||
routes.MapRoute( | |||
name: "default", | |||
template: "{controller=Home}/{action=Index}/{id?}"); | |||
routes.MapSpaFallbackRoute( | |||
name: "spa-fallback", | |||
defaults: new { controller = "Home", action = "Index" }); | |||
}); | |||
} | |||
} | |||
} |
@ -0,0 +1,20 @@ | |||
<style> | |||
div.app-spinner { | |||
position: fixed; | |||
top: 50%; | |||
left: 50%; | |||
} | |||
</style> | |||
<appc-app asp-prerender-webpack-config="config/webpack.config.js"> | |||
<div class="app-spinner"> | |||
<i class="fa fa-spinner fa-spin fa-5x" aria-hidden="true"></i> | |||
</div> | |||
</appc-app> | |||
<script> | |||
window.user = '@ViewBag.user'; | |||
</script> | |||
<script src="~/dist/vendor.js" asp-append-version="true"></script> | |||
<script src="~/dist/@ViewBag.HashedMain" asp-append-version="true"></script> |
@ -0,0 +1,16 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<meta charset="utf-8" /> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |||
<title>eShopConContainers.WebSPA</title> | |||
<base href="/" /> | |||
<link rel="stylesheet" href="~/dist/vendor.css" asp-append-version="true" /> | |||
</head> | |||
<body> | |||
@RenderBody() | |||
</body> | |||
</html> |
@ -0,0 +1,3 @@ | |||
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" | |||
@addTagHelper "*, Microsoft.AspNetCore.SpaServices" |
@ -0,0 +1,3 @@ | |||
@{ | |||
Layout = "_Layout"; | |||
} |
@ -0,0 +1,47 @@ | |||
{ | |||
"ConnectionStrings": { | |||
"DefaultConnection": "Data Source=AspNetCore.db" | |||
}, | |||
"Logging": { | |||
"IncludeScopes": false, | |||
"LogLevel": { | |||
"Default": "Debug", | |||
"System": "Information", | |||
"Microsoft": "Information" | |||
} | |||
}, | |||
"Email": { | |||
"From": "", | |||
"Subject": "", | |||
"SendGrid": { | |||
"Username": "", | |||
"Password": "" | |||
} | |||
}, | |||
"Authentication": { | |||
"Google": { | |||
"ClientId": "", | |||
"ClientSecret": "" | |||
}, | |||
"Facebook": { | |||
"AppId": "", | |||
"AppSecret": "" | |||
}, | |||
"Microsoft": { | |||
"ClientId": "", | |||
"ClientSecret": "" | |||
}, | |||
"Twitter": { | |||
"ConsumerKey": "", | |||
"ConsumerSecret": "" | |||
}, | |||
"Github": { | |||
"ClientId": "", | |||
"ClientSecret": "" | |||
}, | |||
"LinkedIn": { | |||
"ClientId": "", | |||
"ClientSecret": "" | |||
} | |||
} | |||
} |
@ -0,0 +1,23 @@ | |||
/** | |||
* @author: @AngularClass | |||
*/ | |||
var path = require('path'); | |||
// Helper functions | |||
var ROOT = path.resolve(__dirname, '..'); | |||
console.log('root directory:', root() + '\n'); | |||
function hasProcessFlag(flag) { | |||
return process.argv.join('').indexOf(flag) > -1; | |||
} | |||
function root(args) { | |||
args = Array.prototype.slice.call(arguments, 0); | |||
return path.join.apply(path, [ROOT].concat(args)); | |||
} | |||
exports.hasProcessFlag = hasProcessFlag; | |||
exports.root = root; |
@ -0,0 +1,3 @@ | |||
module.exports = { | |||
devtool: 'cheap-module-source-map' | |||
}; |
@ -0,0 +1,74 @@ | |||
var path = require('path'); | |||
var webpack = require('webpack'); | |||
var merge = require('extendify')({ isDeep: true, arrays: 'concat' }); | |||
var ExtractTextPlugin = require('extract-text-webpack-plugin'); | |||
var extractCSS = new ExtractTextPlugin('styles.css'); | |||
var ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin; | |||
var devConfig = require('./webpack.config.dev'); | |||
var prodConfig = require('./webpack.config.prod'); | |||
var isDevelopment = process.env.ASPNETCORE_ENVIRONMENT === 'Production'; | |||
console.log("==========Dev Mode = " + isDevelopment + " ============" ) | |||
module.exports = merge({ | |||
resolve: { | |||
extensions: ['.js', '.ts'] | |||
}, | |||
module: { | |||
rules: [ | |||
{ test: /\.ts$/, exclude: [/\.(spec|e2e)\.ts$/], loaders: ['awesome-typescript-loader?forkChecker=true ', 'angular2-template-loader'] }, | |||
{ test: /\.html$/, loader: "html" }, | |||
{ test: /\.css/, loader: extractCSS.extract(['css']) }, | |||
{ test: /\.scss$/, loaders: ['raw-loader', 'sass-loader?sourceMap'] }, | |||
{ test: /\.json$/, loader: 'json-loader' }, | |||
{ | |||
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, | |||
loader: "url?limit=10000&mimetype=application/font-woff" | |||
}, { | |||
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, | |||
loader: "url?limit=10000&mimetype=application/font-woff" | |||
}, { | |||
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, | |||
loader: "url?limit=10000&mimetype=application/octet-stream" | |||
}, { | |||
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, | |||
loader: "file" | |||
}, { | |||
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, | |||
loader: "url?limit=10000&mimetype=image/svg+xml" | |||
}, | |||
{ | |||
test: /\.(png|jpg|gif)$/, | |||
loader: "file" | |||
} | |||
] | |||
}, | |||
entry: { | |||
'main': './Client/main.ts' | |||
}, | |||
output: { | |||
path: path.join(__dirname, '../wwwroot', 'dist'), | |||
filename: '[name].js', | |||
publicPath: '/dist/' | |||
}, | |||
profile: true, | |||
plugins: [ | |||
extractCSS, | |||
new webpack.DllReferencePlugin({ | |||
context: __dirname, | |||
manifest: require('../wwwroot/dist/vendor-manifest.json') | |||
}), | |||
// To eliminate warning | |||
// https://github.com/AngularClass/angular2-webpack-starter/issues/993 | |||
new webpack.ContextReplacementPlugin( | |||
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, | |||
__dirname | |||
), | |||
new ForkCheckerPlugin(), | |||
new webpack.DefinePlugin({ | |||
'process.env': { | |||
'ENV': JSON.stringify(process.env.ASPNETCORE_ENVIRONMENT) | |||
} | |||
}) | |||
] | |||
}, isDevelopment ? devConfig : prodConfig); |
@ -0,0 +1,23 @@ | |||
var webpack = require('webpack'); | |||
const WebpackMd5Hash = require('webpack-md5-hash'); | |||
module.exports = { | |||
devtool: 'source-map', | |||
output: { | |||
filename: '[name].[chunkhash].bundle.js', | |||
sourceMapFilename: '[name].[chunkhash].bundle.map', | |||
chunkFilename: '[id].[chunkhash].chunk.js' | |||
}, | |||
plugins: [ | |||
// new webpack.LoaderOptionsPlugin({ | |||
// minimize: true, | |||
// debug: false | |||
// }), | |||
new WebpackMd5Hash(), | |||
new webpack.optimize.UglifyJsPlugin({ | |||
beautify: false, | |||
comments: false, | |||
sourceMap: true | |||
}) | |||
] | |||
}; |
@ -0,0 +1,73 @@ | |||
var path = require('path'); | |||
var webpack = require('webpack'); | |||
var ExtractTextPlugin = require('extract-text-webpack-plugin'); | |||
var extractCSS = new ExtractTextPlugin('vendor.css'); | |||
var isDevelopment = process.env.ASPNETCORE_ENVIRONMENT === 'Development'; | |||
module.exports = { | |||
resolve: { | |||
extensions: ['.js'] | |||
}, | |||
module: { | |||
rules: [ | |||
{ test: /\.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' }, | |||
{ test: /\.scss$/i, loader: extractCSS.extract(['css?minimize', 'sass']) }, | |||
{ test: /\.json$/, loader: 'json-loader' } | |||
] | |||
}, | |||
entry: { | |||
// polyfills: [ | |||
// 'core-js/es6/symbol', | |||
// 'core-js/es6/object', | |||
// 'core-js/es6/function', | |||
// 'core-js/es6/parse-int', | |||
// 'core-js/es6/parse-float', | |||
// 'core-js/es6/number', | |||
// 'core-js/es6/math', | |||
// 'core-js/es6/string', | |||
// 'core-js/es6/date', | |||
// 'core-js/es6/array', | |||
// 'core-js/es6/regexp', | |||
// 'core-js/es6/map', | |||
// 'core-js/es6/set', | |||
// 'core-js/es6/reflect', | |||
// 'core-js/es7/reflect', | |||
// 'zone.js/dist/zone' | |||
// ], | |||
vendor: [ | |||
'font-awesome/scss/font-awesome.scss', | |||
'bootstrap/scss/bootstrap.scss', | |||
'@angular/common', | |||
'@angular/compiler', | |||
'@angular/core', | |||
'@angular/http', | |||
'@angular/forms', | |||
'@angular/platform-browser', | |||
'@angular/platform-browser-dynamic', | |||
'@angular/router' | |||
] | |||
}, | |||
output: { | |||
path: path.join(__dirname, '../wwwroot', 'dist'), | |||
filename: '[name].js', | |||
library: '[name]_[hash]', | |||
}, | |||
plugins: [ | |||
extractCSS, | |||
// To eliminate warning | |||
// https://github.com/AngularClass/angular2-webpack-starter/issues/993 | |||
new webpack.ContextReplacementPlugin( | |||
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, | |||
__dirname | |||
), | |||
new webpack.DllPlugin({ | |||
path: path.join(__dirname, '../wwwroot', 'dist', '[name]-manifest.json'), | |||
name: '[name]_[hash]' | |||
}) | |||
].concat(isDevelopment ? [] : [ | |||
new webpack.optimize.UglifyJsPlugin({ | |||
beautify: false, | |||
comments: false | |||
}) | |||
]) | |||
}; |
@ -0,0 +1,78 @@ | |||
version: '2' | |||
services: | |||
webspa: | |||
image: eshop/webspa | |||
build: | |||
context: . | |||
dockerfile: Dockerfile | |||
environment: | |||
- CatalogUrl=http://catalog.api | |||
- OrderingUrl=http://ordering.api | |||
ports: | |||
- "5104:80" | |||
depends_on: | |||
- catalog.api | |||
- identity.data | |||
catalog.api: | |||
image: eshop/catalog.api | |||
environment: | |||
- ConnectionString=Server=catalog.data;Initial Catalog=CatalogData;User Id=sa;Password=Pass@word | |||
expose: | |||
- "80" | |||
ports: | |||
- "5101:80" | |||
depends_on: | |||
- catalog.data | |||
catalog.data: | |||
image: eshop/mssql-server-private-preview | |||
environment: | |||
- SA_PASSWORD=Pass@word | |||
- ACCEPT_EULA=Y | |||
ports: | |||
- "5434:1433" | |||
ordering.api: | |||
image: eshop/ordering.api | |||
environment: | |||
- ConnectionString=Server=ordering.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word | |||
ports: | |||
- "5102:80" | |||
# (Go to Production): For secured/final deployment, remove Ports mapping and | |||
# leave just the internal expose section | |||
# expose: | |||
# - "80" | |||
extra_hosts: | |||
- "CESARDLBOOKVHD:10.0.75.1" | |||
depends_on: | |||
- ordering.data | |||
ordering.data: | |||
image: eshop/ordering.data.sqlserver.linux | |||
ports: | |||
- "5432:1433" | |||
identity.data: | |||
image: eshop/mssql-server-private-preview | |||
environment: | |||
- SA_PASSWORD=Pass@word | |||
- ACCEPT_EULA=Y | |||
ports: | |||
- "5433:1433" | |||
basket.api: | |||
image: eshop/basket.api | |||
environment: | |||
- ConnectionString=basket.data | |||
build: | |||
context: . | |||
dockerfile: Dockerfile | |||
ports: | |||
- "5103:80" | |||
depends_on: | |||
- basket.data | |||
basket.data: | |||
image: redis |
@ -0,0 +1,17 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<PropertyGroup> | |||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> | |||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> | |||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> | |||
</PropertyGroup> | |||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> | |||
<PropertyGroup Label="Globals"> | |||
<ProjectGuid>9842db3a-1391-48c7-a49c-2fabd0a18ac2</ProjectGuid> | |||
<RootNamespace>eShopOnContainers.WebSPA</RootNamespace> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<SchemaVersion>2.0</SchemaVersion> | |||
</PropertyGroup> | |||
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" /> | |||
</Project> |
@ -0,0 +1,3 @@ | |||
{ | |||
"server.urls": "http://localhost:5000;http://localhost:5001" | |||
} |
@ -0,0 +1,109 @@ | |||
{ | |||
"name": "eshopaspnetnetcoredockerspa", | |||
"version": "0.0.0", | |||
"private": true, | |||
"keywords": [ | |||
"aspnetcore", | |||
"entityframework core", | |||
"angular2", | |||
"webpack2", | |||
"typescript2", | |||
"bootstrap4", | |||
"docker" | |||
], | |||
"author": { | |||
"name": "Microsoft", | |||
"email": "cesardl@microsoft.com" | |||
}, | |||
"scripts": { | |||
"rimraf": "rimraf", | |||
"tslint": "tslint", | |||
"typedoc": "typedoc", | |||
"typings": "typings", | |||
"webpack": "webpack", | |||
"clean": "npm cache clean && npm run rimraf -- node_modules doc typings coverage wwwroot/dist", | |||
"clean:dist": "npm run rimraf -- wwwroot/dist", | |||
"preclean:install": "npm run clean", | |||
"clean:install": "npm set progress=false && npm install", | |||
"preclean:start": "npm run clean", | |||
"clean:start": "npm start", | |||
"build:vendor": "node node_modules/webpack/bin/webpack.js --config config/webpack.config.vendor.js", | |||
"build:main": "node node_modules/webpack/bin/webpack.js --config config/webpack.config.js", | |||
"setdev": "set ASPNETCORE_ENVIRONMENT=Development", | |||
"setprod": "set ASPNETCORE_ENVIRONMENT=Production", | |||
"build:dev": "npm run setdev && npm run clean:dist && npm run build:vendor && npm run build:main", | |||
"build:prod": "npm run setprod && npm run clean:dist && npm run build:vendor && npm run build:main", | |||
"lint": "npm run tslint \"Client/**/*.ts\"", | |||
"docs": "npm run typedoc -- --options typedoc.json --exclude '**/*.spec.ts' ./Client/", | |||
"version": "npm run build", | |||
}, | |||
"dependencies": { | |||
"@angular/common": "2.1.2", | |||
"@angular/compiler": "2.1.2", | |||
"@angular/compiler-cli": "2.1.2", | |||
"@angular/core": "2.1.2", | |||
"@angular/forms": "2.1.2", | |||
"@angular/http": "2.1.2", | |||
"@angular/platform-browser": "2.1.2", | |||
"@angular/platform-browser-dynamic": "2.1.2", | |||
"@angular/platform-server": "2.1.2", | |||
"@angular/router": "3.1.2", | |||
"@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.11", | |||
"aspnet-prerendering": "1.0.7", | |||
"aspnet-webpack": "1.0.24", | |||
"bootstrap": "4.0.0-alpha.5", | |||
"core-js": "2.4.1", | |||
"font-awesome": "4.6.3", | |||
"isomorphic-fetch": "2.2.1", | |||
"ng2-translate": "4.0.0", | |||
"normalize.css": "5.0.0", | |||
"preboot": "4.5.2", | |||
"rxjs": "5.0.0-beta.12", | |||
"zone.js": "0.6.26" | |||
}, | |||
"devDependencies": { | |||
"@types/core-js": "0.9.34", | |||
"@types/hammerjs": "2.0.33", | |||
"@types/jasmine": "2.5.35", | |||
"@types/node": "6.0.45", | |||
"@types/protractor": "1.5.20", | |||
"@types/selenium-webdriver": "2.44.26", | |||
"@types/sinon": "1.16.31", | |||
"@types/source-map": "0.1.28", | |||
"@types/uglify-js": "2.6.28", | |||
"@types/webpack": "1.12.35", | |||
"angular2-template-loader": "0.6.0", | |||
"awesome-typescript-loader": "2.2.4", | |||
"codelyzer": "1.0.0-beta.3", | |||
"copy-webpack-plugin": "^4.0.0", | |||
"css": "2.2.1", | |||
"css-loader": "0.25.0", | |||
"es6-promise": "3.2.1", | |||
"es6-promise-loader": "1.0.2", | |||
"expose-loader": "0.7.1", | |||
"extendify": "1.0.0", | |||
"extract-text-webpack-plugin": "2.0.0-beta.4", | |||
"file-loader": "0.9.0", | |||
"html-loader": "0.4.4", | |||
"html-webpack-plugin": "^2.24.1", | |||
"json-loader": "0.5.4", | |||
"node-sass": "3.9.3", | |||
"parse5": "2.1.5", | |||
"raw-loader": "0.5.1", | |||
"rimraf": "2.5.4", | |||
"sass-loader": "4.0.2", | |||
"source-map-loader": "0.1.5", | |||
"style-loader": "0.13.1", | |||
"ts-helpers": "1.1.1", | |||
"ts-node": "1.4.3", | |||
"tslint": "3.15.1", | |||
"tslint-loader": "2.1.5", | |||
"typedoc": "0.5.0", | |||
"typescript": "2.0.6", | |||
"url-loader": "0.5.7", | |||
"webpack": "2.1.0-beta.25", | |||
"webpack-externals-plugin": "1.0.0", | |||
"webpack-hot-middleware": "2.13.0", | |||
"webpack-md5-hash": "0.0.5" | |||
} | |||
} |
@ -0,0 +1,116 @@ | |||
{ | |||
"userSecretsId": "aspnetcorespa-c23d27a4-eb88-4b18-9b77-2a93f3b15119", | |||
"dependencies": { | |||
"Microsoft.NETCore.App": { | |||
"version": "1.0.0", | |||
"type": "platform" | |||
}, | |||
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0", | |||
"Microsoft.AspNetCore.Authentication.Cookies": "1.0.0", | |||
"Microsoft.AspNetCore.Diagnostics": "1.0.0", | |||
"Microsoft.AspNetCore.Mvc": "1.0.1", | |||
"Microsoft.AspNetCore.Cors": "1.0.0", | |||
"Microsoft.AspNetCore.Antiforgery": "1.0.1", | |||
"Microsoft.AspNetCore.Authorization": "1.0.0", | |||
"Newtonsoft.Json": "9.0.1", | |||
"Webpack": "3.0.0", | |||
"Microsoft.AspNetCore.AngularServices": "1.0.0-beta-000014", | |||
"Microsoft.AspNetCore.Razor.Tools": { | |||
"version": "1.0.0-preview2-final", | |||
"type": "build" | |||
}, | |||
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", | |||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0", | |||
"Microsoft.AspNetCore.StaticFiles": "1.0.0", | |||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", | |||
"Microsoft.Extensions.Configuration.Json": "1.0.0", | |||
"Microsoft.Extensions.Logging": "1.0.0", | |||
"Microsoft.Extensions.Logging.Console": "1.0.0", | |||
"Microsoft.Extensions.Logging.Debug": "1.0.0", | |||
"Microsoft.VisualStudio.Web.CodeGeneration.Tools": { | |||
"version": "1.0.0-preview2-final", | |||
"type": "build" | |||
}, | |||
"Microsoft.VisualStudio.Web.CodeGenerators.Mvc": { | |||
"version": "1.0.0-preview2-final", | |||
"type": "build" | |||
}, | |||
"Microsoft.AspNetCore.Http.Abstractions": "1.0.0" | |||
}, | |||
"tools": { | |||
"Microsoft.DotNet.Watcher.Tools": { | |||
"version": "1.0.0-*", | |||
"imports": "portable-net451+win8" | |||
}, | |||
"Microsoft.AspNetCore.Razor.Tools": { | |||
"version": "1.0.0-preview2-final", | |||
"imports": "portable-net45+win8+dnxcore50" | |||
}, | |||
"Microsoft.AspNetCore.Server.IISIntegration.Tools": { | |||
"version": "1.0.0-preview2-final", | |||
"imports": "portable-net45+win8+dnxcore50" | |||
}, | |||
"Microsoft.Extensions.SecretManager.Tools": { | |||
"version": "1.0.0-preview2-final", | |||
"imports": "portable-net45+win8+dnxcore50" | |||
}, | |||
"Microsoft.VisualStudio.Web.CodeGeneration.Tools": { | |||
"version": "1.0.0-preview2-final", | |||
"imports": [ | |||
"portable-net45+win8+dnxcore50", | |||
"portable-net45+win8" | |||
] | |||
} | |||
}, | |||
"frameworks": { | |||
"netcoreapp1.0": { | |||
"imports": [ | |||
"dotnet5.6", | |||
"portable-net45+win8" | |||
] | |||
} | |||
}, | |||
"buildOptions": { | |||
"emitEntryPoint": true, | |||
"preserveCompilationContext": true, | |||
"compile": { | |||
"exclude": [ | |||
"node_modules", | |||
"Client" | |||
] | |||
}, | |||
"debugType": "portable" | |||
}, | |||
"runtimeOptions": { | |||
"configProperties": { | |||
"System.GC.Server": true | |||
} | |||
}, | |||
"publishOptions": { | |||
"include": [ | |||
"appsettings.json", | |||
"Client", | |||
"typings", | |||
"Views", | |||
"tsconfig.json", | |||
"tsd.json", | |||
"web.config", | |||
"config", | |||
"wwwroot", | |||
"dockerfile" | |||
] | |||
}, | |||
"scripts": { | |||
// "prepublish": [ | |||
// "npm install", | |||
// "node node_modules/webpack/bin/webpack.js --config config/webpack.config.vendor.js", | |||
// "node node_modules/webpack/bin/webpack.js --config config/webpack.config.js" | |||
// ], | |||
"postpublish": [ | |||
"dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" | |||
] | |||
}, | |||
"tooling": { | |||
"defaultNamespace": "eShopOnContainers.SPA" | |||
} | |||
} |
@ -0,0 +1,42 @@ | |||
{ | |||
"compilerOptions": { | |||
"target": "es5", | |||
"module": "commonjs", | |||
"moduleResolution": "node", | |||
"declaration": false, | |||
"emitDecoratorMetadata": true, | |||
"experimentalDecorators": true, | |||
"allowSyntheticDefaultImports": true, | |||
"sourceMap": true, | |||
"strictNullChecks": false, | |||
"baseUrl": "./src", | |||
"paths": {}, | |||
"lib": [ | |||
"dom", | |||
"es6" | |||
], | |||
"types": [ | |||
"hammerjs", | |||
"jasmine", | |||
"node", | |||
"protractor", | |||
"selenium-webdriver", | |||
"source-map", | |||
"uglify-js", | |||
"webpack" | |||
] | |||
}, | |||
"exclude": [ | |||
"node_modules", | |||
"wwwroot" | |||
], | |||
"awesomeTypescriptLoaderOptions": { | |||
"forkChecker": true, | |||
"useWebpackText": true | |||
}, | |||
"compileOnSave": false, | |||
"buildOnSave": false, | |||
"atom": { | |||
"rewriteTsconfig": false | |||
} | |||
} |
@ -0,0 +1,178 @@ | |||
{ | |||
"rulesDirectory": [ | |||
"node_modules/codelyzer" | |||
], | |||
"rules": { | |||
"directive-selector-name": [ | |||
true, | |||
"camelCase" | |||
], | |||
"component-selector-name": [ | |||
true, | |||
"kebab-case" | |||
], | |||
"directive-selector-type": [ | |||
true, | |||
"attribute" | |||
], | |||
"component-selector-type": [ | |||
true, | |||
"element" | |||
], | |||
"directive-selector-prefix": [ | |||
true, | |||
"appd" | |||
], | |||
"component-selector-prefix": [ | |||
true, | |||
"appc" | |||
], | |||
"use-input-property-decorator": true, | |||
"use-output-property-decorator": true, | |||
"use-host-property-decorator": true, | |||
"no-attribute-parameter-decorator": true, | |||
"no-input-rename": true, | |||
"no-output-rename": true, | |||
"no-forward-ref": true, | |||
"use-life-cycle-interface": true, | |||
"use-pipe-transform-interface": true, | |||
"pipe-naming": [ | |||
true, | |||
"camelCase", | |||
"appf" | |||
], | |||
"component-class-suffix": true, | |||
"directive-class-suffix": true, | |||
"import-destructuring-spacing": true, | |||
"member-access": false, | |||
"member-ordering": [ | |||
true, | |||
"public-before-private", | |||
"static-before-instance", | |||
"variables-before-functions" | |||
], | |||
"no-any": false, | |||
"no-inferrable-types": false, | |||
"no-internal-module": true, | |||
"no-var-requires": false, | |||
"typedef": false, | |||
"typedef-whitespace": [ | |||
true, | |||
{ | |||
"call-signature": "nospace", | |||
"index-signature": "nospace", | |||
"parameter": "nospace", | |||
"property-declaration": "nospace", | |||
"variable-declaration": "nospace" | |||
}, | |||
{ | |||
"call-signature": "space", | |||
"index-signature": "space", | |||
"parameter": "space", | |||
"property-declaration": "space", | |||
"variable-declaration": "space" | |||
} | |||
], | |||
"ban": false, | |||
"curly": false, | |||
"forin": true, | |||
"label-position": true, | |||
"label-undefined": true, | |||
"no-arg": true, | |||
"no-bitwise": true, | |||
"no-conditional-assignment": true, | |||
"no-console": [ | |||
true, | |||
"debug", | |||
"info", | |||
"time", | |||
"timeEnd", | |||
"trace" | |||
], | |||
"no-construct": true, | |||
"no-debugger": true, | |||
"no-duplicate-key": true, | |||
"no-duplicate-variable": true, | |||
"no-empty": false, | |||
"no-eval": true, | |||
"no-null-keyword": true, | |||
"no-shadowed-variable": true, | |||
"no-string-literal": true, | |||
"no-switch-case-fall-through": true, | |||
"no-unreachable": true, | |||
"no-unused-expression": true, | |||
"no-unused-variable": false, | |||
"no-use-before-declare": true, | |||
"no-var-keyword": true, | |||
"radix": true, | |||
"switch-default": true, | |||
"triple-equals": [ | |||
true, | |||
"allow-null-check" | |||
], | |||
"use-strict": [ | |||
true, | |||
"check-module" | |||
], | |||
"eofline": true, | |||
"indent": [ | |||
true, | |||
"spaces" | |||
], | |||
"max-line-length": [ | |||
true, | |||
200 | |||
], | |||
"no-require-imports": false, | |||
"no-trailing-whitespace": true, | |||
"object-literal-sort-keys": false, | |||
"trailing-comma": [ | |||
true, | |||
{ | |||
"multiline": "never", | |||
"singleline": "never" | |||
} | |||
], | |||
"align": false, | |||
"class-name": true, | |||
"comment-format": [ | |||
true, | |||
"check-space" | |||
], | |||
"interface-name": false, | |||
"jsdoc-format": true, | |||
"no-consecutive-blank-lines": false, | |||
"no-constructor-vars": false, | |||
"one-line": [ | |||
true, | |||
"check-open-brace", | |||
"check-catch", | |||
"check-else", | |||
"check-finally", | |||
"check-whitespace" | |||
], | |||
"quotemark": [ | |||
true, | |||
"single", | |||
"avoid-escape" | |||
], | |||
"semicolon": [ | |||
true, | |||
"always" | |||
], | |||
"variable-name": [ | |||
true, | |||
"check-format", | |||
"allow-leading-underscore", | |||
"ban-keywords" | |||
], | |||
"whitespace": [ | |||
true, | |||
"check-branch", | |||
"check-decl", | |||
"check-operator", | |||
"check-separator", | |||
"check-type" | |||
] | |||
} | |||
} |
@ -0,0 +1,15 @@ | |||
{ | |||
"mode": "modules", | |||
"out": "doc", | |||
"theme": "default", | |||
"ignoreCompilerErrors": "true", | |||
"experimentalDecorators": "true", | |||
"emitDecoratorMetadata": "true", | |||
"target": "ES5", | |||
"moduleResolution": "node", | |||
"preserveConstEnums": "true", | |||
"stripInternal": "true", | |||
"suppressExcessPropertyErrors": "true", | |||
"suppressImplicitAnyIndexErrors": "true", | |||
"module": "commonjs" | |||
} |
@ -0,0 +1,14 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<configuration> | |||
<!-- | |||
Configure your application settings in appsettings.json. Learn more at https://go.microsoft.com/fwlink/?LinkId=786380 | |||
--> | |||
<system.webServer> | |||
<handlers> | |||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/> | |||
</handlers> | |||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/> | |||
</system.webServer> | |||
</configuration> |
@ -0,0 +1,9 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<configuration> | |||
<system.webServer> | |||
<handlers> | |||
<add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/> | |||
</handlers> | |||
<httpPlatform processPath="%DNX_PATH%" arguments="%DNX_ARGS%" stdoutLogEnabled="false"/> | |||
</system.webServer> | |||
</configuration> |