@ -0,0 +1 @@ | |||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 32 32" height="32px" id="Слой_1" version="1.1" viewBox="0 0 32 32" width="32px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path clip-rule="evenodd" d="M21.698,15.286l-9.002-8.999 c-0.395-0.394-1.035-0.394-1.431,0c-0.395,0.394-0.395,1.034,0,1.428L19.553,16l-8.287,8.285c-0.395,0.394-0.395,1.034,0,1.429 c0.395,0.394,1.036,0.394,1.431,0l9.002-8.999C22.088,16.325,22.088,15.675,21.698,15.286z" fill="#FFFFFF" fill-rule="evenodd" id="Chevron_Right"/><g/><g/><g/><g/><g/><g/></svg> |
@ -1,14 +1,41 @@ | |||
<!-- header component --> | |||
<appc-header></appc-header> | |||
<header class="navbar navbar-light navbar-static-top"> | |||
<div class="container"> | |||
<div class="row"> | |||
<div class="col-sm-10"> | |||
<a class="navbar-brand" routerLink="catalog"> | |||
<img src="../images/brand.png" /> | |||
</a> | |||
</div> | |||
<div class="col-sm-2"> | |||
<esh-basket-status></esh-basket-status> | |||
</div> | |||
</div> | |||
</div> | |||
</header> | |||
<div class="container"> | |||
<!-- component routing placeholder --> | |||
<router-outlet></router-outlet> | |||
</div> | |||
<div class="container"> | |||
<!-- footer component --> | |||
<appc-footer></appc-footer> | |||
<!-- component routing placeholder --> | |||
<router-outlet></router-outlet> | |||
<footer class="app-footer"> | |||
<div class="container"> | |||
<div class="row"> | |||
<div class="col-sm-6"> | |||
<br> | |||
<div class="app-footer-brand"> | |||
<img src="../images/brand_dark.png" /> | |||
</div> | |||
</div> | |||
<div class="col-sm-6"> | |||
<br> | |||
<br> | |||
<br> | |||
<div class="app-footer-text hidden-xs">e-ShoponContainers. All right reserved</div> | |||
</div> | |||
</div> | |||
</div> | |||
</footer> | |||
</div> |
@ -1,2 +1,49 @@ | |||
@import './_variables.scss'; | |||
@font-face { | |||
font-family: Montserrat; | |||
font-weight: 400; | |||
src: url("../fonts/Montserrat-Regular.eot?") format("eot"),url("../fonts/Montserrat-Regular.woff") format("woff"),url("../fonts/Montserrat-Regular.ttf") format("truetype"),url("../fonts/Montserrat-Regular.svg#Montserrat") format("svg") | |||
} | |||
@font-face { | |||
font-family: Montserrat; | |||
font-weight: 700; | |||
src: url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg") | |||
} | |||
body { | |||
padding-top: 80px; | |||
/*padding-bottom: 20px;*/ | |||
font-family: Montserrat,sans-serif; | |||
min-width:480px; | |||
} | |||
.app { | |||
&-footer { | |||
padding-top: 40px; | |||
padding-bottom: 40px; | |||
margin-top: 40px; | |||
border-top: 1px solid #eee; | |||
background-color: black; | |||
width: 100%; | |||
&-brand { | |||
margin-top: 25px; | |||
max-width: 231px; | |||
height: 52px; | |||
margin-left: 0px !important; | |||
} | |||
&-text { | |||
text-align: right; | |||
width: 100%; | |||
height: 100%; | |||
color: #83d01b; | |||
margin-top: 10px; | |||
} | |||
} | |||
} | |||
@ -1,15 +1,17 @@ | |||
import { Routes, RouterModule } from '@angular/router'; | |||
import { BasketComponent } from './basket/basket.component'; | |||
import { CatalogComponent } from './catalog/catalog.component'; | |||
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); | |||
// }); | |||
// }) | |||
// } | |||
{ path: '', redirectTo: 'catalog', pathMatch: 'full' }, | |||
{ path: 'basket', component: BasketComponent }, | |||
{ path: 'catalog', component: CatalogComponent } | |||
//Lazy async modules (angular-loader-router) and enable a router in each module. | |||
//{ | |||
// path: 'basket', loadChildren: '/basket/basket.module' }); | |||
// }) | |||
//} | |||
]; | |||
export const routing = RouterModule.forRoot(routes); |
@ -0,0 +1,8 @@ | |||
<div class="esh-basketstatus"> | |||
<div [routerLink]="['basket']" class="esh-basketstatus-image"> | |||
<img src="../../../images/cart.png" /> | |||
</div> | |||
<div class="esh-basketstatus-badge"> | |||
{{badge}} | |||
</div> | |||
</div> |
@ -0,0 +1,24 @@ | |||
@import '../../_variables.scss'; | |||
.esh-basketstatus { | |||
&-image { | |||
height: 36px; | |||
margin-top: 5px; | |||
} | |||
&-badge { | |||
position: absolute; | |||
margin-top: 2px; | |||
margin-left: 14px; | |||
background-color: #83d01b; | |||
padding: 1px; | |||
color: white; | |||
border-radius: 50%; | |||
width: 18px; | |||
height: 18px; | |||
font-size: 12px; | |||
cursor: pointer; | |||
top: 0; | |||
} | |||
} |
@ -0,0 +1,29 @@ | |||
import { Component, OnInit } from '@angular/core'; | |||
import { Subscription } from 'rxjs/Subscription'; | |||
import { BasketService } from '../basket.service'; | |||
import { BasketWrapperService } from '../../shared/services/basket.wrapper.service'; | |||
@Component({ | |||
selector: 'esh-basket-status', | |||
styleUrls: ['./basket-status.component.scss'], | |||
templateUrl: './basket-status.component.html' | |||
}) | |||
export class BasketStatusComponent implements OnInit { | |||
subscription: Subscription; | |||
badge: number = 0; | |||
constructor(private service: BasketService, private basketEvents: BasketWrapperService) { } | |||
ngOnInit() { | |||
this.subscription = this.basketEvents.addItemToBasket$.subscribe( | |||
item => { | |||
console.log(item); | |||
this.service.setBasket(item); | |||
this.badge = this.service.getBasket().length; | |||
}); | |||
} | |||
} | |||
@ -0,0 +1,63 @@ | |||
<div class="esh-basket-header"> | |||
<ul class="container"> | |||
<li class="esh-basket-header-back" routerLink="/catalog">Back to list</li> | |||
</ul> | |||
</div> | |||
<div class="container esh-basket-container"> | |||
<div class="row"> | |||
<div class="col-md-12"> | |||
<section> | |||
<table class="table"> | |||
<thead> | |||
<tr> | |||
<th class="esh-basket-product-column"> | |||
PRODUCT | |||
</th> | |||
<th> | |||
</th> | |||
<th> | |||
BRAND | |||
</th> | |||
<th> | |||
PRICE | |||
</th> | |||
<th> | |||
QUANTITY | |||
</th> | |||
<th> | |||
COST | |||
</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<tr *ngFor="let item of basket?.items"> | |||
<td class="esh-basket-product-column"><img class="esh-basket-product-image" src="{{item.pictureUrl}}" /></td> | |||
<td class="esh-basket-product-column">{{item.productName}}</td> | |||
<td class="esh-basket-product-column">ROSLYN</td> | |||
<td class="esh-basket-product-column">$ {{item.unitPrice}}</td> | |||
<td class="esh-basket-product-column"> | |||
<input type="number" style="width:100px" min="1" [(ngModel)]="item.quantity" (change)="itemQuantityChanged(item)"/> | |||
</td> | |||
<td class="esh-basket-product-column esh-basket-total-value">$ {{item.unitPrice * item.quantity}}</td> | |||
</tr> | |||
<tr class="esh-basket-totals"> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td> | |||
</td> | |||
<td> | |||
<div class="esh-basket-total-value"> | |||
<div class="esh-basket-total-label"><span>TOTAL</span></div> | |||
<span>$ {{totalPrice}}</span> | |||
</div> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</section> | |||
</div> | |||
</div> | |||
</div> |
@ -0,0 +1,80 @@ | |||
@import '../_variables.scss'; | |||
.esh-basket { | |||
&-header { | |||
background-color: #00A69C; | |||
height: 63px; | |||
li { | |||
list-style: none; | |||
display: inline; | |||
opacity: 0.5; | |||
margin-top: 25px; | |||
margin-left: 10px; | |||
float: right; | |||
cursor: pointer; | |||
color: white; | |||
} | |||
li a { | |||
color: white; | |||
} | |||
&-back { | |||
float: left !important; | |||
margin-top: 20px !important; | |||
text-transform: uppercase; | |||
} | |||
li a:hover { | |||
text-decoration: none; | |||
} | |||
} | |||
&-container { | |||
min-height: 70vh; | |||
padding-top: 40px; | |||
margin-bottom: 30px; | |||
min-width: 992px; | |||
} | |||
&-product-column { | |||
max-width: 120px; | |||
text-transform: uppercase; | |||
vertical-align: middle !important; | |||
} | |||
&-product-image { | |||
max-width: 210px; | |||
} | |||
&-total-value { | |||
font-size: 20px; | |||
color: #00a69c; | |||
} | |||
&-total-label { | |||
font-size: 14px; | |||
color: #404040; | |||
margin-top: 10px; | |||
} | |||
&-totals { | |||
border-bottom:none!important; | |||
} | |||
} | |||
.table td { | |||
border-top: solid 1px #ddd; | |||
} | |||
.table thead th { | |||
border: none !important; | |||
} | |||
@ -0,0 +1,36 @@ | |||
import { Component, OnInit } from '@angular/core'; | |||
import { BasketService } from './basket.service'; | |||
import { IBasket } from '../shared/models/basket.model'; | |||
import { IBasketItem } from '../shared/models/basketItem.model'; | |||
@Component({ | |||
selector: 'esh-basket', | |||
styleUrls: ['./basket.component.scss'], | |||
templateUrl: './basket.component.html' | |||
}) | |||
export class BasketComponent implements OnInit { | |||
basket: IBasket; | |||
totalPrice: number = 0; | |||
constructor(private service: BasketService) { } | |||
ngOnInit() { | |||
this.basket = this.service.getBasket() | |||
this.service.basket.items.forEach(item => { | |||
this.totalPrice += (item.unitPrice * item.quantity) | |||
}); | |||
} | |||
itemQuantityChanged(item: IBasketItem) { | |||
this.calculateTotalPrice(); | |||
} | |||
private calculateTotalPrice() { | |||
this.basket = this.service.basket.items; | |||
this.service.basket.items.forEach(item => { | |||
this.totalPrice += (item.unitPrice * item.quantity) | |||
}); | |||
} | |||
} | |||
@ -0,0 +1,26 @@ | |||
import { NgModule, ModuleWithProviders } from '@angular/core'; | |||
import { BrowserModule } from '@angular/platform-browser'; | |||
import { SharedModule } from '../shared/shared.module'; | |||
import { BasketComponent } from './basket.component'; | |||
import { BasketStatusComponent } from './basket-status/basket-status.component'; | |||
//import { routing } from './basket.routes'; | |||
import { BasketService } from './basket.service'; | |||
@NgModule({ | |||
imports: [SharedModule], | |||
declarations: [BasketComponent, BasketStatusComponent], | |||
providers: [BasketService], | |||
exports: [BasketStatusComponent] | |||
}) | |||
export class BasketModule { | |||
static forRoot(): ModuleWithProviders { | |||
return { | |||
ngModule: BasketModule, | |||
providers: [ | |||
// Providers | |||
BasketService | |||
] | |||
}; | |||
} | |||
} |
@ -0,0 +1,9 @@ | |||
//import { Routes, RouterModule } from '@angular/router'; | |||
//import { BasketComponent } from './basket.component'; | |||
//const routes: Routes = [ | |||
// { path: '', component: BasketComponent } | |||
//]; | |||
//export const routing = RouterModule.forChild(routes); |
@ -0,0 +1,40 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { Response } from '@angular/http'; | |||
import { DataService } from '../shared/services/data.service'; | |||
import { IBasket } from '../shared/models/basket.model'; | |||
import { IBasketItem } from '../shared/models/basketItem.model'; | |||
import 'rxjs/Rx'; | |||
import { Observable } from 'rxjs/Observable'; | |||
import 'rxjs/add/observable/throw'; | |||
import { Observer } from 'rxjs/Observer'; | |||
import 'rxjs/add/operator/map'; | |||
@Injectable() | |||
export class BasketService { | |||
private basketUrl: string = 'http://eshopcontainers:5103'; | |||
basket: IBasket = { | |||
buyerId: 'fakeIdentity', | |||
items: [] | |||
}; | |||
constructor(private service: DataService) { | |||
this.basket.items = []; | |||
} | |||
setBasket(item) { | |||
this.basket.items.push(item); | |||
this.service.post(this.basket.buyerId, this.basket.items); | |||
} | |||
getBasket(): Observable<IBasket> { | |||
return this.service.get(this.basketUrl + '/' + this.basket.buyerId).map((response: Response) => { | |||
return response.json(); | |||
}); | |||
} | |||
dropBasket() { | |||
//TODO.. | |||
} | |||
} |
@ -1,27 +1,41 @@ | |||
<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"> | |||
<div class="catalog-banner"> | |||
<img class="catalog-banner-image" src="../../images/main_banner.png" /> | |||
<div class="container"> | |||
<select (change)="onBrandFilterChanged($event, $event.target.value)"> | |||
<option *ngFor="let brand of brands" [value]="brand.id">{{brand.brand}}</option> | |||
</select> | |||
<select (change)="onTypeFilterChanged($event, $event.target.value)"> | |||
<option *ngFor="let type of types" [value]="type.id">{{type.type}}</option> | |||
</select> | |||
<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"> | |||
<div data-name="brand" class="catalog-filter-wrapper"> | |||
<select class="catalog-filter-select" (change)="onBrandFilterChanged($event, $event.target.value)"> | |||
<option *ngFor="let brand of brands" [value]="brand.id">{{brand.brand}}</option> | |||
</select> | |||
</div> | |||
<div data-name="type" class="catalog-filter-wrapper"> | |||
<select class="catalog-filter-select" (change)="onTypeFilterChanged($event, $event.target.value)"> | |||
<option *ngFor="let type of types" [value]="type.id">{{type.type}}</option> | |||
</select> | |||
</div> | |||
<button class="catalog-filter-button" (click)="onFilterApplied($event)"> | |||
<img src="../../images/arrow-right.svg"> | |||
</button> | |||
</div> | |||
</div> | |||
<button class="" (click)="onFilterApplied($event)">Apply</button> | |||
</div> | |||
</div> | |||
<esh-pager [model]="paginationInfo" (changed)="onPageChanged($event)"></esh-pager> | |||
<div class="catalog-content row"> | |||
<div class="col-md-4 catalog-content-item" *ngFor="let item of catalog?.data"> | |||
<img src="{{item.pictureUri}}"/> | |||
<button class="catalog-content-item-button">[ ADD TO CART ]</button> | |||
<esh-pager [model]="paginationInfo" (changed)="onPageChanged($event)"></esh-pager> | |||
<div class="catalog-content row"> | |||
<div class="col-md-4 catalog-content-item" *ngFor="let item of catalog?.data"> | |||
<img src="{{item.pictureUri}}" /> | |||
<button (click)="addToCart(item)" class="catalog-content-item-button">[ ADD TO CART ]</button> | |||
<div class="catalog-content-item-title"> | |||
<span>{{item.name}}</span> | |||
</div> | |||
<div class="catalog-content-item-price"> | |||
<span>{{item.price}}</span> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<esh-pager [model]="paginationInfo" (changed)="onPageChanged($event)"></esh-pager> | |||
<esh-pager [model]="paginationInfo" (changed)="onPageChanged($event)"></esh-pager> | |||
</div> |
@ -1,9 +1,9 @@ | |||
import { Routes, RouterModule } from '@angular/router'; | |||
//import { Routes, RouterModule } from '@angular/router'; | |||
import { CatalogComponent } from './catalog.component'; | |||
//import { CatalogComponent } from './catalog.component'; | |||
const routes: Routes = [ | |||
{ path: 'catalog', component: CatalogComponent } | |||
]; | |||
//const routes: Routes = [ | |||
// { path: '', component: CatalogComponent } | |||
//]; | |||
export const routing = RouterModule.forChild(routes); | |||
//export const routing = RouterModule.forChild(routes); |
@ -0,0 +1,30 @@ | |||
.pager { | |||
margin-bottom: 20px; | |||
margin-top: 20px; | |||
margin-left: 0px; | |||
margin-right: -30px; | |||
&-previous { | |||
position: absolute; | |||
left: 0; | |||
top: 0; | |||
cursor: pointer; | |||
} | |||
&-text { | |||
color: #83d01b; | |||
} | |||
&-next { | |||
position: absolute; | |||
right: 15px; | |||
top: 0; | |||
cursor: pointer; | |||
} | |||
&-isDisabled { | |||
cursor: not-allowed; | |||
opacity: .5; | |||
pointer-events: none; | |||
} | |||
} |
@ -1,8 +0,0 @@ | |||
<footer class="text-muted"> | |||
<div class="container"> | |||
<hr> | |||
<p class="text-muted"> | |||
© 2015-2016 {{'title'}} Company | |||
</p> | |||
</div> | |||
</footer> |
@ -1,8 +0,0 @@ | |||
@import '../../_variables.scss'; | |||
.footer { | |||
padding-top: 40px; | |||
padding-bottom: 40px; | |||
margin-top: 40px; | |||
border-top: 1px solid #eee; | |||
} |
@ -1,10 +0,0 @@ | |||
import { Component } from '@angular/core'; | |||
@Component({ | |||
selector: 'appc-footer', | |||
styleUrls: ['./footer.component.scss'], | |||
templateUrl: './footer.component.html' | |||
}) | |||
export class FooterComponent { | |||
constructor() { } | |||
} |
@ -1,62 +0,0 @@ | |||
<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> |
@ -1,5 +0,0 @@ | |||
@import '../../_variables.scss'; | |||
.header-brand { | |||
background-image:url('../../../images/brand.png') | |||
} |
@ -1,16 +0,0 @@ | |||
import { Component, Inject } from '@angular/core'; | |||
import { Router } from '@angular/router'; | |||
@Component({ | |||
selector: 'appc-header', | |||
styleUrls: ['./header.component.scss'], | |||
templateUrl: './header.component.html' | |||
}) | |||
export class HeaderComponent { | |||
isCollapsed: boolean = true; | |||
constructor(private router: Router) { } | |||
toggleNav() { | |||
this.isCollapsed = !this.isCollapsed; | |||
} | |||
} |
@ -0,0 +1,6 @@ | |||
import { IBasketItem } from './basketItem.model'; | |||
export interface IBasket { | |||
items: IBasketItem[] | |||
buyerId: string | |||
} |
@ -0,0 +1,9 @@ | |||
export interface IBasketItem { | |||
id: string | |||
productId: string | |||
productName: string | |||
unitPrice: number, | |||
quantity: number, | |||
pictureUrl: string | |||
} | |||
@ -0,0 +1,28 @@ | |||
import { Injectable } from '@angular/core'; | |||
import { Subject } from 'rxjs/Subject'; | |||
import { ICatalogItem } from '../models/catalogItem.model'; | |||
import { IBasketItem } from '../models/basketItem.model'; | |||
@Injectable() | |||
export class BasketWrapperService { | |||
constructor() { } | |||
private addItemToBasketSource = new Subject<IBasketItem>(); | |||
addItemToBasket$ = this.addItemToBasketSource.asObservable(); | |||
addItemToBasket(item: ICatalogItem) { | |||
let basket: IBasketItem = { | |||
pictureUrl: item.pictureUri, | |||
productId: item.id, | |||
productName: item.name, | |||
quantity: 1, | |||
unitPrice: item.price, | |||
id: '' | |||
} | |||
this.addItemToBasketSource.next(basket); | |||
} | |||
} |