Skip to content

Commit 1e90cd1

Browse files
Add cart to frontend
1 parent 53ff135 commit 1e90cd1

20 files changed

+1550
-97
lines changed

src/SourceSchemas/Cart/Data/Cart.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,4 @@ public class Cart
99

1010
[Required]
1111
public DateTime CreatedAt { get; set; }
12-
13-
public ICollection<CartItem> Items { get; set; } = new List<CartItem>();
1412
}

src/SourceSchemas/Cart/Data/CartContext.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,17 @@ public static async Task SeedDataAsync(
1919

2020
// Ensure database is created
2121
await context.Database.EnsureCreatedAsync(cancellationToken);
22+
23+
// Create initial cart if none exists
24+
if (!await context.Carts.AnyAsync(cancellationToken))
25+
{
26+
var cart = new Cart
27+
{
28+
CreatedAt = DateTime.UtcNow
29+
};
30+
31+
context.Carts.Add(cart);
32+
await context.SaveChangesAsync(cancellationToken);
33+
}
2234
}
2335
}

src/SourceSchemas/Cart/Types/CartItemNode.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ static partial void Configure(IObjectTypeDescriptor<Data.CartItem> descriptor)
88
descriptor.Ignore(x => x.CartId);
99
}
1010

11+
[ID]
12+
public static int GetId([Parent] Data.CartItem cartItem)
13+
=> cartItem.Id;
14+
1115
public static DateTime GetAddedAt([Parent] Data.CartItem cartItem)
1216
=> cartItem.AddedAt;
1317

src/SourceSchemas/Cart/Types/CartMutations.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ namespace Demo.Cart.Types;
66
[MutationType]
77
public static partial class CartMutations
88
{
9-
public static async Task<Data.Cart> AddToCartAsync(
9+
public static async Task<Data.Cart> AddProductToCartAsync(
1010
[ID<Product>] int productId,
1111
CartContext context,
1212
CancellationToken cancellationToken)
1313
{
1414
var cart = await context.Carts
15-
.Include(c => c.Items)
1615
.FirstOrDefaultAsync(cancellationToken);
1716

1817
if (cart is null)
@@ -32,30 +31,30 @@ public static partial class CartMutations
3231
AddedAt = DateTime.UtcNow
3332
};
3433

35-
cart.Items.Add(cartItem);
34+
context.CartItems.Add(cartItem);
3635
await context.SaveChangesAsync(cancellationToken);
3736

3837
return cart;
3938
}
4039

41-
public static async Task<Data.Cart?> RemoveFromCartAsync(
40+
public static async Task<Data.Cart?> RemoveProductFromCartAsync(
4241
[ID<Product>] int productId,
4342
CartContext context,
4443
CancellationToken cancellationToken)
4544
{
4645
var cart = await context.Carts
47-
.Include(c => c.Items)
4846
.FirstOrDefaultAsync(cancellationToken);
4947

5048
if (cart is null)
5149
{
5250
return null;
5351
}
5452

55-
var itemToRemove = cart.Items.FirstOrDefault(i => i.ProductId == productId);
53+
var itemToRemove = await context.CartItems
54+
.FirstOrDefaultAsync(i => i.CartId == cart.Id && i.ProductId == productId, cancellationToken);
55+
5656
if (itemToRemove is not null)
5757
{
58-
cart.Items.Remove(itemToRemove);
5958
context.CartItems.Remove(itemToRemove);
6059
await context.SaveChangesAsync(cancellationToken);
6160
}
@@ -68,16 +67,18 @@ public static partial class CartMutations
6867
CancellationToken cancellationToken)
6968
{
7069
var cart = await context.Carts
71-
.Include(c => c.Items)
7270
.FirstOrDefaultAsync(cancellationToken);
7371

7472
if (cart is null)
7573
{
7674
return null;
7775
}
7876

79-
context.CartItems.RemoveRange(cart.Items);
80-
cart.Items.Clear();
77+
var cartItems = await context.CartItems
78+
.Where(i => i.CartId == cart.Id)
79+
.ToListAsync(cancellationToken);
80+
81+
context.CartItems.RemoveRange(cartItems);
8182
await context.SaveChangesAsync(cancellationToken);
8283

8384
return cart;
Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
1+
using Demo.Cart.Data;
2+
using GreenDonut.Data;
3+
using HotChocolate.Types.Pagination;
4+
15
namespace Demo.Cart.Types;
26

37
[ObjectType<Data.Cart>]
48
public static partial class CartNode
59
{
6-
public static DateTime GetCreatedAt([Parent] Data.Cart cart)
7-
=> cart.CreatedAt;
8-
9-
[UsePaging]
10-
public static IEnumerable<Data.CartItem> GetItems([Parent] Data.Cart cart)
11-
=> cart.Items;
10+
[ID]
11+
public static int GetId([Parent] Data.Cart cart)
12+
=> cart.Id;
13+
14+
[UsePaging(ConnectionName = "CartItems")]
15+
public static async Task<Connection<CartItem>> GetItemsAsync(
16+
[Parent] Data.Cart cart,
17+
PagingArguments pagingArgs,
18+
CartContext context,
19+
CancellationToken cancellationToken)
20+
{
21+
return await context.CartItems
22+
.Where(item => item.CartId == cart.Id)
23+
.OrderBy(item => item.AddedAt)
24+
.ToPageAsync(pagingArgs, cancellationToken)
25+
.ToConnectionAsync();
26+
}
1227
}

src/SourceSchemas/Cart/Types/Viewer.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,24 @@ namespace Demo.Cart.Types;
55

66
public class Viewer
77
{
8-
public async Task<Data.Cart?> GetCartAsync(
8+
public async Task<Data.Cart> GetCartAsync(
99
CartContext context,
1010
CancellationToken cancellationToken)
1111
{
12-
return await context.Carts
13-
.Include(c => c.Items)
12+
var cart = await context.Carts
1413
.FirstOrDefaultAsync(cancellationToken);
14+
15+
if (cart is null)
16+
{
17+
cart = new Data.Cart
18+
{
19+
CreatedAt = DateTime.UtcNow
20+
};
21+
22+
context.Carts.Add(cart);
23+
await context.SaveChangesAsync(cancellationToken);
24+
}
25+
26+
return cart;
1527
}
1628
}

src/SourceSchemas/Cart/schema-settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"extensions": {
1010
"nitro": {
11-
"apiId": "TODO"
11+
"apiId": "QXBpCmcwMTlhYTAyZWQxYmY3ZmExYjY1NDZhODYwZGM1YTA3Yw=="
1212
}
1313
},
1414
"environments": {

src/frontend/src/App.css

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/frontend/src/App.tsx

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,63 @@
1+
import { useState } from "react";
12
import ProductsPage from "./ProductsPage";
3+
import {
4+
AppBar,
5+
Toolbar,
6+
Typography,
7+
IconButton,
8+
Box,
9+
} from "@mui/material";
10+
import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
11+
import CartPopover from "./CartPopover";
12+
13+
const headingStyles = {
14+
fontWeight: 900,
15+
fontSize: { xs: "1.75rem", sm: "2.25rem", md: "2.5rem" },
16+
textTransform: "uppercase",
17+
letterSpacing: "0.1em",
18+
color: "white",
19+
flexGrow: 1,
20+
textAlign: "center",
21+
} as const;
222

323
function App() {
4-
return <ProductsPage />;
24+
const [cartAnchorEl, setCartAnchorEl] = useState<HTMLElement | null>(null);
25+
const cartOpen = Boolean(cartAnchorEl);
26+
27+
const handleCartClick = (event: React.MouseEvent<HTMLElement>) => {
28+
setCartAnchorEl(event.currentTarget);
29+
};
30+
31+
const handleCartClose = () => {
32+
setCartAnchorEl(null);
33+
};
34+
35+
return (
36+
<>
37+
<AppBar position="fixed">
38+
<Toolbar>
39+
<Typography variant="h4" component="h1" sx={headingStyles}>
40+
Products
41+
</Typography>
42+
<IconButton
43+
onClick={handleCartClick}
44+
color="inherit"
45+
size="large"
46+
>
47+
<ShoppingCartIcon fontSize="large" />
48+
</IconButton>
49+
</Toolbar>
50+
</AppBar>
51+
<CartPopover
52+
anchorEl={cartAnchorEl}
53+
open={cartOpen}
54+
onClose={handleCartClose}
55+
/>
56+
<Box sx={{ pt: { xs: 10, sm: 12 } }}>
57+
<ProductsPage />
58+
</Box>
59+
</>
60+
);
561
}
662

763
export default App;

0 commit comments

Comments
 (0)