diff --git a/code/src/MicroCommerce.Web/CartService.cs b/code/src/MicroCommerce.Web/CartService.cs new file mode 100644 index 00000000..cd8eb1ae --- /dev/null +++ b/code/src/MicroCommerce.Web/CartService.cs @@ -0,0 +1,11 @@ +namespace MicroCommerce.Web; + +public class CartService +{ + public List CartItems { get; private set; } = new(); + + public void AddToCart(GetProductsResponse.Product product) + { + CartItems.Add(product); + } +} diff --git a/code/src/MicroCommerce.Web/Components/App.razor b/code/src/MicroCommerce.Web/Components/App.razor index 813ccaee..3bdd4952 100644 --- a/code/src/MicroCommerce.Web/Components/App.razor +++ b/code/src/MicroCommerce.Web/Components/App.razor @@ -7,13 +7,14 @@ - + +
Loading...
diff --git a/code/src/MicroCommerce.Web/Components/Layout/MainLayout.razor b/code/src/MicroCommerce.Web/Components/Layout/MainLayout.razor index 89f4b0cf..09e6d10f 100644 --- a/code/src/MicroCommerce.Web/Components/Layout/MainLayout.razor +++ b/code/src/MicroCommerce.Web/Components/Layout/MainLayout.razor @@ -1,20 +1,8 @@ @inherits LayoutComponentBase -
- - -
-
- About -
- -
- @Body -
-
-
+
+ @Body +
An unhandled error has occurred. diff --git a/code/src/MicroCommerce.Web/Components/Layout/MainLayout.razor.css b/code/src/MicroCommerce.Web/Components/Layout/MainLayout.razor.css index 038baf17..df8c10ff 100644 --- a/code/src/MicroCommerce.Web/Components/Layout/MainLayout.razor.css +++ b/code/src/MicroCommerce.Web/Components/Layout/MainLayout.razor.css @@ -1,81 +1,3 @@ -.page { - position: relative; - display: flex; - flex-direction: column; -} - -main { - flex: 1; -} - -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - -.top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; - justify-content: flex-end; - height: 3.5rem; - display: flex; - align-items: center; -} - - .top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - text-decoration: none; - } - - .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; - } - - .top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } - -@media (max-width: 640.98px) { - .top-row { - justify-content: space-between; - } - - .top-row ::deep a, .top-row ::deep .btn-link { - margin-left: 0; - } -} - -@media (min-width: 641px) { - .page { - flex-direction: row; - } - - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; - } - - .top-row { - position: sticky; - top: 0; - z-index: 1; - } - - .top-row.auth ::deep a:first-child { - flex: 1; - text-align: right; - width: 0; - } - - .top-row, article { - padding-left: 2rem !important; - padding-right: 1.5rem !important; - } -} - #blazor-error-ui { background: lightyellow; bottom: 0; diff --git a/code/src/MicroCommerce.Web/Components/Layout/NavMenu.razor b/code/src/MicroCommerce.Web/Components/Layout/NavMenu.razor deleted file mode 100644 index 99bef42d..00000000 --- a/code/src/MicroCommerce.Web/Components/Layout/NavMenu.razor +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/code/src/MicroCommerce.Web/Components/Layout/NavMenu.razor.css b/code/src/MicroCommerce.Web/Components/Layout/NavMenu.razor.css deleted file mode 100644 index 95fcc36e..00000000 --- a/code/src/MicroCommerce.Web/Components/Layout/NavMenu.razor.css +++ /dev/null @@ -1,102 +0,0 @@ -.navbar-toggler { - appearance: none; - cursor: pointer; - width: 3.5rem; - height: 2.5rem; - color: white; - position: absolute; - top: 0.5rem; - right: 1rem; - border: 1px solid rgba(255, 255, 255, 0.1); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); -} - -.navbar-toggler:checked { - background-color: rgba(255, 255, 255, 0.5); -} - -.top-row { - height: 3.5rem; - background-color: rgba(0,0,0,0.4); -} - -.navbar-brand { - font-size: 1.1rem; -} - -.bi { - display: inline-block; - position: relative; - width: 1.25rem; - height: 1.25rem; - margin-right: 0.75rem; - top: -1px; - background-size: cover; -} - -.bi-house-door-fill { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); -} - -.bi-plus-square-fill { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); -} - -.bi-list-nested { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); -} - -.nav-item { - font-size: 0.9rem; - padding-bottom: 0.5rem; -} - - .nav-item:first-of-type { - padding-top: 1rem; - } - - .nav-item:last-of-type { - padding-bottom: 1rem; - } - - .nav-item ::deep a { - color: #d7d7d7; - border-radius: 4px; - height: 3rem; - display: flex; - align-items: center; - line-height: 3rem; - } - -.nav-item ::deep a.active { - background-color: rgba(255,255,255,0.37); - color: white; -} - -.nav-item ::deep a:hover { - background-color: rgba(255,255,255,0.1); - color: white; -} - -.nav-scrollable { - display: none; -} - -.navbar-toggler:checked ~ .nav-scrollable { - display: block; -} - -@media (min-width: 641px) { - .navbar-toggler { - display: none; - } - - .nav-scrollable { - /* Never collapse the sidebar for wide screens */ - display: block; - - /* Allow sidebar to scroll for tall menus */ - height: calc(100vh - 3.5rem); - overflow-y: auto; - } -} diff --git a/code/src/MicroCommerce.Web/Components/Pages/Cart.razor b/code/src/MicroCommerce.Web/Components/Pages/Cart.razor new file mode 100644 index 00000000..4318f645 --- /dev/null +++ b/code/src/MicroCommerce.Web/Components/Pages/Cart.razor @@ -0,0 +1,22 @@ +@page "/cart" +@inject CartService CartService + +

Shopping Cart

+
+
    + @if (CartService.CartItems.Count > 0) + { + @foreach (var item in CartService.CartItems) + { +
  • + @item.Name + $@item.Price +
  • + } + } + else + { +

    Your cart is empty.

    + } +
+
diff --git a/code/src/MicroCommerce.Web/Components/Pages/Home.razor b/code/src/MicroCommerce.Web/Components/Pages/Home.razor index e325f17f..d87621b3 100644 --- a/code/src/MicroCommerce.Web/Components/Pages/Home.razor +++ b/code/src/MicroCommerce.Web/Components/Pages/Home.razor @@ -5,30 +5,21 @@ Home
+

Product List

+ @if (_products is null) { -

Loading product catalog…

+

Loading product catalog…

} else { -
- @foreach (var item in _products) +
+ @foreach (var product in _products) { -
-
- @item.Name -
- -
-
-

@item.Name

-

@item.Name

-
-

@item.Price.ToString("C", new CultureInfo("en-US"))

-
-
-
+
+
+ }
} diff --git a/code/src/MicroCommerce.Web/Components/Shared/Cart.razor b/code/src/MicroCommerce.Web/Components/Shared/Cart.razor new file mode 100644 index 00000000..513375d6 --- /dev/null +++ b/code/src/MicroCommerce.Web/Components/Shared/Cart.razor @@ -0,0 +1,29 @@ +@inject NavigationManager Navigation + +
+ @if (true) + { + + + + } +
+ +@code { + protected override async Task OnInitializedAsync() + { + } + + private async Task HandleCheckout() + { + // Preserve query string + Navigation.NavigateTo($"/{new Uri(Navigation.Uri).Query}"); + } +} diff --git a/code/src/MicroCommerce.Web/Components/Shared/ProductCard.razor b/code/src/MicroCommerce.Web/Components/Shared/ProductCard.razor new file mode 100644 index 00000000..3d66acd4 --- /dev/null +++ b/code/src/MicroCommerce.Web/Components/Shared/ProductCard.razor @@ -0,0 +1,17 @@ +@using System.Globalization + +
+ @Product.Name +
+
@Product.Name
+

$@Product.Price.ToString("C", new CultureInfo("en-US"))

+ +
+
+ +@code { + [Parameter] public required GetProductsResponse.Product Product { get; set; } + [Parameter] public required Action AddToCart { get; set; } +} diff --git a/code/src/MicroCommerce.Web/Components/_Imports.razor b/code/src/MicroCommerce.Web/Components/_Imports.razor index 71f7c7ab..9bb70bb2 100644 --- a/code/src/MicroCommerce.Web/Components/_Imports.razor +++ b/code/src/MicroCommerce.Web/Components/_Imports.razor @@ -9,3 +9,4 @@ @using Microsoft.JSInterop @using MicroCommerce.Web @using MicroCommerce.Web.Components +@using MicroCommerce.Web.Components.Shared diff --git a/code/src/MicroCommerce.Web/ProductApiClient.cs b/code/src/MicroCommerce.Web/ProductApiClient.cs index 489c9957..2db375b6 100644 --- a/code/src/MicroCommerce.Web/ProductApiClient.cs +++ b/code/src/MicroCommerce.Web/ProductApiClient.cs @@ -23,4 +23,3 @@ public record Product public string ImageUrl { get; set; } = ""; } } - diff --git a/code/src/MicroCommerce.Web/Program.cs b/code/src/MicroCommerce.Web/Program.cs index 5bff60d2..455b711b 100644 --- a/code/src/MicroCommerce.Web/Program.cs +++ b/code/src/MicroCommerce.Web/Program.cs @@ -21,6 +21,8 @@ client.BaseAddress = new("https+http://apiservice"); }); +builder.Services.AddScoped(); + var app = builder.Build(); if (!app.Environment.IsDevelopment()) diff --git a/code/src/MicroCommerce.Web/wwwroot/app.css b/code/src/MicroCommerce.Web/wwwroot/app.css index 4f0e5907..27701f61 100644 --- a/code/src/MicroCommerce.Web/wwwroot/app.css +++ b/code/src/MicroCommerce.Web/wwwroot/app.css @@ -2,28 +2,6 @@ html, body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; } -a, .btn-link { - color: #006bb7; -} - -.btn-primary { - color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; -} - -.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { - box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; -} - -.content { - padding-top: 1.1rem; -} - -h1:focus { - outline: none; -} - .valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; }