Skip to content

Commit 54e3f87

Browse files
committed
m1: initial store app
1 parent a047b20 commit 54e3f87

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+9482
-279
lines changed

package-lock.json

+764-119
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+26-16
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,37 @@
77
"build": "ng build",
88
"test": "ng test",
99
"lint": "ng lint",
10-
"e2e": "ng e2e"
10+
"e2e": "ng e2e",
11+
"tslint-check": "tslint-config-prettier-check ./tslint.json"
1112
},
1213
"private": true,
1314
"dependencies": {
14-
"@angular/animations": "^6.0.0",
15-
"@angular/common": "^6.0.0",
16-
"@angular/compiler": "^6.0.0",
17-
"@angular/core": "^6.0.0",
18-
"@angular/forms": "^6.0.0",
19-
"@angular/http": "^6.0.0",
20-
"@angular/platform-browser": "^6.0.0",
21-
"@angular/platform-browser-dynamic": "^6.0.0",
22-
"@angular/router": "^6.0.0",
15+
"@angular/animations": "^7.0.4",
16+
"@angular/cdk": "^7.0.4",
17+
"@angular/common": "^7.0.4",
18+
"@angular/compiler": "^7.0.4",
19+
"@angular/core": "^7.0.4",
20+
"@angular/forms": "^7.0.4",
21+
"@angular/http": "^7.0.4",
22+
"@angular/material": "^7.0.4",
23+
"@angular/platform-browser": "^7.0.4",
24+
"@angular/platform-browser-dynamic": "^7.0.4",
25+
"@angular/router": "^7.0.4",
26+
"@ngrx/effects": "^6.0.1",
27+
"@ngrx/entity": "^6.0.1",
28+
"@ngrx/router-store": "^6.0.1",
29+
"@ngrx/store": "^6.0.1",
30+
"@ngrx/store-devtools": "^6.0.1",
2331
"core-js": "^2.5.4",
24-
"rxjs": "^6.0.0",
32+
"rxjs": "^6.3.3",
33+
"tslint-config-prettier": "^1.16.0",
2534
"zone.js": "^0.8.26"
2635
},
2736
"devDependencies": {
28-
"@angular/compiler-cli": "^6.0.0",
2937
"@angular-devkit/build-angular": "~0.6.0",
30-
"typescript": "~2.7.2",
31-
"@angular/cli": "~6.0.0",
32-
"@angular/language-service": "^6.0.0",
38+
"@angular/cli": "~7.0.6",
39+
"@angular/compiler-cli": "^7.0.4",
40+
"@angular/language-service": "^7.0.4",
3341
"@types/jasmine": "~2.8.6",
3442
"@types/jasminewd2": "~2.0.3",
3543
"@types/node": "~8.9.4",
@@ -41,8 +49,10 @@
4149
"karma-coverage-istanbul-reporter": "~1.4.2",
4250
"karma-jasmine": "~1.1.1",
4351
"karma-jasmine-html-reporter": "^0.2.2",
52+
"prettier": "1.13.7",
4453
"protractor": "~5.3.0",
4554
"ts-node": "~5.0.1",
46-
"tslint": "~5.9.1"
55+
"tslint": "~5.9.1",
56+
"typescript": "~3.1.6"
4757
}
4858
}

prettier.config.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
printWidth: 80,
3+
singleQuote: true,
4+
trailingComma: 'es5',
5+
};

src/app/app.component.css

Whitespace-only changes.

src/app/app.component.html

+13-20
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
1-
<!--The content below is only a placeholder and can be replaced.-->
2-
<div style="text-align:center">
3-
<h1>
4-
Welcome to {{ title }}!
5-
</h1>
6-
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
7-
</div>
8-
<h2>Here are some links to help you start: </h2>
9-
<ul>
10-
<li>
11-
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
12-
</li>
13-
<li>
14-
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
15-
</li>
16-
<li>
17-
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
18-
</li>
19-
</ul>
20-
1+
<header>
2+
<mat-toolbar color="primary">
3+
<a
4+
routerLink="home"
5+
class="fill">
6+
NgRx Workshop Store
7+
</a>
8+
<app-cart></app-cart>
9+
</mat-toolbar>
10+
</header>
11+
<main>
12+
<router-outlet></router-outlet>
13+
</main>

src/app/app.component.scss

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
header {
2+
position: fixed;
3+
top: 0px;
4+
width: 100vw;
5+
z-index: 10;
6+
7+
app-cart {
8+
margin: 0 20px;
9+
}
10+
}
11+
12+
main {
13+
margin-top: 64px;
14+
}
15+
16+
mat-toolbar {
17+
display: flex;
18+
19+
.fill {
20+
flex: 1;
21+
}
22+
23+
a {
24+
color: inherit;
25+
text-decoration: none;
26+
}
27+
}

src/app/app.component.spec.ts

-27
This file was deleted.

src/app/app.component.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { Component } from '@angular/core';
33
@Component({
44
selector: 'app-root',
55
templateUrl: './app.component.html',
6-
styleUrls: ['./app.component.css']
6+
styleUrls: ['./app.component.scss'],
77
})
8-
export class AppComponent {
9-
title = 'app';
10-
}
8+
export class AppComponent {}

src/app/app.module.ts

+18-8
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1-
import { BrowserModule } from '@angular/platform-browser';
1+
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
22
import { NgModule } from '@angular/core';
33

44
import { AppComponent } from './app.component';
5+
import { HomeModule } from './home/home.module';
6+
import { RoutingModule } from './routing.module';
7+
import { CartModule } from './cart/cart.module';
8+
import { MatToolbarModule, MatIconModule } from '@angular/material';
9+
import { ProductDetailsModule } from './product-details/product-details.module';
10+
import { CartDetailsModule } from './cart-details/cart-details.module';
511

612
@NgModule({
7-
declarations: [
8-
AppComponent
9-
],
13+
declarations: [AppComponent],
1014
imports: [
11-
BrowserModule
15+
BrowserAnimationsModule,
16+
HomeModule,
17+
RoutingModule,
18+
CartDetailsModule,
19+
CartModule,
20+
ProductDetailsModule,
21+
MatIconModule,
22+
MatToolbarModule,
1223
],
13-
providers: [],
14-
bootstrap: [AppComponent]
24+
bootstrap: [AppComponent],
1525
})
16-
export class AppModule { }
26+
export class AppModule {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<ng-container *ngIf="cartProducts$ | async as cartProducts; else loading">
2+
<ng-container *ngIf="cartProducts.length; else noProducts">
3+
<mat-card *ngFor="let product of cartProducts">
4+
<mat-card-content>
5+
<img [src]="product.url">
6+
<div class="fill">
7+
<mat-card-title>{{product.title}}</mat-card-title>
8+
<mat-card-subtitle>Quantity: {{product.quantity}}</mat-card-subtitle>
9+
</div>
10+
<div class="details">
11+
<div>Price: {{product.price | currency}}</div>
12+
<button
13+
mat-button
14+
(click)="removeOne(product.id)">
15+
Remove
16+
</button>
17+
</div>
18+
</mat-card-content>
19+
</mat-card>
20+
<footer>
21+
<button
22+
type="button"
23+
mat-button
24+
(click)="removeAll()">
25+
Remove All
26+
</button>
27+
<div class="fill"></div>
28+
<button
29+
mat-raised-button
30+
color="primary"
31+
(click)="purchase(cartProducts)">
32+
Purchase
33+
</button>
34+
<div class="total">
35+
Total: {{(total$ | async) || 0 | currency}}
36+
</div>
37+
</footer>
38+
</ng-container>
39+
</ng-container>
40+
41+
<ng-template #loading>Loading...</ng-template>
42+
43+
<ng-template #noProducts>No products here yet</ng-template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
mat-card {
2+
height: 120px;
3+
}
4+
5+
.fill {
6+
margin: 20px;
7+
flex: 1;
8+
}
9+
10+
mat-card-content {
11+
display: flex;
12+
13+
img {
14+
height: 120px;
15+
}
16+
17+
.details {
18+
display: flex;
19+
flex-direction: column;
20+
justify-content: space-between;
21+
align-items: center;
22+
}
23+
}
24+
25+
footer {
26+
display: flex;
27+
align-items: center;
28+
29+
.total {
30+
margin: 0 20px;
31+
font-size: 20px;
32+
line-height: 32px;
33+
font-weight: 500;
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Component } from '@angular/core';
2+
import { combineLatest, Observable } from 'rxjs';
3+
import { map } from 'rxjs/operators';
4+
5+
import { CartProduct } from '../model/product';
6+
import { CartService } from '../services/cart.service';
7+
import { ProductService } from '../services/product.service';
8+
9+
@Component({
10+
selector: 'app-cart-details',
11+
templateUrl: './cart-details.component.html',
12+
styleUrls: ['./cart-details.component.scss'],
13+
})
14+
export class CartDetailsComponent {
15+
cartProducts$: Observable<CartProduct[]> = combineLatest(
16+
this.cartService.cartItemsIds$,
17+
this.productService.getProducts(),
18+
(ids, products) => ({ ids, products })
19+
).pipe(
20+
map(({ ids, products }) => {
21+
// Reduce ids array to id:quantity Indexable
22+
const idsMap = ids.reduce((acc, id) => {
23+
const currentQuantity = acc[id] || 0;
24+
acc[id] = currentQuantity + 1;
25+
return acc;
26+
}, {});
27+
28+
// Fill each id with quantity and product info.
29+
return Object.keys(idsMap).map(id => ({
30+
...products.find(p => p.id === id),
31+
quantity: idsMap[id],
32+
}));
33+
})
34+
);
35+
36+
total$ = this.cartProducts$.pipe(
37+
map(cartProducts =>
38+
cartProducts.reduce(
39+
(acc, product) => acc + product.price * product.quantity,
40+
0
41+
)
42+
)
43+
);
44+
45+
constructor(
46+
private readonly cartService: CartService,
47+
private readonly productService: ProductService
48+
) {}
49+
50+
removeOne(id: string) {
51+
this.cartService.removeOne(id);
52+
}
53+
54+
removeAll() {
55+
this.cartService.removeAll();
56+
}
57+
58+
purchase(products: CartProduct[]) {
59+
this.cartService.purchase(
60+
products.map(p => ({ id: p.id, quantity: p.quantity }))
61+
);
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { CommonModule } from '@angular/common';
2+
import { NgModule } from '@angular/core';
3+
import { MatButtonModule, MatCardModule } from '@angular/material';
4+
5+
import { CartDetailsComponent } from './cart-details.component';
6+
7+
@NgModule({
8+
imports: [CommonModule, MatCardModule, MatButtonModule],
9+
declarations: [CartDetailsComponent],
10+
})
11+
export class CartDetailsModule {}

src/app/cart/cart.component.html

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<button
2+
type="button"
3+
mat-icon-button
4+
routerLink="cart">
5+
<mat-icon>shopping_cart</mat-icon>
6+
<span class="counter-background"></span>
7+
<span class="counter">{{(cartItemsCounter$ | async) || 0}}</span>
8+
</button>

src/app/cart/cart.component.scss

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.counter {
2+
cursor: pointer;
3+
position: absolute;
4+
height: 16px;
5+
width: 16px;
6+
top: -10px;
7+
left: 24px;
8+
9+
&-background {
10+
background: rgba(255, 0, 0, 0.9);
11+
position: absolute;
12+
border-radius: 50%;
13+
height: 20px;
14+
width: 20px;
15+
left: 22px;
16+
top: -1px;
17+
}
18+
}

0 commit comments

Comments
 (0)