Added View Orders and Order Details to Users
This commit is contained in:
parent
7cdf78794d
commit
829e76ca68
@ -14,6 +14,7 @@ const routes: Routes = [
|
|||||||
{path: 'shop', loadChildren: ()=> import('./shop/shop.module').then(mod => mod.ShopModule), data: {breadcrumb: 'Shop'}},
|
{path: 'shop', loadChildren: ()=> import('./shop/shop.module').then(mod => mod.ShopModule), data: {breadcrumb: 'Shop'}},
|
||||||
{path: 'basket', loadChildren: ()=> import('./basket/basket.module').then(mod => mod.BasketModule), data: {breadcrumb: 'Shopping Cart'}},
|
{path: 'basket', loadChildren: ()=> import('./basket/basket.module').then(mod => mod.BasketModule), data: {breadcrumb: 'Shopping Cart'}},
|
||||||
{path: 'checkout', canActivate: [AuthGuard], loadChildren: ()=> import('./checkout/checkout.module').then(mod => mod.CheckoutModule), data: {breadcrumb: 'Checkout'}},
|
{path: 'checkout', canActivate: [AuthGuard], loadChildren: ()=> import('./checkout/checkout.module').then(mod => mod.CheckoutModule), data: {breadcrumb: 'Checkout'}},
|
||||||
|
{path: 'orders', canActivate: [AuthGuard], loadChildren: ()=> import('./orders/orders.module').then(mod => mod.OrdersModule), data: {breadcrumb: 'Orders'}},
|
||||||
{path: 'account', loadChildren: ()=> import('./account/account.module').then(mod => mod.AccountModule), data: {breadcrumb: {skip: true}}},
|
{path: 'account', loadChildren: ()=> import('./account/account.module').then(mod => mod.AccountModule), data: {breadcrumb: {skip: true}}},
|
||||||
{path: '**', redirectTo: 'not-found', pathMatch: 'full'}
|
{path: '**', redirectTo: 'not-found', pathMatch: 'full'}
|
||||||
];
|
];
|
||||||
|
@ -6,7 +6,6 @@ import { AppRoutingModule } from './app-routing.module';
|
|||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { CoreModule } from './core/core.module';
|
import { CoreModule } from './core/core.module';
|
||||||
import { ShopModule } from './shop/shop.module';
|
|
||||||
import { HomeModule } from './home/home.module';
|
import { HomeModule } from './home/home.module';
|
||||||
import { ErrorInterceptor } from './core/interceptors/error.interceptor';
|
import { ErrorInterceptor } from './core/interceptors/error.interceptor';
|
||||||
import { NgxSpinnerModule } from 'ngx-spinner';
|
import { NgxSpinnerModule } from 'ngx-spinner';
|
||||||
|
@ -85,6 +85,12 @@ export class BasketService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteLocalBasket(id: string){
|
||||||
|
this.basketSource.next(null);
|
||||||
|
this.basketTotalSource.next(null);
|
||||||
|
localStorage.removeItem('basket_id');
|
||||||
|
}
|
||||||
|
|
||||||
deleteBasket(basket: IBasket) {
|
deleteBasket(basket: IBasket) {
|
||||||
return this.http.delete(this.baseUrl + 'basket?id=' + basket.id).subscribe({
|
return this.http.delete(this.baseUrl + 'basket?id=' + basket.id).subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<button class="btn btn-outline-primary" cdkStepperPrevious>
|
<button class="btn btn-outline-primary" cdkStepperPrevious>
|
||||||
<i class="fa fa-angle-left"></i> Back to Review
|
<i class="fa fa-angle-left"></i> Back to Review
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary">
|
<button class="btn btn-primary" (click)="submitOrder()">
|
||||||
Complete Checkout <i class="fa fa-angle-right"></i>
|
Complete Checkout <i class="fa fa-angle-right"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { FormGroup } from '@angular/forms';
|
||||||
|
import { NavigationExtras, Router } from '@angular/router';
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { BasketService } from 'src/app/basket/basket.service';
|
||||||
|
import { IBasket } from 'src/app/shared/models/baskset';
|
||||||
|
import { IOrder } from 'src/app/shared/models/order';
|
||||||
|
import { CheckoutService } from '../checkout.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-checkout-payments',
|
selector: 'app-checkout-payments',
|
||||||
@ -6,10 +13,36 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
styleUrls: ['./checkout-payments.component.scss']
|
styleUrls: ['./checkout-payments.component.scss']
|
||||||
})
|
})
|
||||||
export class CheckoutPaymentsComponent implements OnInit {
|
export class CheckoutPaymentsComponent implements OnInit {
|
||||||
|
@Input() checkoutForm: FormGroup;
|
||||||
|
|
||||||
constructor() { }
|
constructor(private basketService: BasketService, private checkoutService: CheckoutService, private toastr: ToastrService, private router: Router) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
submitOrder(){
|
||||||
|
const basket = this.basketService.getCurrentBasketValue();
|
||||||
|
const orderToCreate = this.getOrderToCreate(basket);
|
||||||
|
this.checkoutService.createOrder(orderToCreate).subscribe({
|
||||||
|
next: (order: IOrder) => {
|
||||||
|
this.toastr.success('Order created successfully');
|
||||||
|
this.basketService.deleteLocalBasket(basket.id);
|
||||||
|
const navigationExtras: NavigationExtras = {state: order};
|
||||||
|
this.router.navigate(['checkout/success'], navigationExtras);
|
||||||
|
},
|
||||||
|
error: (e: any) => {
|
||||||
|
this.toastr.error(e.message);
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
complete: () => { console.log('completed') }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getOrderToCreate(basket: IBasket) {
|
||||||
|
return {
|
||||||
|
basketId: basket.id,
|
||||||
|
deliveryMethodId: +this.checkoutForm.get('deliveryForm').get('deliveryMethod').value,
|
||||||
|
shipToAddress: this.checkoutForm.get('addressForm').value
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,12 @@ import { NgModule } from '@angular/core';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { CheckoutComponent } from './checkout.component';
|
import { CheckoutComponent } from './checkout.component';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { CheckoutSuccessComponent } from './checkout-success/checkout-success.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', component: CheckoutComponent}
|
{path: '', component: CheckoutComponent},
|
||||||
]
|
{path: 'success', component: CheckoutSuccessComponent }
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [],
|
||||||
|
@ -1 +1,9 @@
|
|||||||
<p>checkout-success works!</p>
|
<div class="container mt-5">
|
||||||
|
<div>
|
||||||
|
<i class="fa fa-check-circle fa-5x" style="color: green;"></i>
|
||||||
|
</div>
|
||||||
|
<h2>This you. Your order is confirmed</h2>
|
||||||
|
<p class="mb-4">Your order number is: {{order?.id}}</p>
|
||||||
|
<button *ngIf="order" class="btn btn-outline-success" routerLink="/orders/{{order?.id}}">View Your Order</button>
|
||||||
|
<button *ngIf="!order" class="btn btn-outline-success" routerLink="/orders">View Orders</button>
|
||||||
|
</div>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { IOrder } from 'src/app/shared/models/order';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-checkout-success',
|
selector: 'app-checkout-success',
|
||||||
@ -6,8 +8,13 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
styleUrls: ['./checkout-success.component.scss']
|
styleUrls: ['./checkout-success.component.scss']
|
||||||
})
|
})
|
||||||
export class CheckoutSuccessComponent implements OnInit {
|
export class CheckoutSuccessComponent implements OnInit {
|
||||||
|
order: IOrder;
|
||||||
|
|
||||||
constructor() { }
|
constructor(private router: Router) {
|
||||||
|
const navigation = this.router.getCurrentNavigation();
|
||||||
|
const state = navigation && navigation.extras && navigation.extras.state;
|
||||||
|
if(state) { this.order = state as IOrder; }
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<app-checkout-review></app-checkout-review>
|
<app-checkout-review></app-checkout-review>
|
||||||
</cdk-step>
|
</cdk-step>
|
||||||
<cdk-step [label]="'Payment'">
|
<cdk-step [label]="'Payment'">
|
||||||
<app-checkout-payments></app-checkout-payments>
|
<app-checkout-payments [checkoutForm]="checkoutForm"></app-checkout-payments>
|
||||||
</cdk-step>
|
</cdk-step>
|
||||||
</app-stepper>
|
</app-stepper>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { map } from 'rxjs';
|
import { map } from 'rxjs';
|
||||||
import { environment } from 'src/environments/environment';
|
import { environment } from 'src/environments/environment';
|
||||||
import { IDeliveryMethods } from '../shared/models/deliveryMethods';
|
import { IDeliveryMethods } from '../shared/models/deliveryMethods';
|
||||||
|
import { IOrderToCreate } from '../shared/models/order';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -12,6 +13,10 @@ export class CheckoutService {
|
|||||||
|
|
||||||
constructor(private http: HttpClient) { }
|
constructor(private http: HttpClient) { }
|
||||||
|
|
||||||
|
createOrder(order: IOrderToCreate){
|
||||||
|
return this.http.post(this.baseUrl + 'orders', order);
|
||||||
|
}
|
||||||
|
|
||||||
getDeliveryMethods(){
|
getDeliveryMethods(){
|
||||||
return this.http.get(this.baseUrl + 'orders/deliveryMethods').pipe(
|
return this.http.get(this.baseUrl + 'orders/deliveryMethods').pipe(
|
||||||
map((dm: IDeliveryMethods[]) => {
|
map((dm: IDeliveryMethods[]) => {
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
<div class="container mt-5">
|
||||||
|
<div class="row" *ngIf="order">
|
||||||
|
<div class="col-8">
|
||||||
|
<div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-hover table-light">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">
|
||||||
|
<div class="p-2 px-3 text-uppercase">Product</div>
|
||||||
|
</th>
|
||||||
|
<th class="text-center" scope="col">
|
||||||
|
<div class="p-2 px-3 text-uppercase">Price</div>
|
||||||
|
</th>
|
||||||
|
<th class="text-center" scope="col">
|
||||||
|
<div class="p-2 px-3 text-uppercase">quantity</div>
|
||||||
|
</th>
|
||||||
|
<th class="text-center" scope="col">
|
||||||
|
<div class="p-2 px-3 text-uppercase">Total</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let item of order.orderItems">
|
||||||
|
<th scope="row">
|
||||||
|
<div class="p-2">
|
||||||
|
<img class="img-fluid" style="max-height: 50px" src="{{item.pictureUrl}}" alt="{{item.productName}}">
|
||||||
|
<div class="ms-3 d-inline-block align-middle">
|
||||||
|
<h5 class="mb-0">
|
||||||
|
<a class="text-dark">{{item.productName}}</a>
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<td class="align-middle text-center">{{item.price | currency}}</td>
|
||||||
|
<td class="align-middle text-center">{{item.quantity}}</td>
|
||||||
|
<td class="align-middle text-center">{{item.price * item.quantity | currency}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="bg-light px-4 py-3 text-uppercase font-weight-bold">
|
||||||
|
Order Summary
|
||||||
|
</div>
|
||||||
|
<div class="p-4">
|
||||||
|
<ul class="list-unstyled mb-4">
|
||||||
|
<li class="d-flex justify-content-between py-3 border-bottom">
|
||||||
|
<strong class="text-muted">Subtotal</strong>
|
||||||
|
<strong>{{order.subtotal | currency}}</strong>
|
||||||
|
</li>
|
||||||
|
<li class="d-flex justify-content-between py-3 border-bottom">
|
||||||
|
<strong class="text-muted">Shipping and Handling</strong>
|
||||||
|
<strong>{{order.shippingPrice | currency}}</strong>
|
||||||
|
</li>
|
||||||
|
<li class="d-flex justify-content-between py-3 border-bottom">
|
||||||
|
<strong class="text-muted">Total</strong>
|
||||||
|
<strong>{{order.total | currency}}</strong>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,29 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { IOrder } from 'src/app/shared/models/order';
|
||||||
|
import { BreadcrumbService } from 'xng-breadcrumb';
|
||||||
|
import { OrdersService } from '../orders.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-order-details',
|
||||||
|
templateUrl: './order-details.component.html',
|
||||||
|
styleUrls: ['./order-details.component.scss']
|
||||||
|
})
|
||||||
|
export class OrderDetailsComponent implements OnInit {
|
||||||
|
order: IOrder;
|
||||||
|
|
||||||
|
constructor(private route: ActivatedRoute, private breadCrumbService: BreadcrumbService, private orderService: OrdersService) {
|
||||||
|
this.breadCrumbService.set('@OrderDetails', ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.orderService.getOrderDetails(+this.route.snapshot.paramMap.get('id')).subscribe({
|
||||||
|
next: (order: IOrder) => {
|
||||||
|
this.order = order;
|
||||||
|
this.breadCrumbService.set('@OrderDetails', `Order# ${order.id} - ${order.status}`);
|
||||||
|
},
|
||||||
|
error: (e: any) => { console.log(e); },
|
||||||
|
complete: () => { console.log('completed'); }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
15
client/src/app/orders/orders-routing.module.ts
Normal file
15
client/src/app/orders/orders-routing.module.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { OrderDetailsComponent } from './order-details/order-details.component';
|
||||||
|
import { OrdersComponent } from './orders.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{path: '', component: OrdersComponent},
|
||||||
|
{path: ':id', component: OrderDetailsComponent, data: {breadcrumb: {alias: 'OrderDetails'}}},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class OrdersRoutingModule { }
|
24
client/src/app/orders/orders.component.html
Normal file
24
client/src/app/orders/orders.component.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<div class="container mt-5">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<table class="table table-cover" style="cursor: pointer;">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th>Order</th>
|
||||||
|
<th>Total</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let order of orders" routerLink="/orders/{{order.id}}">
|
||||||
|
<th># {{order.id}}</th>
|
||||||
|
<td>{{order.orderDate | date: 'medium'}}</td>
|
||||||
|
<td>{{order.total | currency}}</td>
|
||||||
|
<td>{{order.status}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
0
client/src/app/orders/orders.component.scss
Normal file
0
client/src/app/orders/orders.component.scss
Normal file
27
client/src/app/orders/orders.component.ts
Normal file
27
client/src/app/orders/orders.component.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { IOrder } from '../shared/models/order';
|
||||||
|
import { OrdersService } from './orders.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-orders',
|
||||||
|
templateUrl: './orders.component.html',
|
||||||
|
styleUrls: ['./orders.component.scss']
|
||||||
|
})
|
||||||
|
export class OrdersComponent implements OnInit {
|
||||||
|
orders: IOrder[];
|
||||||
|
|
||||||
|
constructor(private orderService: OrdersService) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.getOrders();
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrders(){
|
||||||
|
this.orderService.getOrdersForUser().subscribe({
|
||||||
|
next: (orders: IOrder[]) => { this.orders = orders; },
|
||||||
|
error: (e: any) => { console.log(e); },
|
||||||
|
complete: () => { console.log('Orders'); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
19
client/src/app/orders/orders.module.ts
Normal file
19
client/src/app/orders/orders.module.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { OrderDetailsComponent } from './order-details/order-details.component';
|
||||||
|
import { OrdersComponent } from './orders.component';
|
||||||
|
import { OrdersRoutingModule } from './orders-routing.module';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
OrdersComponent,
|
||||||
|
OrderDetailsComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
OrdersRoutingModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class OrdersModule { }
|
20
client/src/app/orders/orders.service.ts
Normal file
20
client/src/app/orders/orders.service.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class OrdersService {
|
||||||
|
baseUrl = environment.apiUrl;
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) { }
|
||||||
|
|
||||||
|
getOrdersForUser(){
|
||||||
|
return this.http.get(this.baseUrl + 'orders');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrderDetails(id: number){
|
||||||
|
return this.http.get(this.baseUrl + 'orders/' + id);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
<div class="table-responsive">
|
<ng-container *ngIf="basket$ | async">
|
||||||
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-hover table-light">
|
<table class="table table-striped table-hover table-light">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -49,4 +50,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
28
client/src/app/shared/models/order.ts
Normal file
28
client/src/app/shared/models/order.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { IAddress } from "./address";
|
||||||
|
|
||||||
|
export interface IOrderToCreate {
|
||||||
|
basketId: string;
|
||||||
|
deliveryMethodId: number;
|
||||||
|
shipToAddress: IAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IOrderItem {
|
||||||
|
productId: number;
|
||||||
|
productName: string;
|
||||||
|
pictureUrl: string;
|
||||||
|
price: number;
|
||||||
|
quantity: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IOrder {
|
||||||
|
id: number;
|
||||||
|
buyerEmail: string;
|
||||||
|
orderDate: Date;
|
||||||
|
shipToAddress: IAddress;
|
||||||
|
deliveryMethod: string;
|
||||||
|
shippingPrice: number;
|
||||||
|
orderItems: IOrderItem[];
|
||||||
|
subtotal: number;
|
||||||
|
total: number;
|
||||||
|
status: string;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user