grocery-app/frontend/src/components/modals/ConfirmBuyModal.jsx
2026-06-15 16:03:46 -07:00

166 lines
4.7 KiB
JavaScript

import { useEffect, useState } from "react";
import "../../styles/ConfirmBuyModal.css";
function ChevronIcon({ direction }) {
const path = direction === "previous" ? "M15 18l-6-6 6-6" : "M9 18l6-6-6-6";
return (
<svg className="confirm-buy-nav-icon" viewBox="0 0 24 24" aria-hidden="true">
<path d={path} />
</svg>
);
}
export default function ConfirmBuyModal({
item,
onConfirm,
onCancel,
allItems = [],
onNavigate
}) {
const [quantity, setQuantity] = useState(item.quantity);
const [isSubmitting, setIsSubmitting] = useState(false);
const maxQuantity = item.quantity;
useEffect(() => {
setQuantity(item.quantity);
setIsSubmitting(false);
}, [item.id, item.quantity]);
const currentIndex = allItems.findIndex((listItem) => listItem.id === item.id);
const hasPrev = currentIndex > 0;
const hasNext = currentIndex < allItems.length - 1;
const handleIncrement = () => {
if (!isSubmitting && quantity < maxQuantity) {
setQuantity((prev) => prev + 1);
}
};
const handleDecrement = () => {
if (!isSubmitting && quantity > 1) {
setQuantity((prev) => prev - 1);
}
};
const handleConfirm = async () => {
if (isSubmitting) return;
setIsSubmitting(true);
try {
await onConfirm(quantity);
} finally {
setIsSubmitting(false);
}
};
const handlePrev = () => {
if (isSubmitting) return;
if (hasPrev && onNavigate) {
onNavigate(allItems[currentIndex - 1]);
}
};
const handleNext = () => {
if (isSubmitting) return;
if (hasNext && onNavigate) {
onNavigate(allItems[currentIndex + 1]);
}
};
const imageUrl = item.item_image && item.image_mime_type
? `data:${item.image_mime_type};base64,${item.item_image}`
: null;
return (
<div
className="confirm-buy-modal-overlay"
onClick={() => {
if (!isSubmitting) {
onCancel();
}
}}
>
<div className="confirm-buy-modal" onClick={(event) => event.stopPropagation()}>
<div className="confirm-buy-header">
{item.zone && <div className="confirm-buy-zone">{item.zone}</div>}
<h2 className="confirm-buy-item-name">{item.item_name}</h2>
</div>
<div className="confirm-buy-image-section">
<button
type="button"
className="confirm-buy-nav-btn confirm-buy-nav-prev"
onClick={handlePrev}
style={{ visibility: hasPrev ? "visible" : "hidden" }}
disabled={!hasPrev || isSubmitting}
aria-label="Previous item"
title="Previous item"
>
<ChevronIcon direction="previous" />
</button>
<div className="confirm-buy-image-container">
{imageUrl ? (
<img src={imageUrl} alt={item.item_name} className="confirm-buy-image" />
) : (
<div className="confirm-buy-image-placeholder" role="img" aria-label="No item photo">
<span aria-hidden="true">{"\uD83D\uDCE6"}</span>
</div>
)}
</div>
<button
type="button"
className="confirm-buy-nav-btn confirm-buy-nav-next"
onClick={handleNext}
style={{ visibility: hasNext ? "visible" : "hidden" }}
disabled={!hasNext || isSubmitting}
aria-label="Next item"
title="Next item"
>
<ChevronIcon direction="next" />
</button>
</div>
<div className="confirm-buy-quantity-section">
<div className="confirm-buy-counter">
<button
type="button"
onClick={handleDecrement}
className="confirm-buy-counter-btn"
disabled={quantity <= 1 || isSubmitting}
>
-
</button>
<input
type="number"
value={quantity}
readOnly
className="confirm-buy-counter-display"
/>
<button
type="button"
onClick={handleIncrement}
className="confirm-buy-counter-btn"
disabled={quantity >= maxQuantity || isSubmitting}
>
+
</button>
</div>
</div>
<div className="confirm-buy-actions">
<button type="button" onClick={onCancel} className="confirm-buy-cancel" disabled={isSubmitting}>
Cancel
</button>
<button type="button" onClick={handleConfirm} className="confirm-buy-confirm" disabled={isSubmitting}>
{isSubmitting ? "Saving..." : "Mark as Bought"}
</button>
</div>
</div>
</div>
);
}