9 |
10 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quaerat quo autem
11 | ut facilis velit, laboriosam nihil, voluptates cum neque asperiores commodi
12 | sequi vel eveniet iure dicta suscipit sit inventore molestiae.
13 |
14 |
`,
15 | styleUrls: ['./thank-you-page.component.scss']
16 | })
17 | export class ThankYouPageComponent {
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/pages/checkout/thank-you-page/thank-you-page.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | import { ThankYouPageRoutingModule } from './thank-you-page-routing.module';
5 | import { ThankYouPageComponent } from './thank-you-page.component';
6 |
7 |
8 | @NgModule({
9 | declarations: [
10 | ThankYouPageComponent
11 | ],
12 | imports: [
13 | CommonModule,
14 | ThankYouPageRoutingModule
15 | ]
16 | })
17 | export class ThankYouPageModule { }
18 |
--------------------------------------------------------------------------------
/src/app/pages/products/interfaces/product.interface.ts:
--------------------------------------------------------------------------------
1 | export interface Product {
2 | id: number;
3 | name: string;
4 | price: number;
5 | description: string;
6 | categoryId: number;
7 | stock: number;
8 | qty: number;
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/pages/products/product/product.component.html:
--------------------------------------------------------------------------------
1 |
2 | {{ product.name }}
3 | {{ product.description }}
4 |
5 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/app/pages/products/product/product.component.scss:
--------------------------------------------------------------------------------
1 | .card {
2 | display: flex;
3 | flex-direction: column;
4 | margin: 1rem;
5 | max-width: 25rem;
6 | min-height: 10rem;
7 | min-width: 25rem;
8 | }
9 |
10 | .mat-card-title {
11 | line-height: 1.2;
12 | }
13 |
14 | .mat-card-actions {
15 | margin: 0;
16 | }
17 |
18 | .mat-card > .mat-card-actions:last-child {
19 | margin-bottom: 0;
20 | margin-top: auto;
21 | }
22 |
23 | button {
24 | width: 100%;
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/pages/products/product/product.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
2 | import { Product } from '../interfaces/product.interface';
3 |
4 | @Component({
5 | selector: 'app-product',
6 | templateUrl: './product.component.html',
7 | styleUrls: ['./product.component.scss'],
8 | changeDetection: ChangeDetectionStrategy.OnPush
9 | })
10 | export class ProductComponent {
11 | @Input() product!: Product;
12 | @Output() addToCartClick = new EventEmitter();
13 |
14 |
15 | onClick(): void {
16 | this.addToCartClick.emit(this.product);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/pages/products/products-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 | import { ProductsComponent } from './products.component';
4 |
5 | const routes: Routes = [{ path: '', component: ProductsComponent }];
6 |
7 | @NgModule({
8 | imports: [RouterModule.forChild(routes)],
9 | exports: [RouterModule]
10 | })
11 | export class ProductsRoutingModule { }
12 |
--------------------------------------------------------------------------------
/src/app/pages/products/products.component.scss:
--------------------------------------------------------------------------------
1 |
2 | .products {
3 | align-items: center;
4 | display: flex;
5 | flex-wrap: wrap;
6 | justify-content: center;
7 | margin-top: 6rem;
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/src/app/pages/products/products.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { ProductsService } from './services/products.service';
3 | import { tap } from 'rxjs/operators';
4 | import { Product } from './interfaces/product.interface';
5 | import { ShoppingCartService } from 'src/app/shared/services/shopping-cart.service';
6 |
7 | @Component({
8 | selector: 'app-products',
9 | template: `
10 |
11 |
16 |
17 | `,
18 | styleUrls: ['./products.component.scss']
19 | })
20 | export class ProductsComponent implements OnInit {
21 | products!: Product[];
22 |
23 | constructor(private productSvc: ProductsService, private shoppingCartSvc: ShoppingCartService) { }
24 |
25 | ngOnInit(): void {
26 | this.productSvc.getProducts()
27 | .pipe(
28 | tap((products: Product[]) => this.products = products)
29 | )
30 | .subscribe();
31 | }
32 |
33 | addToCart(product: Product): void {
34 | console.log('Add to cart', product);
35 | this.shoppingCartSvc.updateCart(product);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/pages/products/products.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | import { ProductsRoutingModule } from './products-routing.module';
5 | import { ProductsComponent } from './products.component';
6 | import { ProductComponent } from './product/product.component';
7 |
8 | import { MaterialModule } from './../../material.module';
9 |
10 |
11 | @NgModule({
12 | declarations: [
13 | ProductsComponent,
14 | ProductComponent
15 | ],
16 | imports: [
17 | CommonModule,
18 | ProductsRoutingModule,
19 | MaterialModule
20 | ]
21 | })
22 | export class ProductsModule { }
23 |
--------------------------------------------------------------------------------
/src/app/pages/products/services/products.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Injectable } from '@angular/core';
3 | import { Observable } from 'rxjs';
4 | import { Product } from '../interfaces/product.interface';
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class ProductsService {
10 | private apiURl = 'http://localhost:3000/products';
11 | constructor(private http: HttpClient) { }
12 |
13 | getProducts(): Observable {
14 | return this.http.get(this.apiURl);
15 | }
16 |
17 | updateStock(productId: number, stock: number): Observable {
18 | const body = { "stock": stock };
19 | return this.http.patch(`${this.apiURl}/${productId}`, body);
20 | }
21 |
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/shared/components/cart/cart.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from "@angular/core";
2 | import { ShoppingCartService } from 'src/app/shared/services/shopping-cart.service';
3 | @Component({
4 | selector: 'app-cart',
5 | template: `
6 |
7 |
8 | add_shopping_cart
9 | {{dataCart.total | currency}}
10 | ({{dataCart.quantity}})
11 |
12 |
13 | `
14 | })
15 | export class CartComponent {
16 |
17 | quantity$ = this.shoppingCartSvc.quantityAction$;
18 | total$ = this.shoppingCartSvc.totalAction$;
19 | constructor(private shoppingCartSvc: ShoppingCartService) { }
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/shared/components/header/header.component.scss:
--------------------------------------------------------------------------------
1 | .main-header {
2 | display: flex;
3 | justify-content: space-between;
4 | left: 0;
5 | position: fixed;
6 | top: 0;
7 | z-index: 1;
8 | }
9 |
10 | a {
11 | color: #fff;
12 | text-decoration: none;
13 | }
14 |
15 | .shopping-cart {
16 | align-items: center;
17 | border-radius: .2rem;
18 | cursor: pointer;
19 | display: flex;
20 | padding: .1rem .5rem;
21 | transition: background-color .25s ease-in-out;
22 |
23 | &:hover {
24 | background-color: rgb(4,172, 194);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/shared/components/header/header.component.ts:
--------------------------------------------------------------------------------
1 |
2 | import { Component } from '@angular/core';
3 | import { Router } from '@angular/router';
4 |
5 | @Component({
6 | selector: 'app-header',
7 | template: `
8 |
9 | My Store
10 |
11 | `,
12 | styleUrls: ['./header.component.scss']
13 | })
14 | export class HeaderComponent {
15 | constructor(private router: Router) { }
16 |
17 | goToCheckout(): void {
18 | this.router.navigate(['/checkout']);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/shared/interfaces/order.interface.ts:
--------------------------------------------------------------------------------
1 | export interface Details {
2 | productId: number;
3 | productName: string;
4 | quantity: number;
5 | }
6 |
7 | export interface Order {
8 | name: string;
9 | shippingAddress: string;
10 | city: string;
11 | date: string;
12 | isDelivery: boolean;
13 | id: number;
14 | }
15 |
16 | export interface DetailsOrder {
17 | details: Details[];
18 | orderId: number;
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/shared/interfaces/stores.interface.ts:
--------------------------------------------------------------------------------
1 | export interface Store {
2 | id: number;
3 | name: string;
4 | address: string;
5 | city: string;
6 | openingHours: string;
7 | }
8 |
--------------------------------------------------------------------------------
/src/app/shared/services/data.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Injectable } from '@angular/core';
3 | import { Observable } from 'rxjs';
4 | import { Order, DetailsOrder } from '../interfaces/order.interface';
5 | import { Store } from '../interfaces/stores.interface';
6 |
7 | @Injectable({
8 | providedIn: 'root'
9 | })
10 |
11 | export class DataService {
12 | private apiURL = 'http://localhost:3000';
13 | constructor(private http: HttpClient) { }
14 |
15 | getStores(): Observable {
16 | return this.http.get(`${this.apiURL}/stores`)
17 | }
18 |
19 | saveOrder(order: Order): Observable {
20 | return this.http.post(`${this.apiURL}/orders`, order);
21 | }
22 |
23 | saveDetailsOrder(details: DetailsOrder): Observable {
24 | return this.http.post(`${this.apiURL}/detailsOrders`, details);
25 | }
26 |
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/shared/services/shopping-cart.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable, BehaviorSubject } from 'rxjs';
3 | import { Product } from 'src/app/pages/products/interfaces/product.interface';
4 | @Injectable({
5 | providedIn: 'root'
6 | })
7 | export class ShoppingCartService {
8 | products: Product[] = [];
9 |
10 | private cartSubject = new BehaviorSubject([]);
11 | private totalSubject = new BehaviorSubject(0);
12 | private quantitySubject = new BehaviorSubject(0);
13 |
14 |
15 | get totalAction$(): Observable {
16 | return this.totalSubject.asObservable();
17 | }
18 | get quantityAction$(): Observable {
19 | return this.quantitySubject.asObservable();
20 | }
21 | get cartAction$(): Observable {
22 | return this.cartSubject.asObservable();
23 | }
24 |
25 |
26 | updateCart(product: Product): void {
27 | this.addToCart(product);
28 | this.quantityProducts();
29 | this.calcTotal();
30 | }
31 |
32 | resetCart(): void {
33 | this.cartSubject.next([]);
34 | this.totalSubject.next(0);
35 | this.quantitySubject.next(0);
36 | this.products = [];
37 | }
38 |
39 | private addToCart(product: Product): void {
40 | const isProductInCart = this.products.find(({ id }) => id === product.id)
41 |
42 | if (isProductInCart) {
43 | isProductInCart.qty += 1;
44 | } else {
45 | this.products.push({ ...product, qty: 1 })
46 | }
47 |
48 | this.cartSubject.next(this.products);
49 | }
50 |
51 | private quantityProducts(): void {
52 | const quantity = this.products.reduce((acc, prod) => acc += prod.qty, 0);
53 | this.quantitySubject.next(quantity);
54 | }
55 |
56 | private calcTotal(): void {
57 | const total = this.products.reduce((acc, prod) => acc += (prod.price * prod.qty), 0);
58 | this.totalSubject.next(total);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/domini-code/curso-angular-12/dd827e71b6d4427f23027996deafc303ce2588a8/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/domini-code/curso-angular-12/dd827e71b6d4427f23027996deafc303ce2588a8/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Store
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.error(err));
13 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * IE11 requires the following for NgClass support on SVG elements
23 | */
24 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
25 |
26 | /**
27 | * Web Animations `@angular/platform-browser/animations`
28 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
29 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
30 | */
31 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
32 |
33 | /**
34 | * By default, zone.js will patch all possible macroTask and DomEvents
35 | * user can disable parts of macroTask/DomEvents patch by setting following flags
36 | * because those flags need to be set before `zone.js` being loaded, and webpack
37 | * will put import in the top of bundle, so user need to create a separate file
38 | * in this directory (for example: zone-flags.ts), and put the following flags
39 | * into that file, and then add the following code before importing zone.js.
40 | * import './zone-flags';
41 | *
42 | * The flags allowed in zone-flags.ts are listed here.
43 | *
44 | * The following flags will work for all browsers.
45 | *
46 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
47 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
48 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
49 | *
50 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
51 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
52 | *
53 | * (window as any).__Zone_enable_cross_context_check = true;
54 | *
55 | */
56 |
57 | /***************************************************************************************************
58 | * Zone JS is required by default for Angular itself.
59 | */
60 | import 'zone.js'; // Included with Angular CLI.
61 |
62 |
63 | /***************************************************************************************************
64 | * APPLICATION IMPORTS
65 | */
66 |
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
3 | html, body { height: 100%; }
4 | body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
5 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | keys(): string[];
13 | (id: string): T;
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting()
21 | );
22 | // Then we find all the tests.
23 | const context = require.context('./', true, /\.spec\.ts$/);
24 | // And load the modules.
25 | context.keys().map(context);
26 |
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts",
10 | "src/polyfills.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "outDir": "./dist/out-tsc",
7 | "forceConsistentCasingInFileNames": true,
8 | "strict": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true,
11 | "sourceMap": true,
12 | "declaration": false,
13 | "downlevelIteration": true,
14 | "experimentalDecorators": true,
15 | "moduleResolution": "node",
16 | "importHelpers": true,
17 | "target": "es2017",
18 | "module": "es2020",
19 | "lib": [
20 | "es2018",
21 | "dom"
22 | ]
23 | },
24 | "angularCompilerOptions": {
25 | "enableI18nLegacyMessageIdFormat": false,
26 | "strictInjectionParameters": true,
27 | "strictInputAccessModifiers": true,
28 | "strictTemplates": true
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------