Add To Cart - Functionality complete
This commit is contained in:
parent
0ef69dd498
commit
3bf434124c
|
@ -21,10 +21,10 @@ namespace ShopOnline.Api.Controllers
|
|||
try
|
||||
{
|
||||
var products = await this.productRepository.GetItems();
|
||||
//foreach (var product in products)
|
||||
//{
|
||||
// Console.WriteLine("Product [" + product.Id + "]: " + product.Price + " / " + product.Quantity);
|
||||
//}
|
||||
foreach (var product in products)
|
||||
{
|
||||
Console.WriteLine("Product [" + product.Id + "]: " + product.Price + " / " + product.Quantity);
|
||||
}
|
||||
|
||||
var productCategories = await this.productRepository.GetCategories();
|
||||
if (products == null || productCategories == null)
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ShopOnline.Api.Extensions;
|
||||
using ShopOnline.Api.Repositories.Contracts;
|
||||
using ShopOnline.Models.Dtos;
|
||||
|
||||
namespace ShopOnline.Api.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class ShoppingCartController : ControllerBase
|
||||
{
|
||||
private readonly IShoppingCartRepository shoppingCartRepository;
|
||||
private readonly IProductRepository productRepository;
|
||||
|
||||
public ShoppingCartController(IShoppingCartRepository shoppingCartRepository,
|
||||
IProductRepository productRepository)
|
||||
{
|
||||
this.shoppingCartRepository = shoppingCartRepository;
|
||||
this.productRepository = productRepository;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{userId}/GetItems")]
|
||||
public async Task<ActionResult<IEnumerable<CartItemDto>>> GetItems(int userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cartItems = await shoppingCartRepository.GetItems(userId);
|
||||
|
||||
if(cartItems == null)
|
||||
{
|
||||
return NoContent();
|
||||
}
|
||||
var products = await this.productRepository.GetItems();
|
||||
|
||||
if(products == null)
|
||||
{
|
||||
throw new Exception("No products exist in the system.");
|
||||
}
|
||||
|
||||
var cartItemsDto = cartItems.ConvertToDto(products);
|
||||
|
||||
return Ok(cartItemsDto);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}")]
|
||||
public async Task<ActionResult<CartItemDto>> GetItem(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cartItem = await this.shoppingCartRepository.GetItem(id);
|
||||
if (cartItem == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var product = await productRepository.GetItem(cartItem.ProductId);
|
||||
if(product == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var cartItemDto = cartItem.ConvertToDto(product);
|
||||
return Ok(cartItemDto);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<CartItemDto>> PostItem([FromBody] CartItemToAddDto cartItemToAddDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var newCartItem = await this.shoppingCartRepository.AddItem(cartItemToAddDto);
|
||||
if(newCartItem == null)
|
||||
{
|
||||
return NoContent();
|
||||
}
|
||||
var product = await productRepository.GetItem(newCartItem.ProductId);
|
||||
if(product == null)
|
||||
{
|
||||
throw new Exception($"Something went wrong when attempting to retrieve product (productID:({cartItemToAddDto.ProductId}))");
|
||||
}
|
||||
|
||||
var newCartItemDto = newCartItem.ConvertToDto(product);
|
||||
return CreatedAtAction(nameof(GetItem), new { id = newCartItemDto.Id }, newCartItemDto);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,5 +40,40 @@ namespace ShopOnline.Api.Extensions
|
|||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<CartItemDto> ConvertToDto(this IEnumerable<CartItem> cartItems, IEnumerable<Product> products)
|
||||
{
|
||||
return (from cartItem in cartItems
|
||||
join product in products
|
||||
on cartItem.ProductId equals product.Id
|
||||
select new CartItemDto
|
||||
{
|
||||
Id = cartItem.Id,
|
||||
ProductId = cartItem.ProductId,
|
||||
ProductName = product.Name,
|
||||
ProductDescription = product.Description,
|
||||
ProductImageURL = product.ImageURL,
|
||||
Price = product.Price,
|
||||
CartId = cartItem.CartId,
|
||||
Quantity = cartItem.Quantity,
|
||||
TotalPrice = product.Price * cartItem.Quantity
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public static CartItemDto ConvertToDto(this CartItem cartItem, Product product)
|
||||
{
|
||||
return new CartItemDto
|
||||
{
|
||||
Id = cartItem.Id,
|
||||
ProductId = cartItem.ProductId,
|
||||
ProductName = product.Name,
|
||||
ProductDescription = product.Description,
|
||||
ProductImageURL = product.ImageURL,
|
||||
Price = product.Price,
|
||||
CartId = cartItem.CartId,
|
||||
Quantity = cartItem.Quantity,
|
||||
TotalPrice = product.Price * cartItem.Quantity
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ builder.Services.AddDbContextPool<ShopOnlineDbContext>(options =>
|
|||
);
|
||||
|
||||
builder.Services.AddScoped<IProductRepository, ProductRepository>();
|
||||
builder.Services.AddScoped<IShoppingCartRepository, ShoppingCartRepository>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
using ShopOnline.Api.Entities;
|
||||
using ShopOnline.Models.Dtos;
|
||||
|
||||
namespace ShopOnline.Api.Repositories.Contracts
|
||||
{
|
||||
public interface IShoppingCartRepository
|
||||
{
|
||||
Task<CartItem>
|
||||
AddItem(CartItemToAddDto cartItemToAddDto);
|
||||
Task<CartItem> UpdateQuantity(int id, CartItemQuantityUpdateDto cartItemQuantityUpdateDto);
|
||||
Task<CartItem> DeleteItem(int id);
|
||||
Task<CartItem> GetItem(int id);
|
||||
Task<IEnumerable<CartItem>> GetItems(int userId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using ShopOnline.Api.Data;
|
||||
using ShopOnline.Api.Entities;
|
||||
using ShopOnline.Api.Repositories.Contracts;
|
||||
using ShopOnline.Models.Dtos;
|
||||
|
||||
namespace ShopOnline.Api.Repositories
|
||||
{
|
||||
public class ShoppingCartRepository : IShoppingCartRepository
|
||||
{
|
||||
private readonly ShopOnlineDbContext shopOnlineDbContext;
|
||||
|
||||
public ShoppingCartRepository(ShopOnlineDbContext shopOnlineDbContext)
|
||||
{
|
||||
this.shopOnlineDbContext = shopOnlineDbContext;
|
||||
}
|
||||
|
||||
private async Task<bool> CartItemExists(int cartId, int productId)
|
||||
{
|
||||
return await this.shopOnlineDbContext.CartItems.AnyAsync(c => c.CartId == cartId && c.ProductId == productId);
|
||||
}
|
||||
public async Task<CartItem> AddItem(CartItemToAddDto cartItemToAddDto)
|
||||
{
|
||||
if(await CartItemExists(cartItemToAddDto.CartId, cartItemToAddDto.ProductId) == false)
|
||||
{
|
||||
var item = await (from product in this.shopOnlineDbContext.Products
|
||||
where product.Id == cartItemToAddDto.ProductId
|
||||
select new CartItem
|
||||
{
|
||||
CartId = cartItemToAddDto.CartId,
|
||||
ProductId = product.Id,
|
||||
Quantity = cartItemToAddDto.Quantity,
|
||||
}).SingleOrDefaultAsync();
|
||||
|
||||
if(item != null)
|
||||
{
|
||||
var result = await this.shopOnlineDbContext.CartItems.AddAsync(item);
|
||||
await this.shopOnlineDbContext.SaveChangesAsync();
|
||||
return result.Entity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Task<CartItem> DeleteItem(int id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<CartItem> GetItem(int id)
|
||||
{
|
||||
return await (from cart in this.shopOnlineDbContext.Carts
|
||||
join cartItem in this.shopOnlineDbContext.CartItems
|
||||
on cart.Id equals cartItem.CartId
|
||||
where cartItem.Id == id
|
||||
select new CartItem
|
||||
{
|
||||
Id = cartItem.Id,
|
||||
ProductId = cartItem.ProductId,
|
||||
Quantity = cartItem.Quantity,
|
||||
CartId = cartItem.CartId
|
||||
}).SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CartItem>> GetItems(int userId)
|
||||
{
|
||||
return await (from cart in this.shopOnlineDbContext.Carts
|
||||
join cartItem in this.shopOnlineDbContext.CartItems
|
||||
on cart.Id equals cartItem.CartId
|
||||
where cart.UserId == userId
|
||||
select new CartItem
|
||||
{
|
||||
Id = cartItem.Id,
|
||||
ProductId = cartItem.ProductId,
|
||||
Quantity = cartItem.Quantity,
|
||||
CartId = cartItem.CartId
|
||||
}).ToListAsync();
|
||||
}
|
||||
|
||||
public Task<CartItem> UpdateQuantity(int id, CartItemQuantityUpdateDto cartItemQuantityUpdateDto)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace ShopOnline.Web
|
||||
{
|
||||
public static class HardCoded
|
||||
{
|
||||
public const int UserId = 1;
|
||||
public const int CartId = 1;
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
height: 64px;
|
||||
margin: 8px;
|
||||
border-radius: 50%;
|
||||
background: black;
|
||||
background: #ffde00;
|
||||
animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,14 @@ else
|
|||
<p class="mb-4">
|
||||
<b>@Product.Price.ToString("C") (@Product.Quantity in stock)</b>
|
||||
</p>
|
||||
<div>
|
||||
<button class="btn btn-success"
|
||||
@onclick = "() => AddToCart_Click(new CartItemToAddDto {
|
||||
CartId = HardCoded.CartId, ProductId = Product.Id, Quantity = 1,
|
||||
})"
|
||||
>Add To Cart</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
|
@ -11,6 +11,10 @@ namespace ShopOnline.Web.Pages
|
|||
public int Id { get; set; }
|
||||
[Inject]
|
||||
public IProductService ProductService { get; set; }
|
||||
[Inject]
|
||||
public IShoppingCartService ShoppingCartService { get; set; }
|
||||
[Inject]
|
||||
public NavigationManager NavigationManager { get; set; }
|
||||
public ProductDto Product { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
|
@ -25,5 +29,19 @@ namespace ShopOnline.Web.Pages
|
|||
ErrorMessage = ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task AddToCart_Click(CartItemToAddDto cartItemToAddDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cartItemDto = await ShoppingCartService.AddItem(cartItemToAddDto);
|
||||
NavigationManager.NavigateTo("/ShoppingCart");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
@page "/ShoppingCart"
|
||||
@inherits ShoppingCartBase
|
||||
|
||||
@if(ShoppingCartItems == null && ErrorMessage == null) {
|
||||
<DisplaySpinner></DisplaySpinner>
|
||||
}
|
||||
else if(ErrorMessage != null) {
|
||||
<DisplayError ErrorMessage="@ErrorMessage"></DisplayError>
|
||||
}
|
||||
else {
|
||||
<h3 class="mb-5">Shopping Cart</h3>
|
||||
<div class="row mb-5">
|
||||
<div class="col-md-9">
|
||||
@foreach(var item in ShoppingCartItems) {
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<img src="@item.ProductImageURL" width="300" class="img-thumbnail" >
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<h5>@item.ProductName</h5>
|
||||
<div class="mb-4">@item.ProductDescription</div>
|
||||
<span>Price: <b>@item.Price.ToString("C")</b></span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h5>Cart Summary</h5>
|
||||
<div class="mt-2">
|
||||
<div>Total - </div>
|
||||
<a href="#" class="btn btn-success">
|
||||
<span class="oi oi-credit-card"></span> Proceed to Checkout
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using ShopOnline.Models.Dtos;
|
||||
using ShopOnline.Web.Services.Contracts;
|
||||
|
||||
namespace ShopOnline.Web.Pages
|
||||
{
|
||||
public class ShoppingCartBase:ComponentBase
|
||||
{
|
||||
[Inject]
|
||||
public IShoppingCartService ShoppingCartService { get; set; }
|
||||
|
||||
public IEnumerable<CartItemDto> ShoppingCartItems { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
ShoppingCartItems = await ShoppingCartService.GetItems(HardCoded.UserId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -10,5 +10,6 @@ builder.RootComponents.Add<HeadOutlet>("head::after");
|
|||
|
||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("https://localhost:7125/") });
|
||||
builder.Services.AddScoped<IProductService, ProductService>();
|
||||
builder.Services.AddScoped<IShoppingCartService, ShoppingCartService>();
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
using ShopOnline.Models.Dtos;
|
||||
|
||||
namespace ShopOnline.Web.Services.Contracts
|
||||
{
|
||||
public interface IShoppingCartService
|
||||
{
|
||||
Task<IEnumerable<CartItemDto>> GetItems(int userId);
|
||||
Task<CartItemDto> AddItem(CartItemToAddDto cartItemToAddDto);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
using ShopOnline.Models.Dtos;
|
||||
using ShopOnline.Web.Services.Contracts;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace ShopOnline.Web.Services
|
||||
{
|
||||
public class ShoppingCartService : IShoppingCartService
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
public ShoppingCartService(HttpClient httpClient)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
public async Task<CartItemDto> AddItem(CartItemToAddDto cartItemToAddDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await httpClient.PostAsJsonAsync<CartItemToAddDto>("api/ShoppingCart", cartItemToAddDto);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
if(response.StatusCode == System.Net.HttpStatusCode.NoContent)
|
||||
{
|
||||
return default(CartItemDto);
|
||||
}
|
||||
return await response.Content.ReadFromJsonAsync<CartItemDto>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = await response.Content.ReadAsStringAsync();
|
||||
throw new Exception($"Http status: {response.StatusCode}\n\tMessage: {message}");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CartItemDto>> GetItems(int userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await httpClient.GetAsync($"api/ShoppingCart/{userId}/GetItems");
|
||||
if(response.IsSuccessStatusCode)
|
||||
{
|
||||
if(response.StatusCode == System.Net.HttpStatusCode.NoContent)
|
||||
{
|
||||
return Enumerable.Empty<CartItemDto>();
|
||||
}
|
||||
return await response.Content.ReadFromJsonAsync<IEnumerable<CartItemDto>>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = await response.Content.ReadAsStringAsync();
|
||||
throw new Exception($"Http status: {response.StatusCode}\n\tMessage: {message}");
|
||||
|
||||
}
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShopOnline.Web", "ShopOnlin
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShopOnline.Api", "ShopOnline.Api\ShopOnline.Api.csproj", "{B1FE41C0-D80F-414D-AE0F-3164789CF796}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShopOnline.Models", "ShopOnline.Models\ShopOnline.Models.csproj", "{C0674431-3C6E-4F51-8A5C-0523243EB33D}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShopOnline.Models", "ShopOnline.Models\ShopOnline.Models.csproj", "{C0674431-3C6E-4F51-8A5C-0523243EB33D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
Loading…
Reference in New Issue