Commit 551ae3e0 authored by Ryan Nowak's avatar Ryan Nowak
Browse files

Add part 3 - order status

parent 4ae51c7e
......@@ -34,13 +34,19 @@ Lunch
- add a ConfiguredPizzaItem component
- hook up the order button to do an HTTP POST and clear the order
- (no way to see existing orders yet)
1. DI
1. Build the order status screen
- Add a new page MyOrders with `@page orders`
- Add a new NavLink to the layout that links to this URL
- At this point you can appreciate how this page will share layout because that's specified in imports
- MyOrders should retrieve list of orders and show the past orders
- Add a new page OrderDetails to show the status of an individual order
- It should be possible to click from MyOrders->OrderDetails
- The OrderDetails should poll for updates to the order fromthe backend
- Go back to the index and make placing an order navigate you to the MyOrders page
1. DI and the AppState pattern
- Create a service for interacting with the backend, repository abstraction
- Refactor HttpClient code to use service instead
- Talk to DI scopes
1. Build the order status screen
- Confirmation screen
- Cover `@page`
1. JS interop
- Add order status
- Real status (map location, time to delivery) via polling
......
......@@ -10,7 +10,7 @@ namespace BlazingPizza.Server
{
[Route("orders")]
[ApiController]
[Authorize]
// [Authorize]
public class OrdersController : Controller
{
private readonly PizzaStoreContext _db;
......@@ -24,7 +24,7 @@ namespace BlazingPizza.Server
public async Task<ActionResult<List<OrderWithStatus>>> GetOrders()
{
var orders = await _db.Orders
.Where(o => o.UserId == GetUserId())
// .Where(o => o.UserId == GetUserId())
.Include(o => o.Pizzas).ThenInclude(p => p.Special)
.Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping)
.OrderByDescending(o => o.CreatedTime)
......@@ -38,7 +38,7 @@ namespace BlazingPizza.Server
{
var order = await _db.Orders
.Where(o => o.OrderId == orderId)
.Where(o => o.UserId == GetUserId())
// .Where(o => o.UserId == GetUserId())
.Include(o => o.Pizzas).ThenInclude(p => p.Special)
.Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping)
.SingleOrDefaultAsync();
......@@ -56,7 +56,7 @@ namespace BlazingPizza.Server
{
order.CreatedTime = DateTime.Now;
order.DeliveryLocation = new LatLong(51.5001, -0.1239);
order.UserId = GetUserId();
// order.UserId = GetUserId();
_db.Orders.Attach(order);
await _db.SaveChangesAsync();
......
......@@ -10,7 +10,7 @@ namespace BlazingPizza.Server
{
[Route("orders")]
[ApiController]
[Authorize]
// [Authorize]
public class OrdersController : Controller
{
private readonly PizzaStoreContext _db;
......@@ -24,7 +24,7 @@ namespace BlazingPizza.Server
public async Task<ActionResult<List<OrderWithStatus>>> GetOrders()
{
var orders = await _db.Orders
.Where(o => o.UserId == GetUserId())
// .Where(o => o.UserId == GetUserId())
.Include(o => o.Pizzas).ThenInclude(p => p.Special)
.Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping)
.OrderByDescending(o => o.CreatedTime)
......@@ -38,7 +38,7 @@ namespace BlazingPizza.Server
{
var order = await _db.Orders
.Where(o => o.OrderId == orderId)
.Where(o => o.UserId == GetUserId())
// .Where(o => o.UserId == GetUserId())
.Include(o => o.Pizzas).ThenInclude(p => p.Special)
.Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping)
.SingleOrDefaultAsync();
......@@ -56,7 +56,7 @@ namespace BlazingPizza.Server
{
order.CreatedTime = DateTime.Now;
order.DeliveryLocation = new LatLong(51.5001, -0.1239);
order.UserId = GetUserId();
// order.UserId = GetUserId();
_db.Orders.Attach(order);
await _db.SaveChangesAsync();
......
......@@ -10,7 +10,7 @@ namespace BlazingPizza.Server
{
[Route("orders")]
[ApiController]
[Authorize]
// [Authorize]
public class OrdersController : Controller
{
private readonly PizzaStoreContext _db;
......@@ -24,7 +24,7 @@ namespace BlazingPizza.Server
public async Task<ActionResult<List<OrderWithStatus>>> GetOrders()
{
var orders = await _db.Orders
.Where(o => o.UserId == GetUserId())
// .Where(o => o.UserId == GetUserId())
.Include(o => o.Pizzas).ThenInclude(p => p.Special)
.Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping)
.OrderByDescending(o => o.CreatedTime)
......@@ -38,7 +38,7 @@ namespace BlazingPizza.Server
{
var order = await _db.Orders
.Where(o => o.OrderId == orderId)
.Where(o => o.UserId == GetUserId())
// .Where(o => o.UserId == GetUserId())
.Include(o => o.Pizzas).ThenInclude(p => p.Special)
.Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping)
.SingleOrDefaultAsync();
......@@ -56,7 +56,7 @@ namespace BlazingPizza.Server
{
order.CreatedTime = DateTime.Now;
order.DeliveryLocation = new LatLong(51.5001, -0.1239);
order.UserId = GetUserId();
// order.UserId = GetUserId();
_db.Orders.Attach(order);
await _db.SaveChangesAsync();
......
@page "/"
@inject HttpClient HttpClient
@inject IUriHelper UriHelper
<div class="main">
<ul class="pizza-cards">
......@@ -104,5 +105,6 @@
{
await HttpClient.PostJsonAsync("/orders", order);
order = new Order();
UriHelper.NavigateTo("myorders");
}
}
@page "/myorders"
@inject HttpClient HttpClient
<div class="main">
@if (ordersWithStatus == null)
{
<text>Loading...</text>
}
else if (ordersWithStatus.Count == 0)
{
<h2>No orders placed</h2>
<a class="btn btn-success" href="">Order some pizza</a>
}
else
{
<div class="list-group orders-list">
@foreach (var item in ordersWithStatus)
{
<div class="list-group-item">
<div class="col">
<h5>@item.Order.CreatedTime.ToLongDateString()</h5>
Items:
<strong>@item.Order.Pizzas.Count()</strong>;
Total price:
<strong>£@item.Order.GetFormattedTotalPrice()</strong>
</div>
<div class="col">
Status: <strong>@item.StatusText</strong>
</div>
<div class="col flex-grow-0">
<a href="myorders/@item.Order.OrderId" class="btn btn-success">
Track &gt;
</a>
</div>
</div>
}
</div>
}
</div>
@functions {
List<OrderWithStatus> ordersWithStatus;
protected override async Task OnParametersSetAsync()
{
ordersWithStatus = await HttpClient.GetJsonAsync<List<OrderWithStatus>>("/orders");
}
}
@page "/myorders/{orderId:int}"
@using System.Threading
@inject HttpClient HttpClient
@implements IDisposable
<div class="main">
@if (invalidOrder)
{
<h2>Nope</h2>
<p>Sorry, this order could not be loaded.</p>
}
else if (orderWithStatus == null)
{
<text>Loading...</text>
}
else
{
<div class="track-order">
<div class="track-order-title">
<h2>
Order placed @orderWithStatus.Order.CreatedTime.ToLongDateString()
</h2>
<p class="ml-auto mb-0">
Status: <strong>@orderWithStatus.StatusText</strong>
</p>
</div>
<div class="track-order-body">
<div class="track-order-details">
@foreach (var pizza in orderWithStatus.Order.Pizzas)
{
<p>
<strong>
@(pizza.Size)"
@pizza.Special.Name
(£@pizza.GetFormattedTotalPrice())
</strong>
</p>
<ul>
@foreach (var topping in pizza.Toppings)
{
<li>+ @topping.Topping.Name</li>
}
</ul>
}
<p>
<strong>
Total price:
£@orderWithStatus.Order.GetFormattedTotalPrice()
</strong>
</p>
</div>
</div>
</div>
}
</div>
@functions {
[Parameter] int OrderId { get; set; }
OrderWithStatus orderWithStatus;
bool invalidOrder;
CancellationTokenSource pollingCancellationToken;
protected override void OnParametersSet()
{
// If we were already polling for a different order, stop doing so
pollingCancellationToken?.Cancel();
// Start a new poll loop
PollForUpdates();
}
void IDisposable.Dispose()
{
pollingCancellationToken?.Cancel();
}
private async void PollForUpdates()
{
pollingCancellationToken = new CancellationTokenSource();
while (!pollingCancellationToken.IsCancellationRequested)
{
try
{
invalidOrder = false;
orderWithStatus = await HttpClient.GetJsonAsync<OrderWithStatus>($"/orders/{OrderId}");
}
catch (Exception ex)
{
invalidOrder = true;
pollingCancellationToken.Cancel();
Console.Error.WriteLine(ex);
}
StateHasChanged();
await Task.Delay(4000);
}
}
}
......@@ -7,6 +7,11 @@
<img src="img/pizza-slice.svg" />
<div>Get Pizza</div>
</NavLink>
<NavLink href="myorders" class="nav-tab">
<img src="img/bike.svg" />
<div>My Orders</div>
</NavLink>
</div>
<div class="content">
......
......@@ -10,7 +10,7 @@ namespace BlazingPizza.Server
{
[Route("orders")]
[ApiController]
[Authorize]
// [Authorize]
public class OrdersController : Controller
{
private readonly PizzaStoreContext _db;
......@@ -24,7 +24,7 @@ namespace BlazingPizza.Server
public async Task<ActionResult<List<OrderWithStatus>>> GetOrders()
{
var orders = await _db.Orders
.Where(o => o.UserId == GetUserId())
// .Where(o => o.UserId == GetUserId())
.Include(o => o.Pizzas).ThenInclude(p => p.Special)
.Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping)
.OrderByDescending(o => o.CreatedTime)
......@@ -38,7 +38,7 @@ namespace BlazingPizza.Server
{
var order = await _db.Orders
.Where(o => o.OrderId == orderId)
.Where(o => o.UserId == GetUserId())
// .Where(o => o.UserId == GetUserId())
.Include(o => o.Pizzas).ThenInclude(p => p.Special)
.Include(o => o.Pizzas).ThenInclude(p => p.Toppings).ThenInclude(t => t.Topping)
.SingleOrDefaultAsync();
......@@ -56,7 +56,7 @@ namespace BlazingPizza.Server
{
order.CreatedTime = DateTime.Now;
order.DeliveryLocation = new LatLong(51.5001, -0.1239);
order.UserId = GetUserId();
// order.UserId = GetUserId();
_db.Orders.Attach(order);
await _db.SaveChangesAsync();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment