Skip to content
This repository was archived by the owner on Jun 2, 2025. It is now read-only.

Commit 69f4faf

Browse files
author
Steph Dietz
committed
add cart component with hard coded data
1 parent 3440b7c commit 69f4faf

File tree

4 files changed

+219
-4
lines changed

4 files changed

+219
-4
lines changed

frontend/app/components/cart.tsx

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
"use client";
2+
3+
import { useEffect, useRef, useState } from "react";
4+
5+
function Price({ amount, currencyCode, className }) {
6+
return (
7+
<span className={className}>
8+
{currencyCode} {amount}
9+
</span>
10+
);
11+
}
12+
13+
function CloseCart() {
14+
return <span>X</span>;
15+
}
16+
17+
function OpenCart({ quantity }) {
18+
return (
19+
<div>
20+
<span>Cart</span>
21+
{quantity > 0 && <span>({quantity})</span>}
22+
</div>
23+
);
24+
}
25+
26+
function DeleteItemButton({ item }) {
27+
return <button>Delete</button>;
28+
}
29+
30+
function EditItemQuantityButton({ item, type }) {
31+
return <button>{type === "minus" ? "-" : "+"}</button>;
32+
}
33+
34+
export default function CartModal() {
35+
const cart = {
36+
totalQuantity: 3,
37+
lines: [
38+
{
39+
quantity: 1,
40+
merchandise: {
41+
product: {
42+
handle: "product-1",
43+
title: "Product 1",
44+
featuredImage: {
45+
url: "/path/to/image1.jpg",
46+
altText: "Product 1 Image",
47+
},
48+
},
49+
title: "Default Option",
50+
selectedOptions: [{ name: "Size", value: "M" }],
51+
},
52+
cost: {
53+
totalAmount: { amount: "10.00", currencyCode: "USD" },
54+
},
55+
},
56+
{
57+
quantity: 2,
58+
merchandise: {
59+
product: {
60+
handle: "product-2",
61+
title: "Product 2",
62+
featuredImage: {
63+
url: "/path/to/image2.jpg",
64+
altText: "Product 2 Image",
65+
},
66+
},
67+
title: "Default Option",
68+
selectedOptions: [{ name: "Size", value: "L" }],
69+
},
70+
cost: {
71+
totalAmount: { amount: "20.00", currencyCode: "USD" },
72+
},
73+
},
74+
],
75+
cost: {
76+
totalTaxAmount: { amount: "2.00", currencyCode: "USD" },
77+
totalAmount: { amount: "32.00", currencyCode: "USD" },
78+
},
79+
checkoutUrl: "/checkout",
80+
};
81+
82+
const [isOpen, setIsOpen] = useState(false);
83+
const quantityRef = useRef(cart.totalQuantity);
84+
const openCart = () => setIsOpen(true);
85+
const closeCart = () => setIsOpen(false);
86+
87+
useEffect(() => {
88+
if (cart.totalQuantity !== quantityRef.current) {
89+
if (!isOpen) {
90+
setIsOpen(true);
91+
}
92+
quantityRef.current = cart.totalQuantity;
93+
}
94+
}, [isOpen, cart.totalQuantity, quantityRef]);
95+
96+
return (
97+
<>
98+
<button aria-label="Open cart" onClick={openCart}>
99+
<OpenCart quantity={cart.totalQuantity} />
100+
</button>
101+
{isOpen && (
102+
<div className="relative z-50">
103+
<div className="fixed inset-0 bg-black/30" aria-hidden="true" />
104+
<div className="fixed bottom-0 right-0 top-0 flex h-full w-full flex-col border-l border-neutral-200 bg-white p-6 text-black md:w-[390px]">
105+
<div className="flex items-center justify-between">
106+
<p className="text-lg font-semibold">My Cart</p>
107+
<button aria-label="Close cart" onClick={closeCart}>
108+
<CloseCart />
109+
</button>
110+
</div>
111+
{cart.lines.length === 0 ? (
112+
<div className="mt-20 flex w-full flex-col items-center justify-center overflow-hidden">
113+
<span className="h-16">🛒</span>
114+
<p className="mt-6 text-center text-2xl font-bold">
115+
Your cart is empty.
116+
</p>
117+
</div>
118+
) : (
119+
<div className="flex h-full flex-col justify-between overflow-hidden p-1">
120+
<ul className="flex-grow overflow-auto py-4">
121+
{cart.lines.map((item, i) => (
122+
<li
123+
key={i}
124+
className="flex w-full flex-col border-b border-neutral-300"
125+
>
126+
<div className="relative flex w-full flex-row justify-between px-1 py-4">
127+
<div className="absolute z-40 -mt-2 ml-[55px]">
128+
<DeleteItemButton item={item} />
129+
</div>
130+
<a
131+
href="#"
132+
onClick={closeCart}
133+
className="z-30 flex flex-row space-x-4"
134+
>
135+
<div className="relative h-16 w-16 cursor-pointer overflow-hidden rounded-md border border-neutral-300 bg-neutral-300">
136+
<img
137+
className="h-full w-full object-cover"
138+
width={64}
139+
height={64}
140+
alt={
141+
item.merchandise.product.featuredImage
142+
.altText || item.merchandise.product.title
143+
}
144+
src={item.merchandise.product.featuredImage.url}
145+
/>
146+
</div>
147+
<div className="flex flex-1 flex-col text-base">
148+
<span className="leading-tight">
149+
{item.merchandise.product.title}
150+
</span>
151+
{item.merchandise.title !== "Default Option" && (
152+
<p className="text-sm text-neutral-500">
153+
{item.merchandise.title}
154+
</p>
155+
)}
156+
</div>
157+
</a>
158+
<div className="flex h-16 flex-col justify-between">
159+
<Price
160+
className="flex justify-end space-y-2 text-right text-sm"
161+
amount={item.cost.totalAmount.amount}
162+
currencyCode={item.cost.totalAmount.currencyCode}
163+
/>
164+
<div className="ml-auto flex h-9 flex-row items-center rounded-full border border-neutral-200">
165+
<EditItemQuantityButton item={item} type="minus" />
166+
<p className="w-6 text-center">
167+
<span className="w-full text-sm">
168+
{item.quantity}
169+
</span>
170+
</p>
171+
<EditItemQuantityButton item={item} type="plus" />
172+
</div>
173+
</div>
174+
</div>
175+
</li>
176+
))}
177+
</ul>
178+
<div className="py-4 text-sm text-neutral-500">
179+
<div className="mb-3 flex items-center justify-between border-b border-neutral-200 pb-1">
180+
<p>Taxes</p>
181+
<Price
182+
className="text-right text-base text-black"
183+
amount={cart.cost.totalTaxAmount.amount}
184+
currencyCode={cart.cost.totalTaxAmount.currencyCode}
185+
/>
186+
</div>
187+
<div className="mb-3 flex items-center justify-between border-b border-neutral-200 pb-1 pt-1">
188+
<p>Shipping</p>
189+
<p className="text-right">Calculated at checkout</p>
190+
</div>
191+
<div className="mb-3 flex items-center justify-between border-b border-neutral-200 pb-1 pt-1">
192+
<p>Total</p>
193+
<Price
194+
className="text-right text-base text-black"
195+
amount={cart.cost.totalAmount.amount}
196+
currencyCode={cart.cost.totalAmount.currencyCode}
197+
/>
198+
</div>
199+
</div>
200+
<a
201+
href={cart.checkoutUrl}
202+
className="block w-full rounded-full bg-blue-600 p-3 text-center text-sm font-medium text-white opacity-90 hover:opacity-100"
203+
>
204+
Proceed to Checkout
205+
</a>
206+
</div>
207+
)}
208+
</div>
209+
</div>
210+
)}
211+
</>
212+
);
213+
}

frontend/app/components/header.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ShoppingCartIcon } from "@heroicons/react/24/outline";
33
import { Suspense } from "react";
44
import { SearchInputSkeleton } from "./skeletons";
55
import Logo from "./logo";
6+
import Cart from "./cart";
67

78
export default function Header() {
89
return (
@@ -14,9 +15,10 @@ export default function Header() {
1415
<Suspense fallback={<SearchInputSkeleton />}>
1516
<Search />
1617
</Suspense>
17-
<button className="relative flex h-11 w-11 items-center justify-center rounded-md border transition-colors border-neutral-700 text-white">
18+
{/* <button className="relative flex h-11 w-11 items-center justify-center rounded-md border transition-colors border-neutral-700 text-white">
1819
<ShoppingCartIcon className="h-4 transition-all ease-in-out hover:scale-110" />
19-
</button>
20+
</button> */}
21+
<Cart />
2022
</div>
2123
<div className="md:hidden items-center justify-between px-4">
2224
<div className="flex items-center justify-between mb-2">

frontend/app/components/logo.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default function Logo() {
1414
className="fill-foreground"
1515
viewBox="0 0 130 124"
1616
>
17-
<g clip-path="url(#a)">
17+
<g clipPath="url(#a)">
1818
<path
1919
fill="#fff"
2020
d="M90.2 0 76 55.5H39.5L53.7 0H14.2L0 55.5h32.3l-16.7 65.8 60.3-65.4-17.2 67.2h39.5L129.7 0z"

frontend/public/logo-dark.svg

Lines changed: 1 addition & 1 deletion
Loading

0 commit comments

Comments
 (0)