fix: cart delete mutation and console warnings
Some checks failed
Build and Deploy / build (push) Has been cancelled
Some checks failed
Build and Deploy / build (push) Has been cancelled
- Fix checkoutLinesDelete mutation: use 'id' param and 'linesIds' instead of 'lineIds' - Fix viewport metadata warning: move to separate viewport export in layout.tsx - Add sizes prop to checkout Image with fill - Fix CartDrawer init checkout useEffect to prevent re-render loops - Various product detail improvements
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
@@ -27,11 +27,15 @@ export default function CartDrawer() {
|
||||
const lines = getLines();
|
||||
const total = getTotal();
|
||||
const lineCount = getLineCount();
|
||||
const [initialized, setInitialized] = useState(false);
|
||||
|
||||
// Initialize checkout on mount
|
||||
// Initialize checkout on mount (only once)
|
||||
useEffect(() => {
|
||||
initCheckout();
|
||||
}, [initCheckout]);
|
||||
if (!initialized) {
|
||||
initCheckout();
|
||||
setInitialized(true);
|
||||
}
|
||||
}, [initialized]);
|
||||
|
||||
// Lock body scroll when cart is open
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { ChevronDown, Star, Minus, Plus } from "lucide-react";
|
||||
import type { Product } from "@/types/saleor";
|
||||
import { useSaleorCheckoutStore } from "@/stores/saleorCheckoutStore";
|
||||
import { getProductPrice, getLocalizedProduct } from "@/lib/saleor";
|
||||
import { getProductPrice, getProductPriceAmount, getLocalizedProduct, formatPrice } from "@/lib/saleor";
|
||||
import ProductCard from "@/components/product/ProductCard";
|
||||
import ProductBenefits from "@/components/product/ProductBenefits";
|
||||
import ProductReviews from "@/components/product/ProductReviews";
|
||||
@@ -91,8 +91,23 @@ export default function ProductDetail({ product, relatedProducts, locale = "SR"
|
||||
const [selectedImage, setSelectedImage] = useState(0);
|
||||
const [quantity, setQuantity] = useState(1);
|
||||
const [isAdding, setIsAdding] = useState(false);
|
||||
const [urgencyIndex, setUrgencyIndex] = useState(0);
|
||||
const { addLine, openCart } = useSaleorCheckoutStore();
|
||||
|
||||
// Cycle through urgency messages
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setUrgencyIndex(prev => (prev + 1) % 3);
|
||||
}, 3000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const urgencyMessages = [
|
||||
{ icon: "🚀", text: "Hurry up! 500+ items sold in the last 3 days!" },
|
||||
{ icon: "🛒", text: "In the carts of 2.5K people - buy before its gone!" },
|
||||
{ icon: "👀", text: "7,562 people viewed this product in the last 24 hours!" },
|
||||
];
|
||||
|
||||
const localized = getLocalizedProduct(product, locale);
|
||||
const variant = product.variants?.[0];
|
||||
|
||||
@@ -115,6 +130,8 @@ export default function ProductDetail({ product, relatedProducts, locale = "SR"
|
||||
|
||||
const isAvailable = variant?.quantityAvailable > 0;
|
||||
const price = getProductPrice(product);
|
||||
const priceAmount = getProductPriceAmount(product);
|
||||
const originalPrice = priceAmount > 0 ? formatPrice(Math.round(priceAmount * 1.30)) : null;
|
||||
|
||||
// Extract short description (first sentence or first 100 chars)
|
||||
const shortDescription = localized.description
|
||||
@@ -235,24 +252,65 @@ export default function ProductDetail({ product, relatedProducts, locale = "SR"
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
className="lg:pl-8"
|
||||
>
|
||||
{/* Urgency Sales Banner */}
|
||||
<motion.div
|
||||
key={urgencyIndex}
|
||||
initial={{ opacity: 0, y: -10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 10 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="bg-white/80 backdrop-blur-sm text-[#1a1a1a] py-3 rounded-lg mb-4 text-sm font-medium text-left"
|
||||
>
|
||||
<span className="mr-2">{urgencyMessages[urgencyIndex].icon}</span>
|
||||
{urgencyMessages[urgencyIndex].text}
|
||||
</motion.div>
|
||||
|
||||
{/* Product Name */}
|
||||
<h1 className="text-3xl md:text-4xl font-medium mb-4 tracking-tight">
|
||||
{localized.name}
|
||||
</h1>
|
||||
|
||||
{/* Short Description */}
|
||||
<p className="text-[#666666] leading-relaxed mb-6">
|
||||
<p className="text-[#666666] leading-relaxed mb-4">
|
||||
{shortDescription}
|
||||
</p>
|
||||
|
||||
{/* Price & Rating */}
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<span className="text-3xl font-medium">
|
||||
{price || (locale === "EN" ? "Contact for price" : "Kontaktirajte za cenu")}
|
||||
{/* Stock Warning - Static */}
|
||||
<div className="flex items-center justify-start gap-2 mb-6">
|
||||
<span className="relative flex h-3 w-3">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-3 w-3 bg-red-500"></span>
|
||||
</span>
|
||||
<StarRating rating={5} count={1000} />
|
||||
<span className="text-red-600 text-sm font-medium">Stocks are running out!</span>
|
||||
</div>
|
||||
|
||||
{/* Discount Price Display */}
|
||||
{originalPrice && priceAmount > 0 && (
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="text-xl text-[#666666] line-through">
|
||||
{originalPrice}
|
||||
</span>
|
||||
<span className="bg-[#b91c1c] text-white text-xs font-bold px-2 py-1 rounded">
|
||||
-30%
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-3xl font-bold text-[#b91c1c]">
|
||||
{price}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Price & Rating */}
|
||||
{!originalPrice && (
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<span className="text-3xl font-medium">
|
||||
{price || (locale === "EN" ? "Contact for price" : "Kontaktirajte za cenu")}
|
||||
</span>
|
||||
<StarRating rating={5} count={1000} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Divider */}
|
||||
<div className="border-t border-[#e5e5e5] mb-8" />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user