Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shirt sizes and quantities #101

Merged
merged 9 commits into from
Oct 12, 2024
2 changes: 2 additions & 0 deletions src/components/App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Tours from '../Pages/Tours/Tours';
import Contact from '../Pages/Contact/Contact';
import Purchase from '../Pages/Purchase/Purchase';
import NotFound from '../Pages/NotFound/NotFound';
import FloatingCartButton from '../FloatingCartButton/FloatingCartButton';
import './App.css';

import { MerchCartProvider } from '../../context/MerchCartContext';
Expand All @@ -37,6 +38,7 @@ function App() {
<Route path='*' Component={ NotFound } />
</Routes>
</BrowserRouter>
<FloatingCartButton />
</MerchCartProvider>
<Footer />
</div>
Expand Down
41 changes: 21 additions & 20 deletions src/components/CartItem/CartItem.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
import { Button, Stack } from "react-bootstrap";
import React from "react";
import formatCurrency from "../../utilities/formatCurrency";
import { useMerchCart } from "../../context/MerchCartContext";
import merchItems from '../../merchdata/merchitems.json';
import formatCurrency from "../../utilities/formatCurrency";

export function CartItem({ id, quantity }) {
export function CartItem({ id, quantity, size }) {
const { removeFromCart } = useMerchCart();
const item = merchItems.find(i => i.id === id);

if (item == null) return null;

return(
<Stack direction="horizontal" gap={2} className="d-flex align-items-center">
return (
<div className="d-flex align-items-center">
<img
src={item.images[0]}
style={{ width: "125px", height: "75px", objectFit: "cover" }}
alt="merch-item"
alt={item.name}
style={{ width: "125px", height: "auto", objectFit: "cover" }}
/>

<div className="me-auto">
<div>
{item.name}{" "}
{quantity > 1 && (
<span className="text-muted" style={{fontSize: ".65rem"}}>
{quantity}x
</span>
)}
{size && <span className="text-muted" style={{ fontSize: ".75rem" }}>({size.toUpperCase()})</span>}
</div>
<div className="text-muted" style={{ fontSize: ".75rem"}}>
{formatCurrency(item.price)}
<div className="text-muted" style={{ fontSize: ".75rem" }}>
{formatCurrency(item.price)} x {quantity}
</div>
<div>{formatCurrency(item.price * quantity)}</div>
<Button variant="outline-danger" size="sm" onClick={() => removeFromCart(item.id)}>&times;</Button>
</div>
</Stack>
)
}
<div>{formatCurrency(item.price * quantity)}</div>
<button
className="btn btn-outline-danger btn-sm"
onClick={() => removeFromCart(id, size)}
>
&times;
</button>
</div>
);
}
42 changes: 42 additions & 0 deletions src/components/FloatingCartButton/FloatingCartButton.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.floating-cart-button {
position: fixed;
bottom: 90px;
right: 50px;
background: none;
border: none;
cursor: pointer;
z-index: 1001;
filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.15));
transition: transform 0.3s ease, filter 0.3s ease;
}

.floating-cart-button:hover {
transform: translateY(-5px);
filter: drop-shadow(0 12px 20px rgba(0, 0, 0, 0.2));
}

.cart-notification {
position: absolute;
top: -10px;
right: -10px;
background-color: #ff3e3e;
color: white;
border-radius: 50%;
padding: 6px 8px;
font-size: 14px;
font-weight: bold;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
animation: pulse 2s infinite;
}

@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
26 changes: 26 additions & 0 deletions src/components/FloatingCartButton/FloatingCartButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { useMerchCart } from '../../context/MerchCartContext';
import './FloatingCartButton.css';

export default function FloatingCartButton() {
const { openCart, cartQuantity } = useMerchCart();

if (cartQuantity === 0) return null; // Don't render if cart is empty

return (
<button onClick={openCart}
className="floating-cart-button">
<svg className='cart-svg' fill="#ffffff" width="30" height="30" viewBox="0 0 902.86 902.86" stroke="#ffffff">
<g strokeWidth="0" />
<g strokeLinecap="round" strokeLinejoin="round" />
<g>
<g>
<path d="M671.504,577.829l110.485-432.609H902.86v-68H729.174L703.128,179.2L0,178.697l74.753,399.129h596.751V577.829z M685.766,247.188l-67.077,262.64H131.199L81.928,246.756L685.766,247.188z" />
<path d="M578.418,825.641c59.961,0,108.743-48.783,108.743-108.744s-48.782-108.742-108.743-108.742H168.717c-59.961,0-108.744,48.781-108.744,108.742s48.782,108.744,108.744,108.744c59.962,0,108.743-48.783,108.743-108.744c0-14.4-2.821-28.152-7.927-40.742h208.069c-5.107,12.59-7.928,26.342-7.928,40.742C469.675,776.858,518.457,825.641,578.418,825.641z M209.46,716.897c0,22.467-18.277,40.744-40.743,40.744c-22.466,0-40.744-18.277-40.744-40.744c0-22.465,18.277-40.742,40.744-40.742C191.183,676.155,209.46,694.432,209.46,716.897z M619.162,716.897c0,22.467-18.277,40.744-40.743,40.744s-40.743-18.277-40.743-40.744c0-22.465,18.277-40.742,40.743-40.742S619.162,694.432,619.162,716.897z" />
</g>
</g>
</svg>
<div className='cart-notification'>{cartQuantity}</div>
</button>
);
}
10 changes: 5 additions & 5 deletions src/components/Footer/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ const albumTracks = [

export default function Footer() {
const [selectedTrack, setSelectedTrack] = useState(null); // Initialize with null
const [isMinimized, setIsMinimized] = useState(false);
const [isMinimized, setIsMinimized] = useState(true);

// Function to select a random track
const getRandomTrack = () => {
const randomIndex = Math.floor(Math.random() * albumTracks.length);
return albumTracks[randomIndex].path;
};
// const getRandomTrack = () => {
// const randomIndex = Math.floor(Math.random() * albumTracks.length);
// return albumTracks[randomIndex].path;
// };

// useEffect(() => {
// // Set a random track when the component mounts
Expand Down
10 changes: 5 additions & 5 deletions src/components/MerchCart/MerchCart.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@ export function MerchCart({ isOpen }) {
</Offcanvas.Header>
<Offcanvas.Body>
<Stack gap={3}>
{cartItems.map(item => (
<CartItem key={item.id} {...item} />
{cartItems.map((item) => (
<CartItem key={`${item.id}-${item.size}`} {...item} />
))}
<div className="ms-auto fw-bold fs-5">
Total{" "}
{formatCurrency(
cartItems.reduce((total, cartItem) => {
const item = merchItems.find(i => i.id === cartItem.id)
return total + (item?.price || 0) * cartItem.quantity
const item = merchItems.find(i => i.id === cartItem.id);
return total + (item?.price || 0) * cartItem.quantity;
}, 0)
)}
</div>
<div className="ms-auto fs-5">
Page Under Construction
<div className="ms-auto fs-6">
For all merch & size inqueries, please message us on Instagram @maldevera, or any social media platform.
For all merch & size inquiries, please message us on Instagram @maldevera, or any social media platform.
</div>
</div>
</Stack>
Expand Down
20 changes: 0 additions & 20 deletions src/components/NavBar/NavBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,6 @@ export default function NavBar() {
<NavLink className="my-nav-link" to='/about'>ABOUT</NavLink>
<NavLink className="my-nav-link" to='/shows'>SHOWS</NavLink>
<NavLink className="my-nav-link" to='/contact'>CONTACT</NavLink>
{cartQuantity > 0 && (
<button onClick={openCart}
className="my-nav-link"
style={{ position: 'relative',
background: 'none',
border: 'none',
cursor: 'pointer' }}>
<svg className='cart-svg' fill="#ffffff" width="30" height="30" viewBox="0 0 902.86 902.86" stroke="#ffffff">
<g strokeWidth="0" />
<g strokeLinecap="round" strokeLinejoin="round" />
<g>
<g>
<path d="M671.504,577.829l110.485-432.609H902.86v-68H729.174L703.128,179.2L0,178.697l74.753,399.129h596.751V577.829z M685.766,247.188l-67.077,262.64H131.199L81.928,246.756L685.766,247.188z" />
<path d="M578.418,825.641c59.961,0,108.743-48.783,108.743-108.744s-48.782-108.742-108.743-108.742H168.717c-59.961,0-108.744,48.781-108.744,108.742s48.782,108.744,108.744,108.744c59.962,0,108.743-48.783,108.743-108.744c0-14.4-2.821-28.152-7.927-40.742h208.069c-5.107,12.59-7.928,26.342-7.928,40.742C469.675,776.858,518.457,825.641,578.418,825.641z M209.46,716.897c0,22.467-18.277,40.744-40.743,40.744c-22.466,0-40.744-18.277-40.744-40.744c0-22.465,18.277-40.742,40.744-40.742C191.183,676.155,209.46,694.432,209.46,716.897z M619.162,716.897c0,22.467-18.277,40.744-40.743,40.744s-40.743-18.277-40.743-40.744c0-22.465,18.277-40.742,40.743-40.742S619.162,694.432,619.162,716.897z" />
</g>
</g>
</svg>
<div className='cart-notification'>{cartQuantity}</div>
</button>
)}
</div>
</div>
)
Expand Down
35 changes: 34 additions & 1 deletion src/components/Pages/Merch/StoreItem/StoreItem.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,37 @@
.store-item-purchase-button:hover {
cursor: pointer;
background-color: orange;
}
}



.size-select {
border: none;
border-radius: 40px;
text-align: center;
margin: 10px;
}
.size-options {
text-align: center;
}





@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-10px);
}
60% {
transform: translateY(-5px);
}
}

button[disabled] {
cursor: not-allowed;
opacity: 0.8;
}
96 changes: 64 additions & 32 deletions src/components/Pages/Merch/StoreItem/StoreItem.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,79 @@
import React from "react";
import React, { useState } from "react";
import formatCurrency from "../../../../utilities/formatCurrency";
import './StoreItem.css';
import { useMerchCart } from "../../../../context/MerchCartContext";


export default function StoreItem({ item }) {
const {
getItemQuantity,
increaseItemQuantity,
decreaseItemQuantity,
removeFromCart
increaseItemQuantity
} = useMerchCart();

const quantity = getItemQuantity(item.id);
const [selectedSize, setSelectedSize] = useState(""); // Track selected size
const [availableSizes, setAvailableSizes] = useState(item.sizes); // Track available sizes
const [itemAdded, setItemAdded] = useState(false); // Track if item is added to cart

// Handle size selection
const handleSizeChange = (event) => {
setSelectedSize(event.target.value);
};

// Handle adding to cart and updating size quantity
const handleAddToCart = () => {
if (selectedSize) {
if (availableSizes[selectedSize] > 0) {
increaseItemQuantity(item.id, selectedSize); // Pass selected size to cart
setItemAdded(true); // Set item added to true
// Optional: Reset back to "Add to Cart" after a delay
setTimeout(() => setItemAdded(false), 2000); // Reset after 2 seconds
} else {
alert("Selected size is out of stock");
}
} else {
alert("Please select a size");
}
};

return (
<div className="store-item-wrapper">
{
// item.images?.length < 2 ?
<img className="store-item-image" src={ item.images[0] } alt={ `${ item.description }` } />
// : <ImageSwiper images={ item.images } /> //? account for this later
}
<img className="store-item-image" src={item.images[0]} alt={`${item.description}`} />
<div className="store-item-info">
<div className="store-item-name">
{ item.name.toUpperCase() }
</div>
<div className="store-item-price">
{formatCurrency(item.price)}
<div className="store-item-name">{item.name.toUpperCase()}</div>
<div className="store-item-price">{formatCurrency(item.price)}</div>

{/* Size selection dropdown */}
<div className="store-item-sizes">
<select
id={`size-select-${item.id}`}
value={selectedSize}
onChange={handleSizeChange}
className="size-select"
>
<option className="size-options" value="">🤘Select Size🤘</option>
{Object.keys(availableSizes).map((size) => (
<option key={size} value={size} disabled={availableSizes[size] === 0}>
{size.toUpperCase()}
</option>
))}
</select>
</div>
{quantity === 0 ? (
<button style={{ border: "none", borderRadius: "20px", cursor: "pointer", width: "auto", alignSelf: "center" }} onClick={() => increaseItemQuantity(item.id)}>+ Add To Cart</button>
) : (<div style={{ display: "flex", alignItems: "center", flexDirection: "column", gap: ".5rem" }}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: ".5rem" }}>
<button onClick={() => decreaseItemQuantity(item.id)} style={{ border: "none", borderRadius: "20px", width: "30px", cursor: "pointer" }}>-</button>
<div>
<span>{quantity}</span> in cart
</div>
<button onClick={() => increaseItemQuantity(item.id)} style={{ border: "none", borderRadius: "20px", width: "30px", cursor: "pointer" }}>+</button>
</div>
<button onClick={() => removeFromCart(item.id)} style={{ backgroundColor: "orangered", borderRadius: "40px", border: "none", color: "white", padding: "5px 10px 5px 10px", cursor: "pointer" }}>Remove</button>
</div>
)}

{/* Add to cart button with feedback */}
<button
style={{
border: "none",
borderRadius: "20px",
cursor: "pointer",
width: "auto",
alignSelf: "center",
backgroundColor: itemAdded ? "lightgreen" : "white", // Change color if added
animation: itemAdded ? "bounce 0.3s ease" : "none" // Add bounce animation if added
}}
onClick={handleAddToCart}
disabled={itemAdded} // Disable button after item is added
>
{itemAdded ? "Item Added!" : "+ Add To Cart"}
</button>
</div>
</div>
)
}
);
}
Loading
Loading