Javier Suárez Ruiz 8 years ago
parent
commit
e13c4f09e6
52 changed files with 4405 additions and 252 deletions
  1. +9
    -2
      src/Services/Catalog/Catalog.API/Startup.cs
  2. +1
    -1
      src/Services/Catalog/Catalog.API/docker-compose.yml
  3. +1
    -1
      src/Services/Catalog/Catalog.API/settings.json
  4. +2
    -2
      src/Web/WebMVC/docker-compose.yml
  5. +1
    -0
      src/Web/WebMVC/wwwroot/css/site.css
  6. BIN
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Bold.eot
  7. +1933
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Bold.svg
  8. BIN
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Bold.ttf
  9. BIN
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Bold.woff
  10. BIN
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Bold.woff2
  11. BIN
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Regular.eot
  12. +1743
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Regular.svg
  13. BIN
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Regular.ttf
  14. BIN
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Regular.woff
  15. BIN
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Regular.woff2
  16. +1
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/images/arrow-right.svg
  17. BIN
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/images/cart.png
  18. +37
    -10
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html
  19. +47
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss
  20. +3
    -1
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.module.ts
  21. +11
    -9
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.routes.ts
  22. +8
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.html
  23. +24
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.scss
  24. +29
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.ts
  25. +63
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.html
  26. +80
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.scss
  27. +36
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.ts
  28. +26
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.module.ts
  29. +9
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.routes.ts
  30. +40
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.service.ts
  31. +37
    -23
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.html
  32. +81
    -10
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.scss
  33. +10
    -4
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.ts
  34. +3
    -5
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.module.ts
  35. +6
    -6
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.routes.ts
  36. +6
    -4
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/pager/pager.html
  37. +30
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/pager/pager.scss
  38. +18
    -3
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/pager/pager.ts
  39. +0
    -8
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/footer.component.html
  40. +0
    -8
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/footer.component.scss
  41. +0
    -10
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/footer.component.ts
  42. +0
    -62
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/header.component.html
  43. +0
    -5
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/header.component.scss
  44. +0
    -16
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/header.component.ts
  45. +6
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/basket.model.ts
  46. +9
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/basketItem.model.ts
  47. +2
    -1
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/pager.model.ts
  48. +28
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/basket.wrapper.service.ts
  49. +50
    -52
      src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/shared.module.ts
  50. +14
    -9
      src/Web/WebSPA/eShopOnContainers.WebSPA/config/webpack.config.js
  51. +1
    -0
      src/Web/WebSPA/eShopOnContainers.WebSPA/package.json
  52. BIN
      src/Web/WebSPA/eShopOnContainers.WebSPA/wwwroot/favicon.ico

+ 9
- 2
src/Services/Catalog/Catalog.API/Startup.cs View File

@ -53,7 +53,14 @@
});
});
services.AddCors();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddMvc(mvcoptions =>
{
@ -78,7 +85,7 @@
.Wait();
// Use frameworks
app.UseCors(policyBuilder => policyBuilder.AllowAnyOrigin());
app.UseCors("CorsPolicy");
app.UseMvc();


+ 1
- 1
src/Services/Catalog/Catalog.API/docker-compose.yml View File

@ -16,7 +16,7 @@ services:
- catalog.data
catalog.data:
image: eshop/mssql-server-private-preview
image: microsoft/mssql-server-linux
environment:
- SA_PASSWORD=Pass@word
- ACCEPT_EULA=Y


+ 1
- 1
src/Services/Catalog/Catalog.API/settings.json View File

@ -1,5 +1,5 @@
{
"ConnectionString": "Server=tcp:127.0.0.1,1455;Initial Catalog=CatalogDB;User Id=sa;Password=Pass@word",
"ConnectionString": "Server=tcp:127.0.0.1,1433;Initial Catalog=CatalogDB;User Id=sa;Password=Pass@word",
"Logging": {
"IncludeScopes": false,
"LogLevel": {


+ 2
- 2
src/Web/WebMVC/docker-compose.yml View File

@ -27,7 +27,7 @@ services:
- catalog.data
catalog.data:
image: eshop/mssql-server-private-preview
image: microsoft/mssql-server-linux
environment:
- SA_PASSWORD=Pass@word
- ACCEPT_EULA=Y
@ -55,7 +55,7 @@ services:
- "5432:1433"
identity.data:
image: eshop/mssql-server-private-preview
image: microsoft/mssql-server-linux
environment:
- SA_PASSWORD=Pass@word
- ACCEPT_EULA=Y


+ 1
- 0
src/Web/WebMVC/wwwroot/css/site.css View File

@ -825,6 +825,7 @@ form .col-md-4 {
text-transform: uppercase;
}
.navbar-nav {
margin-top: 10px;
margin-bottom: 7.5px;


BIN
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Bold.eot View File


+ 1933
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Bold.svg
File diff suppressed because it is too large
View File


BIN
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Bold.ttf View File


BIN
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Bold.woff View File


BIN
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Bold.woff2 View File


BIN
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Regular.eot View File


+ 1743
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Regular.svg
File diff suppressed because it is too large
View File


BIN
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Regular.ttf View File


BIN
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Regular.woff View File


BIN
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/fonts/Montserrat-Regular.woff2 View File


+ 1
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/images/arrow-right.svg View File

@ -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>

BIN
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/images/cart.png View File

Before After
Width: 43  |  Height: 37  |  Size: 1.5 KiB

+ 37
- 10
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html View File

@ -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>

+ 47
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss View File

@ -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;
}
}
}

+ 3
- 1
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.module.ts View File

@ -9,6 +9,7 @@ import { AppService } from './app.service';
import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';
import { CatalogModule } from './catalog/catalog.module';
import { BasketModule } from './basket/basket.module';
@NgModule({
declarations: [AppComponent],
@ -18,7 +19,8 @@ import { CatalogModule } from './catalog/catalog.module';
HttpModule,
// Only module that app module loads
SharedModule.forRoot(),
CatalogModule
CatalogModule,
BasketModule
],
providers: [
AppService


+ 11
- 9
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.routes.ts View File

@ -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);

+ 8
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.html View File

@ -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>

+ 24
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.scss View File

@ -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;
}
}

+ 29
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.ts View File

@ -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;
});
}
}

+ 63
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.html View File

@ -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>

+ 80
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.scss View File

@ -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;
}

+ 36
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.ts View File

@ -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)
});
}
}

+ 26
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.module.ts View File

@ -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
]
};
}
}

+ 9
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.routes.ts View File

@ -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);

+ 40
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.service.ts View File

@ -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..
}
}

+ 37
- 23
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.html View File

@ -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>

+ 81
- 10
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.scss View File

@ -1,9 +1,13 @@
@import '../_variables.scss';
.catalog{
select::-ms-expand {
display: none;
}
.catalog {
&-banner {
height: 258px;
vertical-align:middle;
vertical-align: middle;
&-image {
width: 100%;
@ -13,7 +17,7 @@
}
&-text {
position:relative;
position: relative;
top: 75px;
}
}
@ -22,22 +26,70 @@
height: 65px;
&-container {
position:absolute;
width:100%;
position: absolute;
width: 100%;
background-color: $primary-colour;
left:0;
left: 0;
height: 65px;
}
&-button {
width: 45px;
height: 45px;
padding: 6px 20px 10px 8px;
background-color: $primary-accent;
color: white;
font-size: 16px;
margin: 10px 0;
border: none;
position: absolute;
}
&-select {
background-color: transparent;
padding: 10px;
margin: 10px;
margin-right: 20px;
color: #fff;
padding-top: 20px;
padding-bottom: 3px;
min-width: 140px;
border-color: #37c7ca;
max-height: 43px;
-webkit-appearance: none;
& option {
background-color: #00a69c;
}
}
&-wrapper {
z-index: 0;
display: inline-block;
margin-left: -10px;
}
&-wrapper::before {
content: attr(data-name);
opacity: 0.5;
z-index: 1;
text-transform: uppercase;
position: absolute;
font-size: 10px;
margin-top: 15px;
margin-left: 21px;
color: white;
}
}
&-content{
&-content {
margin-top: 10px;
&-item {
text-align: center;
margin-bottom: 20px;
&-image{
&-image {
}
&-button {
@ -48,7 +100,26 @@
color: white;
font-size: 16px;
margin: 10px 0;
border:none;
border: none;
cursor: pointer;
}
&-title {
text-align: center;
text-transform: uppercase;
font-weight: 300;
font-size: 16px;
/*margin-top: 20px;*/
}
&-price {
text-align: center;
font-weight: 900;
font-size: 28px;
}
&-price::before {
content: '$';
}
}
}


+ 10
- 4
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.ts View File

@ -5,6 +5,7 @@ import { ICatalogItem } from '../shared/models/catalogItem.model';
import { ICatalogType } from '../shared/models/catalogType.model';
import { ICatalogBrand } from '../shared/models/catalogBrand.model';
import { IPager } from '../shared/models/pager.model';
import { BasketWrapperService} from '../shared/services/basket.wrapper.service';
@Component({
selector: 'esh-catalog',
@ -19,11 +20,11 @@ export class CatalogComponent implements OnInit {
typeSelected: number;
paginationInfo: IPager;
constructor(private service: CatalogService) { }
constructor(private service: CatalogService, private basketService: BasketWrapperService) { }
ngOnInit() {
this.getBrands();
this.getCatalog(10,0);
this.getCatalog(10, 0);
this.getTypes();
}
@ -49,8 +50,12 @@ export class CatalogComponent implements OnInit {
this.getCatalog(this.paginationInfo.itemsPage, value);
}
addToCart(item: ICatalogItem) {
this.basketService.addItemToBasket(item);
}
getCatalog(pageSize:number, pageIndex: number, brand?: number, type?: number) {
this.service.getCatalog(brand, type).subscribe(catalog => {
this.service.getCatalog(pageIndex, pageSize, brand, type).subscribe(catalog => {
this.catalog = catalog;
console.log('catalog items retrieved: ' + catalog.count);
@ -58,7 +63,8 @@ export class CatalogComponent implements OnInit {
actualPage : catalog.pageIndex,
itemsPage : catalog.pageSize,
totalItems : catalog.count,
totalPages : (catalog.count / catalog.pageSize)
totalPages: Math.ceil(catalog.count / catalog.pageSize),
items: catalog.pageSize
};
console.log(this.paginationInfo);


+ 3
- 5
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.module.ts View File

@ -3,15 +3,13 @@ import { BrowserModule } from '@angular/platform-browser';
import { SharedModule } from '../shared/shared.module';
import { CatalogComponent } from './catalog.component';
import { routing } from './catalog.routes';
//import { routing } from './catalog.routes';
import { CatalogService } from './catalog.service';
import { Pager } from '../shared/components/pager/pager';
@NgModule({
imports: [routing, BrowserModule, SharedModule],
declarations: [CatalogComponent],
imports: [BrowserModule, SharedModule],
declarations: [CatalogComponent],
providers: [CatalogService]
})
export class CatalogModule { }

+ 6
- 6
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.routes.ts View File

@ -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);

+ 6
- 4
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/pager/pager.html View File

@ -1,9 +1,10 @@
<div class="row">
<div class="row pager">
<div class="col-xs-4">
<nav>
<ul>
<li class="page-item">
<span class="text previous" id="Previous"
<span class="pager-text pager-previous" id="Previous"
[hidden]="buttonStates?.previousDisabled"
(click)="onPreviousCliked($event)"
aria-label="Previous">
Previous
@ -12,12 +13,13 @@
</ul>
</nav>
</div>
<div class="col-xs-4 u-align-center"><span>Showing {{model?.itemsPage}} of {{model?.totalItems}} products - Page {{model?.actualPage}} - {{model?.totalPages}}</span></div>
<div class="col-xs-4 u-align-center"><span>Showing {{model?.items}} of {{model?.totalItems}} products - Page {{model?.actualPage + 1}} - {{model?.totalPages}}</span></div>
<div class="col-xs-4">
<nav>
<ul>
<li class="page-item">
<span class="text next" id="Next"
<span class="pager-text pager-next" id="Next"
[hidden]="buttonStates?.nextDisabled"
(click)="onNextClicked($event)"
aria-label="Next">
Next


+ 30
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/pager/pager.scss View File

@ -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;
}
}

+ 18
- 3
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/pager/pager.ts View File

@ -1,4 +1,4 @@
import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
import { Component, OnInit, OnChanges, Output, Input, EventEmitter } from '@angular/core';
import { IPager } from '../../models/pager.model';
@ -7,7 +7,7 @@ import { IPager } from '../../models/pager.model';
templateUrl: './pager.html',
styleUrls: ['./pager.scss']
})
export class Pager implements OnInit {
export class Pager implements OnInit, OnChanges {
@Output()
changed: EventEmitter<number> = new EventEmitter<number>();
@ -15,18 +15,33 @@ export class Pager implements OnInit {
@Input()
model: IPager;
buttonStates: any = {
nextDisabled: true,
previousDisabled: true,
}
ngOnInit() {
console.log(this.model);
}
ngOnChanges() {
if (this.model) {
this.model.items = (this.model.itemsPage > this.model.totalItems) ? this.model.totalItems : this.model.itemsPage;
this.buttonStates.previousDisabled = (this.model.actualPage == 0);
this.buttonStates.nextDisabled = (this.model.actualPage + 1 >= this.model.totalPages);
}
}
onNextClicked(event: any) {
event.preventDefault();
console.log('Pager Next Clicked');
console.log('Pager next clicked');
this.changed.emit(this.model.actualPage + 1);
}
onPreviousCliked(event: any) {
event.preventDefault();
console.log('Pager previous clicked');
this.changed.emit(this.model.actualPage - 1);
}


+ 0
- 8
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/footer.component.html View File

@ -1,8 +0,0 @@
<footer class="text-muted">
<div class="container">
<hr>
<p class="text-muted">
&copy; 2015-2016 {{'title'}} Company
</p>
</div>
</footer>

+ 0
- 8
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/footer.component.scss View File

@ -1,8 +0,0 @@
@import '../../_variables.scss';
.footer {
padding-top: 40px;
padding-bottom: 40px;
margin-top: 40px;
border-top: 1px solid #eee;
}

+ 0
- 10
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/footer.component.ts View File

@ -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() { }
}

+ 0
- 62
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/header.component.html View File

@ -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>

+ 0
- 5
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/header.component.scss View File

@ -1,5 +0,0 @@
@import '../../_variables.scss';
.header-brand {
background-image:url('../../../images/brand.png')
}

+ 0
- 16
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/layout/header.component.ts View File

@ -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;
}
}

+ 6
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/basket.model.ts View File

@ -0,0 +1,6 @@
import { IBasketItem } from './basketItem.model';
export interface IBasket {
items: IBasketItem[]
buyerId: string
}

+ 9
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/basketItem.model.ts View File

@ -0,0 +1,9 @@
export interface IBasketItem {
id: string
productId: string
productName: string
unitPrice: number,
quantity: number,
pictureUrl: string
}

+ 2
- 1
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/pager.model.ts View File

@ -2,5 +2,6 @@ export interface IPager {
itemsPage: number,
totalItems: number,
actualPage: number,
totalPages: number
totalPages: number,
items: number,
}

+ 28
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/basket.wrapper.service.ts View File

@ -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);
}
}

+ 50
- 52
src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/shared.module.ts View File

@ -12,68 +12,66 @@ 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 { UtilityService } from './services/utility.service';
import { UppercasePipe } from './pipes/uppercase.pipe';
import { BasketWrapperService} from './services/basket.wrapper.service';
//Components:
import {Pager } from './components/pager/pager';
@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule,
NgbModule.forRoot(),
// No need to export as these modules don't expose any components/directive etc'
HttpModule,
JsonpModule
],
declarations: [
DynamicFormComponent,
DynamicFormControlComponent,
ErrorMessageComponent,
ErrorSummaryComponent,
FooterComponent,
HeaderComponent,
PageHeadingComponent,
UppercasePipe,
Pager
],
exports: [
// Modules
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule,
NgbModule,
// Providers, Components, directive, pipes
DynamicFormComponent,
DynamicFormControlComponent,
ErrorSummaryComponent,
ErrorMessageComponent,
FooterComponent,
HeaderComponent,
PageHeadingComponent,
UppercasePipe,
Pager
]
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule,
NgbModule.forRoot(),
// No need to export as these modules don't expose any components/directive etc'
HttpModule,
JsonpModule
],
declarations: [
DynamicFormComponent,
DynamicFormControlComponent,
ErrorMessageComponent,
ErrorSummaryComponent,
PageHeadingComponent,
UppercasePipe,
Pager
],
exports: [
// Modules
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule,
NgbModule,
// Providers, Components, directive, pipes
DynamicFormComponent,
DynamicFormControlComponent,
ErrorSummaryComponent,
ErrorMessageComponent,
//FooterComponent,
//HeaderComponent,
PageHeadingComponent,
UppercasePipe,
Pager
]
})
export class SharedModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: SharedModule,
providers: [
// Providers
DataService,
FormControlService,
UtilityService
]
};
}
static forRoot(): ModuleWithProviders {
return {
ngModule: SharedModule,
providers: [
// Providers
DataService,
FormControlService,
UtilityService,
BasketWrapperService
]
};
}
}

+ 14
- 9
src/Web/WebSPA/eShopOnContainers.WebSPA/config/webpack.config.js View File

@ -16,31 +16,36 @@ module.exports = merge({
},
module: {
rules: [
{ test: /\.ts$/, exclude: [/\.(spec|e2e)\.ts$/], loaders: ['awesome-typescript-loader?forkChecker=true ', 'angular2-template-loader'] },
{
test: /\.ts$/, exclude: [/\.(spec|e2e)\.ts$/],
loaders: ['awesome-typescript-loader?forkChecker=true ', 'angular2-template-loader', 'angular2-router-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"
loader: "file"
}, {
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
loader: "url?limit=10000&mimetype=application/font-woff"
loader: "file"
}, {
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
loader: "url?limit=10000&mimetype=application/octet-stream"
loader: "file"
}, {
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)$/,
test: /\.(png|jpg|gif|svg)$/,
loader: "file"
}
// {
// test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
//loader: "url?limit=10000&mimetype=image/svg+xml"
//}
]
},
entry: {


+ 1
- 0
src/Web/WebSPA/eShopOnContainers.WebSPA/package.json View File

@ -72,6 +72,7 @@
"@types/uglify-js": "2.6.28",
"@types/webpack": "1.12.35",
"angular2-template-loader": "0.6.0",
"angular2-router-loader": "0.3.4",
"awesome-typescript-loader": "2.2.4",
"codelyzer": "1.0.0-beta.3",
"copy-webpack-plugin": "^4.0.0",


BIN
src/Web/WebSPA/eShopOnContainers.WebSPA/wwwroot/favicon.ico View File

Before After

Loading…
Cancel
Save