Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
19 changes: 19 additions & 0 deletions W2-M3-BlazingPizza/BlazingPizza.Client/App.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<CascadingAuthenticationState>
<Router AppAssembly="typeof(Program).Assembly" Context="routeData">
<Found>
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)">
<NotAuthorized>
<RedirectToLogin />
</NotAuthorized>
<Authorizing>
<div class="main">Please wait...</div>
</Authorizing>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="typeof(MainLayout)">
<div class="main">Sorry, there's nothing at this address.</div>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
19 changes: 19 additions & 0 deletions W2-M3-BlazingPizza/BlazingPizza.Client/BlazingPizza.Client.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="$(BlazorVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="$(BlazorVersion)" />
<PackageReference Include="Microsoft.Extensions.Http" Version="$(AspNetCoreVersion)" />
<PackageReference Include="System.Net.Http.Json" Version="$(SystemNetHttpJsonVersion)" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BlazingPizza.ComponentsLibrary\BlazingPizza.ComponentsLibrary.csproj" />
<ProjectReference Include="..\BlazingPizza.Shared\BlazingPizza.Shared.csproj" />
</ItemGroup>

</Project>
13 changes: 13 additions & 0 deletions W2-M3-BlazingPizza/BlazingPizza.Client/JSRuntimeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.JSInterop;
using System.Threading.Tasks;

namespace BlazingPizza.Client
{
public static class JSRuntimeExtensions
{
public static ValueTask<bool> Confirm(this IJSRuntime jsRuntime, string message)
{
return jsRuntime.InvokeAsync<bool>("confirm", message);
}
}
}
55 changes: 55 additions & 0 deletions W2-M3-BlazingPizza/BlazingPizza.Client/OrderState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Collections.Generic;

namespace BlazingPizza.Client
{
public class OrderState
{
public bool ShowingConfigureDialog { get; private set; }

public Pizza ConfiguringPizza { get; private set; }

public Order Order { get; private set; } = new Order();

public void ShowConfigurePizzaDialog(PizzaSpecial special)
{
ConfiguringPizza = new Pizza()
{
Special = special,
SpecialId = special.Id,
Size = Pizza.DefaultSize,
Toppings = new List<PizzaTopping>(),
};

ShowingConfigureDialog = true;
}

public void CancelConfigurePizzaDialog()
{
ConfiguringPizza = null;
ShowingConfigureDialog = false;
}

public void ConfirmConfigurePizzaDialog()
{
Order.Pizzas.Add(ConfiguringPizza);
ConfiguringPizza = null;

ShowingConfigureDialog = false;
}

public void RemoveConfiguredPizza(Pizza pizza)
{
Order.Pizzas.Remove(pizza);
}

public void ResetOrder()
{
Order = new Order();
}

public void ReplaceOrder(Order order)
{
Order = order;
}
}
}
35 changes: 35 additions & 0 deletions W2-M3-BlazingPizza/BlazingPizza.Client/OrdersClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;

namespace BlazingPizza.Client
{
public class OrdersClient
{
private readonly HttpClient httpClient;

public OrdersClient(HttpClient httpClient)
{
this.httpClient = httpClient;
}

public async Task<IEnumerable<OrderWithStatus>> GetOrders() =>
await httpClient.GetFromJsonAsync<IEnumerable<OrderWithStatus>>("orders");


public async Task<OrderWithStatus> GetOrder(int orderId) =>
await httpClient.GetFromJsonAsync<OrderWithStatus>($"orders/{orderId}");


public async Task<int> PlaceOrder(Order order)
{
var response = await httpClient.PostAsJsonAsync("orders", order);
response.EnsureSuccessStatusCode();
var orderId = await response.Content.ReadFromJsonAsync<int>();
return orderId;
}
}
}
32 changes: 32 additions & 0 deletions W2-M3-BlazingPizza/BlazingPizza.Client/Pages/Authentication.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@page "/authentication/{action}"
@inject OrderState OrderState
@inject NavigationManager NavigationManager

<RemoteAuthenticatorViewCore
TAuthenticationState="PizzaAuthenticationState"
AuthenticationState="RemoteAuthenticationState"
OnLogInSucceeded="RestorePizza"
Action="@Action" />

@code{
[Parameter] public string Action { get; set; }

public PizzaAuthenticationState RemoteAuthenticationState { get; set; } = new PizzaAuthenticationState();

protected override void OnInitialized()
{
if (RemoteAuthenticationActions.IsAction(RemoteAuthenticationActions.LogIn, Action))
{
// Preserve the current order so that we don't loose it
RemoteAuthenticationState.Order = OrderState.Order;
}
}

private void RestorePizza(PizzaAuthenticationState pizzaState)
{
if (pizzaState.Order != null)
{
OrderState.ReplaceOrder(pizzaState.Order);
}
}
}
47 changes: 47 additions & 0 deletions W2-M3-BlazingPizza/BlazingPizza.Client/Pages/Checkout.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@page "/checkout"
@attribute [Authorize]
@inject OrderState OrderState
@inject OrdersClient OrdersClient
@inject NavigationManager NavigationManager

<div class="main">
<EditForm Model="OrderState.Order.DeliveryAddress" OnValidSubmit="PlaceOrder">
<div class="checkout-cols">
<div class="checkout-order-details">
<h4>Review order</h4>
<OrderReview Order="OrderState.Order" />
</div>

<div class="checkout-delivery-address">
<h4>Deliver to...</h4>
<AddressEditor Address="OrderState.Order.DeliveryAddress" />
</div>
</div>

<button type="submit" class="checkout-button btn btn-warning" disabled="@isSubmitting">
Place order
</button>

<DataAnnotationsValidator />
</EditForm>
</div>

@code {
bool isSubmitting;

async Task PlaceOrder()
{
isSubmitting = true;

try
{
var newOrderId = await OrdersClient.PlaceOrder(OrderState.Order);
OrderState.ResetOrder();
NavigationManager.NavigateTo($"myorders/{newOrderId}");
}
catch (AccessTokenNotAvailableException ex)
{
ex.Redirect();
}
}
}
75 changes: 75 additions & 0 deletions W2-M3-BlazingPizza/BlazingPizza.Client/Pages/Index.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
@page "/"
@inject HttpClient HttpClient
@inject OrderState OrderState
@inject NavigationManager NavigationManager
@inject IJSRuntime JS

<div class="main">
<ul class="pizza-cards">
@if (specials != null)
{
@foreach (var special in specials)
{
<li @onclick="@(() => OrderState.ShowConfigurePizzaDialog(special))" style="background-image: url('@special.ImageUrl')">
<div class="pizza-info">
<span class="title">@special.Name</span>
@special.Description
<span class="price">@special.GetFormattedBasePrice()</span>
</div>
</li>
}
}
</ul>
</div>

<div class="sidebar">
@if (Order.Pizzas.Any())
{
<div class="order-contents">
<h2>Your order</h2>

@foreach (var configuredPizza in Order.Pizzas)
{
<ConfiguredPizzaItem Pizza="configuredPizza" OnRemoved="@(() => RemovePizza(configuredPizza))" />
}
</div>
}
else
{
<div class="empty-cart">Choose a pizza<br>to get started</div>
}

<div class="order-total @(Order.Pizzas.Any() ? "" : "hidden")">
Total:
<span class="total-price">@Order.GetFormattedTotalPrice()</span>
<a href="checkout" class="@(Order.Pizzas.Count == 0 ? "btn btn-warning disabled" : "btn btn-warning")">
Order >
</a>
</div>
</div>

@if (OrderState.ShowingConfigureDialog)
{
<ConfigurePizzaDialog
Pizza="OrderState.ConfiguringPizza"
OnCancel="OrderState.CancelConfigurePizzaDialog"
OnConfirm="OrderState.ConfirmConfigurePizzaDialog" />
}

@code {
List<PizzaSpecial> specials;
Order Order => OrderState.Order;

protected override async Task OnInitializedAsync()
{
specials = await HttpClient.GetFromJsonAsync<List<PizzaSpecial>>("specials");
}

async Task RemovePizza(Pizza configuredPizza)
{
if (await JS.Confirm($"Remove {configuredPizza.Special.Name} pizza from the order?"))
{
OrderState.RemoveConfiguredPizza(configuredPizza);
}
}
}
57 changes: 57 additions & 0 deletions W2-M3-BlazingPizza/BlazingPizza.Client/Pages/MyOrders.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@page "/myorders"
@attribute [Authorize]
@inject OrdersClient OrdersClient
@inject NavigationManager NavigationManager

<div class="main">
@if (ordersWithStatus == null)
{
<text>Loading...</text>
}
else if (!ordersWithStatus.Any())
{
<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>

@code {
IEnumerable<OrderWithStatus> ordersWithStatus;

protected override async Task OnParametersSetAsync()
{
try
{
ordersWithStatus = await OrdersClient.GetOrders();
}
catch (AccessTokenNotAvailableException ex)
{
ex.Redirect();
}
}
}
Loading