feat/fix: checkout email tracing, UI tweaks for add-to-cart, cart page overflow fix, implement hide admin bar setting
This commit is contained in:
@@ -173,55 +173,84 @@ export default function Cart() {
|
||||
{cart.items.map((item: CartItem) => (
|
||||
<div
|
||||
key={item.key}
|
||||
className="flex gap-4 p-4 border rounded-lg bg-white"
|
||||
className="flex flex-col sm:flex-row gap-4 p-4 border rounded-lg bg-white relative"
|
||||
>
|
||||
{/* Product Image */}
|
||||
{elements.product_images && (
|
||||
<div className="relative w-24 h-24 flex-shrink-0 rounded-lg overflow-hidden bg-gray-100">
|
||||
{item.image ? (
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.name}
|
||||
className="block w-full !h-full object-cover object-center"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full !h-full flex items-center justify-center text-gray-400 text-xs">
|
||||
No Image
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Product Info */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-semibold text-lg mb-1 truncate">
|
||||
{item.name}
|
||||
</h3>
|
||||
|
||||
{/* Variation Attributes */}
|
||||
{item.attributes && Object.keys(item.attributes).length > 0 && (
|
||||
<div className="text-sm text-gray-500 mb-1">
|
||||
{Object.entries(item.attributes).map(([key, value]) => {
|
||||
// Format attribute name: capitalize first letter
|
||||
const formattedKey = key.charAt(0).toUpperCase() + key.slice(1);
|
||||
return (
|
||||
<span key={key} className="mr-3">
|
||||
{formattedKey}: <span className="font-medium">{value}</span>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
<div className="flex gap-4 sm:flex-1">
|
||||
{/* Product Image */}
|
||||
{elements.product_images && (
|
||||
<div className="relative w-20 h-20 sm:w-24 sm:h-24 flex-shrink-0 rounded-lg overflow-hidden bg-gray-100">
|
||||
{item.image ? (
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.name}
|
||||
className="block w-full !h-full object-cover object-center"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full !h-full flex items-center justify-center text-gray-400 text-xs">
|
||||
No Image
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-gray-600 mb-2">
|
||||
{formatPrice(item.price)}
|
||||
</p>
|
||||
{/* Product Info */}
|
||||
<div className="flex-1 min-w-0 pr-8 sm:pr-0">
|
||||
<h3 className="font-semibold text-base sm:text-lg mb-1 truncate">
|
||||
{item.name}
|
||||
</h3>
|
||||
|
||||
{/* Quantity Controls */}
|
||||
{/* Variation Attributes */}
|
||||
{item.attributes && Object.keys(item.attributes).length > 0 && (
|
||||
<div className="text-xs sm:text-sm text-gray-500 mb-1 flex flex-wrap gap-x-3 gap-y-1">
|
||||
{Object.entries(item.attributes).map(([key, value]) => {
|
||||
const formattedKey = key.charAt(0).toUpperCase() + key.slice(1);
|
||||
return (
|
||||
<span key={key}>
|
||||
{formattedKey}: <span className="font-medium">{value}</span>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-gray-600 mb-2 font-medium">
|
||||
{formatPrice(item.price)}
|
||||
</p>
|
||||
|
||||
{/* Quantity Controls - Desktop */}
|
||||
<div className="hidden sm:flex items-center gap-2 mt-2">
|
||||
<button
|
||||
onClick={() => handleUpdateQuantity(item.key, item.quantity - 1)}
|
||||
className="font-[inherit] p-1 hover:bg-gray-100 rounded"
|
||||
>
|
||||
<Minus className="h-4 w-4" />
|
||||
</button>
|
||||
<input
|
||||
type="number"
|
||||
value={item.quantity}
|
||||
onChange={(e) =>
|
||||
handleUpdateQuantity(item.key, parseInt(e.target.value) || 1)
|
||||
}
|
||||
className="w-16 text-center border rounded py-1"
|
||||
min="1"
|
||||
/>
|
||||
<button
|
||||
onClick={() => handleUpdateQuantity(item.key, item.quantity + 1)}
|
||||
className="font-[inherit] p-1 hover:bg-gray-100 rounded"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Quantity & Item Total Container */}
|
||||
<div className="flex items-center justify-between sm:hidden pt-3 border-t border-gray-100">
|
||||
{/* Quantity Controls - Mobile */}
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => handleUpdateQuantity(item.key, item.quantity - 1)}
|
||||
className="font-[inherit] p-1 hover:bg-gray-100 rounded"
|
||||
className="font-[inherit] p-1 hover:bg-gray-100 rounded border"
|
||||
>
|
||||
<Minus className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -231,27 +260,30 @@ export default function Cart() {
|
||||
onChange={(e) =>
|
||||
handleUpdateQuantity(item.key, parseInt(e.target.value) || 1)
|
||||
}
|
||||
className="w-16 text-center border rounded py-1"
|
||||
className="w-12 text-center border rounded py-1"
|
||||
min="1"
|
||||
/>
|
||||
<button
|
||||
onClick={() => handleUpdateQuantity(item.key, item.quantity + 1)}
|
||||
className="font-[inherit] p-1 hover:bg-gray-100 rounded"
|
||||
className="font-[inherit] p-1 hover:bg-gray-100 rounded border"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
<p className="font-bold text-base">
|
||||
{formatPrice(item.price * item.quantity)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Item Total & Remove */}
|
||||
<div className="flex flex-col items-end justify-between">
|
||||
{/* Desktop Item Total & Remove + Mobile Absolute Remove */}
|
||||
<div className="absolute top-2 right-2 sm:static sm:flex sm:flex-col sm:items-end sm:justify-between">
|
||||
<button
|
||||
onClick={() => handleRemoveItem(item.key)}
|
||||
className="font-[inherit] text-red-600 hover:text-red-700 p-2"
|
||||
className="font-[inherit] text-gray-400 hover:text-red-600 p-2 sm:p-1"
|
||||
>
|
||||
<Trash2 className="h-5 w-5" />
|
||||
</button>
|
||||
<p className="font-bold text-lg">
|
||||
<p className="hidden sm:block font-bold text-lg">
|
||||
{formatPrice(item.price * item.quantity)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import { getSectionBackground } from '@/lib/sectionStyles';
|
||||
|
||||
interface HeroSectionProps {
|
||||
id: string;
|
||||
@@ -28,7 +29,8 @@ export function HeroSection({
|
||||
const isImageRight = layout === 'hero-right-image' || layout === 'image-right';
|
||||
const isCentered = layout === 'centered' || layout === 'default';
|
||||
|
||||
const hasCustomBackground = !!styles?.backgroundColor || !!styles?.backgroundImage;
|
||||
const hasCustomBackground = !!styles?.backgroundColor || !!styles?.backgroundImage || styles?.backgroundType === 'gradient';
|
||||
const sectionBg = getSectionBackground(styles);
|
||||
|
||||
// Helper to get text styles (including font family)
|
||||
const getTextStyles = (elementName: string) => {
|
||||
@@ -71,8 +73,9 @@ export function HeroSection({
|
||||
const heightClasses = heightMap[heightPreset] || 'py-12 md:py-24';
|
||||
|
||||
// Helper to get background style for dynamic schemes
|
||||
const getBackgroundStyle = () => {
|
||||
if (hasCustomBackground) return undefined;
|
||||
const getBackgroundStyle = (): React.CSSProperties | undefined => {
|
||||
// If user set custom bg via Design tab, use that
|
||||
if (hasCustomBackground) return sectionBg.style;
|
||||
if (colorScheme === 'gradient') {
|
||||
return { backgroundImage: 'linear-gradient(135deg, var(--wn-gradient-start, #9333ea), var(--wn-gradient-end, #3b82f6))' };
|
||||
}
|
||||
|
||||
@@ -909,8 +909,8 @@ export default function Product() {
|
||||
{/* Sticky CTA Bar */}
|
||||
{layout.sticky_add_to_cart && stockStatus === 'instock' && (
|
||||
<div className="fixed bottom-0 left-0 right-0 bg-white border-t-2 border-gray-200 p-3 shadow-2xl z-50">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex-1">
|
||||
<div className="max-w-6xl mx-auto flex items-center justify-between gap-3 px-2">
|
||||
<div className="flex-1 flex flex-col justify-center min-w-0">
|
||||
{/* Show selected variation for variable products */}
|
||||
{product.type === 'variable' && Object.keys(selectedAttributes).length > 0 && (
|
||||
<div className="text-xs text-gray-600 mb-1 flex items-center gap-1 flex-wrap">
|
||||
|
||||
Reference in New Issue
Block a user