2026-01-07 09:31:02 +00:00
|
|
|
import React, { useState } from 'react';
|
|
|
|
|
import { Card } from '../ui/card';
|
|
|
|
|
import { Button } from '../ui/button';
|
|
|
|
|
import { CartItem } from '../commerce/CartItem';
|
|
|
|
|
import { PromoCodeModal } from '../commerce/modals/PromoCodeModal';
|
|
|
|
|
import { useCart } from '../../context/CartContext';
|
2026-01-13 18:47:57 +00:00
|
|
|
import {
|
|
|
|
|
ShoppingCart,
|
|
|
|
|
ArrowRight,
|
|
|
|
|
Tag,
|
|
|
|
|
ShieldCheck,
|
|
|
|
|
ChevronLeft,
|
|
|
|
|
} from 'lucide-react';
|
2026-01-07 09:31:02 +00:00
|
|
|
|
|
|
|
|
interface CartViewProps {
|
2026-01-13 18:47:57 +00:00
|
|
|
onCheckout: () => void;
|
|
|
|
|
onContinueShopping: () => void;
|
2026-01-07 09:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
export const CartView: React.FC<CartViewProps> = ({
|
|
|
|
|
onCheckout,
|
|
|
|
|
onContinueShopping,
|
|
|
|
|
}) => {
|
2026-01-07 09:31:02 +00:00
|
|
|
const { cart, removeFromCart, cartTotal } = useCart();
|
|
|
|
|
const [showPromo, setShowPromo] = useState(false);
|
2026-01-13 18:47:57 +00:00
|
|
|
const [discount, setDiscount] = useState<{
|
|
|
|
|
code: string;
|
|
|
|
|
amount: number;
|
|
|
|
|
} | null>(null);
|
2026-01-07 09:31:02 +00:00
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
const discountAmount = discount ? cartTotal * (discount.amount / 100) : 0;
|
2026-01-07 09:31:02 +00:00
|
|
|
const total = cartTotal - discountAmount;
|
|
|
|
|
const tax = total * 0.08; // Mock tax 8%
|
|
|
|
|
const finalTotal = total + tax;
|
|
|
|
|
|
|
|
|
|
const handleApplyPromo = (percent: number, code: string) => {
|
2026-01-13 18:47:57 +00:00
|
|
|
setDiscount({ code, amount: percent });
|
2026-01-07 09:31:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (cart.length === 0) {
|
2026-01-13 18:47:57 +00:00
|
|
|
return (
|
|
|
|
|
<div className="min-h-[60vh] flex flex-col items-center justify-center animate-fadeIn text-center">
|
|
|
|
|
<div className="w-24 h-24 bg-kodo-ink rounded-full flex items-center justify-center mb-6 border-2 border-dashed border-kodo-steel">
|
2026-01-16 00:48:26 +00:00
|
|
|
<ShoppingCart className="w-10 h-10 text-kodo-content-dim" />
|
2026-01-13 18:47:57 +00:00
|
|
|
</div>
|
|
|
|
|
<h2 className="text-2xl font-bold text-white mb-2">
|
|
|
|
|
Your cart is empty
|
|
|
|
|
</h2>
|
2026-01-16 00:48:26 +00:00
|
|
|
<p className="text-kodo-content-dim mb-8 max-w-sm">
|
2026-01-13 18:47:57 +00:00
|
|
|
Looks like you haven't added any sounds yet. Explore the marketplace
|
|
|
|
|
to find your next inspiration.
|
|
|
|
|
</p>
|
|
|
|
|
<Button
|
|
|
|
|
variant="primary"
|
|
|
|
|
onClick={onContinueShopping}
|
|
|
|
|
icon={<ArrowRight className="w-4 h-4" />}
|
|
|
|
|
>
|
|
|
|
|
Browse Marketplace
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2026-01-07 09:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="animate-fadeIn max-w-6xl mx-auto pb-20">
|
2026-01-13 18:47:57 +00:00
|
|
|
<div className="mb-8">
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
onClick={onContinueShopping}
|
2026-01-16 00:48:26 +00:00
|
|
|
className="mb-4 text-kodo-content-dim hover:text-white pl-0"
|
2026-01-13 18:47:57 +00:00
|
|
|
>
|
|
|
|
|
<ChevronLeft className="w-4 h-4 mr-2" /> Continue Shopping
|
|
|
|
|
</Button>
|
2026-01-15 22:54:05 +00:00
|
|
|
<h2 className="text-2xl font-display font-bold text-white">
|
2026-01-13 18:47:57 +00:00
|
|
|
Your Cart ({cart.length})
|
|
|
|
|
</h2>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
|
|
|
{/* Cart Items List */}
|
|
|
|
|
<div className="lg:col-span-2 space-y-4">
|
|
|
|
|
{cart.map((item) => (
|
|
|
|
|
<CartItem key={item.cartId} item={item} onRemove={removeFromCart} />
|
|
|
|
|
))}
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
{/* Summary Sidebar */}
|
aesthetic-improvements: apply design direction to pages batch 2 (Action 11.5.1.5)
- Pages: Updated padding (p-6 → p-8) and spacing (space-y-6 → space-y-8, gap-6 → gap-8) for 8px grid alignment
- LivePage, SocialPage, EducationPage, QueuePage, DeveloperPage, SettingsPage
- Views: Updated spacing and removed decorative effects
- ProfileView: Removed decorative hover scale (group-hover:scale-105), replaced decorative gradient with solid color (bg-gradient-to-r → bg-kodo-ink), updated spacing
- CheckoutView, FileDetailsView, AnalyticsView, GearView, DiscoverView, StudioView, SearchPageView, EducationView, CartView: Updated spacing (space-y-6 → space-y-8, gap-6 → gap-8, p-6 → p-8)
- Action 11.5.1.5: Apply direction to all pages - Batch 2 complete (6 pages + 10 views = 16 files)
2026-01-16 10:58:55 +00:00
|
|
|
<div className="space-y-8">
|
|
|
|
|
<Card variant="gaming" className="p-8">
|
2026-01-16 00:48:26 +00:00
|
|
|
<h3 className="font-bold text-white mb-6 uppercase tracking-wider text-sm border-b border-kodo-steel pb-2">
|
2026-01-13 18:47:57 +00:00
|
|
|
Order Summary
|
|
|
|
|
</h3>
|
2026-01-07 09:31:02 +00:00
|
|
|
|
aesthetic-improvements: align spacing to 8px grid (Action 11.2.1.3)
- Created automated script (scripts/align-8px-grid.py) to align all spacing to 8px grid
- Replaced non-8px-aligned spacing: gap-3/p-3/m-3 (12px) → gap-4/p-4/m-4 (16px), gap-5/p-5/m-5 (20px) → gap-6/p-6/m-6 (24px), gap-10/p-10/m-10 (40px) → gap-12/p-12/m-12 (48px), gap-20/p-20/m-20 (80px) → gap-24/p-24/m-24 (96px)
- Preserved: 4px values (gap-1, p-1, m-1) as they may be intentional fine-tuning, responsive breakpoints (sm:, md:, lg:), test files, documentation
- Modified files across all components to ensure consistent 8px grid alignment
- Action 11.2.1.3: Align all elements to 8px grid - COMPLETE
2026-01-16 10:50:46 +00:00
|
|
|
<div className="space-y-4 text-sm mb-6">
|
2026-01-16 00:48:26 +00:00
|
|
|
<div className="flex justify-between text-kodo-content-dim">
|
2026-01-13 18:47:57 +00:00
|
|
|
<span>Subtotal</span>
|
|
|
|
|
<span className="text-white font-mono">
|
|
|
|
|
${cartTotal.toFixed(2)}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{discount && (
|
|
|
|
|
<div className="flex justify-between text-kodo-lime">
|
|
|
|
|
<span className="flex items-center gap-2">
|
|
|
|
|
<Tag className="w-3 h-3" /> Discount ({discount.code})
|
|
|
|
|
</span>
|
|
|
|
|
<span className="font-mono">
|
|
|
|
|
-${discountAmount.toFixed(2)}
|
|
|
|
|
</span>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-13 18:47:57 +00:00
|
|
|
)}
|
|
|
|
|
|
2026-01-16 00:48:26 +00:00
|
|
|
<div className="flex justify-between text-kodo-content-dim">
|
2026-01-13 18:47:57 +00:00
|
|
|
<span>Estimated Tax (8%)</span>
|
|
|
|
|
<span className="text-white font-mono">${tax.toFixed(2)}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-01-16 00:48:26 +00:00
|
|
|
<div className="border-t border-kodo-steel pt-4 mb-6">
|
2026-01-13 18:47:57 +00:00
|
|
|
<div className="flex justify-between items-end">
|
|
|
|
|
<span className="font-bold text-white">Total</span>
|
aesthetic-improvements: reduce decorative cyan in views and pages (80/20 rule, batch 8)
- Loading spinners: PurchasesView, NotificationsView, MarketplaceView, EducationView, SearchPageView (text-kodo-cyan → text-kodo-steel, 5 instances)
- Decorative text/emphasis: CartView total price, CheckoutView total price, FullPlayer artist name, DashboardPage username highlight (text-kodo-cyan → text-kodo-steel, 4 instances)
- Decorative icons: CheckoutView credit card icon, CreateGroupModal header icon, DeveloperPage icon, SocialPage icon, DashboardPage activity icons (text-kodo-cyan → text-kodo-steel, 5 instances)
- Informational badges: PurchasesView product type badge (text-kodo-cyan → text-kodo-steel, 1 instance)
- Total: ~10 files, ~15 instances replaced
- Preserved: Active/selected states (DashboardPage selected button 30J, CheckoutView checkbox accents - focus/interaction, SearchPageView radio button accent - focus/interaction), primary actions, design system variants, functional loading indicators (PlayerLoading spinner - already preserved)
- Action 11.3.1.3 in progress (eighth batch: views and pages decorative elements)
2026-01-16 10:19:41 +00:00
|
|
|
<span className="text-2xl font-mono font-bold text-kodo-steel">
|
2026-01-13 18:47:57 +00:00
|
|
|
${finalTotal.toFixed(2)}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
variant="primary"
|
|
|
|
|
className="w-full h-12 text-base shadow-neon-cyan/20"
|
|
|
|
|
onClick={onCheckout}
|
|
|
|
|
>
|
|
|
|
|
PROCEED TO CHECKOUT
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
{!discount && (
|
|
|
|
|
<button
|
aesthetic-improvements: reduce decorative cyan in UI components (80/20 rule, batch 9)
- LiveView: decorative chat message username color (text-kodo-cyan → text-kodo-steel)
- Sidebar: decorative section header icon (text-kodo-cyan → text-kodo-steel)
- CartView: decorative promo code link (text-kodo-cyan → text-kodo-steel)
- Total: ~3 files, ~3 instances replaced
- Preserved: Functional links (LoginPage register link, RegisterPage login link, LiveView streamer profile link, LiveView wallet link), design system variants (Spinner default variant, alert.tsx info variant, badge.tsx cyan variant, ErrorDisplay.tsx info variant, Toast.tsx info variant, Alert.tsx info variant - intentional design system options), semantic status indicators (PasswordStrengthIndicator strong password - semantic color), interactive states (radio-group.tsx focus/interaction, select.tsx selected option, dropdown-menu.tsx checked state), primary actions
- Action 11.3.1.3 in progress (ninth batch: UI components decorative elements)
2026-01-16 10:21:33 +00:00
|
|
|
className="w-full text-center text-xs text-kodo-steel mt-4 hover:underline font-bold"
|
2026-01-13 18:47:57 +00:00
|
|
|
onClick={() => setShowPromo(true)}
|
|
|
|
|
>
|
|
|
|
|
Have a promo code?
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
</Card>
|
|
|
|
|
|
aesthetic-improvements: align spacing to 8px grid (Action 11.2.1.3)
- Created automated script (scripts/align-8px-grid.py) to align all spacing to 8px grid
- Replaced non-8px-aligned spacing: gap-3/p-3/m-3 (12px) → gap-4/p-4/m-4 (16px), gap-5/p-5/m-5 (20px) → gap-6/p-6/m-6 (24px), gap-10/p-10/m-10 (40px) → gap-12/p-12/m-12 (48px), gap-20/p-20/m-20 (80px) → gap-24/p-24/m-24 (96px)
- Preserved: 4px values (gap-1, p-1, m-1) as they may be intentional fine-tuning, responsive breakpoints (sm:, md:, lg:), test files, documentation
- Modified files across all components to ensure consistent 8px grid alignment
- Action 11.2.1.3: Align all elements to 8px grid - COMPLETE
2026-01-16 10:50:46 +00:00
|
|
|
<div className="bg-kodo-ink/50 p-4 rounded-xl border border-kodo-steel/50 flex items-start gap-4">
|
2026-01-13 18:47:57 +00:00
|
|
|
<ShieldCheck className="w-5 h-5 text-kodo-gold flex-shrink-0" />
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-bold text-white text-sm mb-1">
|
|
|
|
|
Secure Checkout
|
|
|
|
|
</h4>
|
2026-01-16 00:48:26 +00:00
|
|
|
<p className="text-xs text-kodo-content-dim leading-relaxed">
|
2026-01-13 18:47:57 +00:00
|
|
|
All transactions are encrypted and secure. We accept major
|
|
|
|
|
credit cards and crypto.
|
|
|
|
|
</p>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-13 18:47:57 +00:00
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-13 18:47:57 +00:00
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
{showPromo && (
|
|
|
|
|
<PromoCodeModal
|
|
|
|
|
onClose={() => setShowPromo(false)}
|
|
|
|
|
onApply={handleApplyPromo}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|