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:
Dwindi Ramadhana
2026-02-27 23:15:10 +07:00
parent 687a2318b0
commit a62037d993
22 changed files with 2711 additions and 294 deletions

View File

@@ -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>

View File

@@ -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))' };
}

View File

@@ -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">