store/resources/js/pages/Home.vue

1426 lines
77 KiB
Vue

<script setup lang="ts">
import { Head } from '@inertiajs/vue3';
import {
Search,
ShoppingBag,
Sun,
Moon,
X,
Plus,
Minus,
ArrowRight,
Star,
Check,
Info,
ChevronRight,
Eye,
Phone,
MapPin,
Mail,
Filter
} from '@lucide/vue';
import { ref, computed, onMounted, onUnmounted } from 'vue';
import RupiahInput from '@/components/form/rupiah-input/RupiahInput.vue';
import Select from '@/components/ui/select/Select.vue';
import SelectContent from '@/components/ui/select/SelectContent.vue';
import SelectItem from '@/components/ui/select/SelectItem.vue';
import SelectTrigger from '@/components/ui/select/SelectTrigger.vue';
import SelectValue from '@/components/ui/select/SelectValue.vue';
interface Price {
id: number;
variant_id: number;
type: string;
price: number;
price_formatted: string;
type_label: string;
}
interface MediaItem {
id: number;
url: string;
thumb_url: string;
}
interface Variant {
id: number;
product_id: number;
name: string;
stock: number;
prices: Price[];
images: MediaItem[];
}
interface Category {
id: number;
name: string;
slug: string;
}
interface Product {
id: number;
name: string;
slug: string;
description: string | null;
is_active: boolean;
categories: Category[];
variants: Variant[];
}
const props = defineProps<{
categories: Category[];
products: Product[];
appName: string;
aboutApp: string;
contactEmail: string;
contactPhone: string;
contactAddress: string;
logoUrl: string | null;
instagramUrl: string | null;
facebookUrl: string | null;
tiktokUrl: string | null;
}>();
// Dark Mode State
const isDark = ref(false);
const toggleDarkMode = () => {
isDark.value = !isDark.value;
if (isDark.value) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
};
// Search & Catalog Filter State
const searchInput = ref('');
const selectedCategory = ref<string | null>(null);
const minPrice = ref<number | null>(null);
const maxPrice = ref<number | null>(null);
const sortBy = ref<string>('default'); // 'default', 'price_asc', 'price_desc', 'name_asc'
const showFilters = ref(false);
const toggleCategory = (slug: string) => {
selectedCategory.value = selectedCategory.value === slug ? null : slug;
};
// Helper to get ecer price of a product for filtering/sorting
const getPrimaryEcerPrice = (product: Product): number => {
const ecerPrices = product.variants.flatMap(v =>
v.prices.filter(p => p.type === 'ecer').map(p => p.price)
);
if (ecerPrices.length > 0) {
return Math.min(...ecerPrices);
}
const allPrices = product.variants.flatMap(v => v.prices.map(p => p.price));
return allPrices.length > 0 ? Math.min(...allPrices) : 0;
};
// Filtered and Sorted Products
const filteredProducts = computed(() => {
const result = props.products.filter(product => {
// Search filter
const matchesSearch = product.name.toLowerCase().includes(searchInput.value.toLowerCase()) ||
(product.description && product.description.toLowerCase().includes(searchInput.value.toLowerCase()));
// Category filter
const matchesCategory = !selectedCategory.value ||
product.categories.some(cat => cat.slug === selectedCategory.value);
// Price filter
const price = getPrimaryEcerPrice(product);
const matchesMinPrice = minPrice.value === null || price >= minPrice.value;
const matchesMaxPrice = maxPrice.value === null || price <= maxPrice.value;
return matchesSearch && matchesCategory && matchesMinPrice && matchesMaxPrice;
});
// Sorting
if (sortBy.value === 'price_asc') {
result.sort((a, b) => getPrimaryEcerPrice(a) - getPrimaryEcerPrice(b));
} else if (sortBy.value === 'price_desc') {
result.sort((a, b) => getPrimaryEcerPrice(b) - getPrimaryEcerPrice(a));
} else if (sortBy.value === 'name_asc') {
result.sort((a, b) => a.name.localeCompare(b.name));
}
return result;
});
// Image Resolver
const getProductImage = (product: Product, indexOffset = 0) => {
for (const variant of product.variants) {
if (variant.images && variant.images.length > 0) {
return variant.images[0].url;
}
}
// Fallbacks based on category slug
const firstCat = product.categories?.[0]?.slug || '';
const fallbacks: Record<string, string[]> = {
'daster': [
'https://images.unsplash.com/photo-1595777457583-95e059d581b8?w=800&auto=format&fit=crop&q=80',
'https://images.unsplash.com/photo-1572804013309-59a88b7e92f1?w=800&auto=format&fit=crop&q=80'
],
'setelan-celana': [
'https://images.unsplash.com/photo-1539109136881-3be0616acf4b?w=800&auto=format&fit=crop&q=80',
'https://images.unsplash.com/photo-1618244972963-dbee1a7edc95?w=800&auto=format&fit=crop&q=80'
],
'atasan': [
'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?w=800&auto=format&fit=crop&q=80',
'https://images.unsplash.com/photo-1532244769068-b223ede82d7b?w=800&auto=format&fit=crop&q=80'
],
'bawahan': [
'https://images.unsplash.com/photo-1583496661160-fb4886b36ca7?w=800&auto=format&fit=crop&q=80',
'https://images.unsplash.com/photo-1551163943-3f6a855d1153?w=800&auto=format&fit=crop&q=80'
],
'busui': [
'https://images.unsplash.com/photo-1509631179647-0177331693ae?w=800&auto=format&fit=crop&q=80',
'https://images.unsplash.com/photo-1496747611176-843222e1e57c?w=800&auto=format&fit=crop&q=80'
]
};
const categoryUrls = fallbacks[firstCat] || [
'https://images.unsplash.com/photo-1490481651871-ab68de25d43d?w=800&auto=format&fit=crop&q=80',
'https://images.unsplash.com/photo-1434389677669-e08b4cac3105?w=800&auto=format&fit=crop&q=80'
];
return categoryUrls[indexOffset % categoryUrls.length];
};
// Price Formatting (Indonesian Rupiah)
const formatPrice = (price: number) => {
return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(price);
};
// Price Range Resolver
const getProductPriceRange = (product: Product) => {
const prices = product.variants.flatMap(v =>
v.prices.filter(p => p.type === 'ecer').map(p => p.price)
);
if (prices.length === 0) {
const fallbackPrices = product.variants.flatMap(v => v.prices.map(p => p.price));
if (fallbackPrices.length === 0) {
return 'Rp 0';
}
const min = Math.min(...fallbackPrices);
const max = Math.max(...fallbackPrices);
return min === max ? formatPrice(min) : `${formatPrice(min)} - ${formatPrice(max)}`;
}
const min = Math.min(...prices);
const max = Math.max(...prices);
return min === max ? formatPrice(min) : `${formatPrice(min)} - ${formatPrice(max)}`;
};
const getTotalStock = (product: Product) => {
return product.variants.reduce((acc, variant) => acc + variant.stock, 0);
};
const featuredProducts = computed(() => {
return props.products.slice(0, 3);
});
// Countdown Timer
const days = ref(0);
const hours = ref(0);
const minutes = ref(0);
const seconds = ref(0);
const startCountdown = () => {
const now = new Date();
const target = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59);
const update = () => {
const current = new Date().getTime();
const diff = target.getTime() - current;
if (diff <= 0) {
days.value = 0;
hours.value = 0;
minutes.value = 0;
seconds.value = 0;
return;
}
days.value = Math.floor(diff / (1000 * 60 * 60 * 24));
hours.value = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
minutes.value = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
seconds.value = Math.floor((diff % (1000 * 60)) / 1000);
};
update();
const interval = setInterval(update, 1000);
return interval;
};
let countdownInterval: any;
// Cart System
interface CartItem {
product: Product;
variant: Variant;
price: Price;
quantity: number;
}
const cart = ref<CartItem[]>([]);
const isCartOpen = ref(false);
const addToCart = (product: Product, variant: Variant, price: Price) => {
const existingIndex = cart.value.findIndex(item => item.variant.id === variant.id && item.price.type === price.type);
if (existingIndex > -1) {
if (cart.value[existingIndex].quantity < variant.stock) {
cart.value[existingIndex].quantity += 1;
}
} else {
cart.value.push({
product,
variant,
price,
quantity: 1
});
}
isCartOpen.value = true;
};
const removeFromCart = (variantId: number, priceType: string) => {
cart.value = cart.value.filter(item => !(item.variant.id === variantId && item.price.type === priceType));
};
const updateCartQuantity = (variantId: number, priceType: string, change: number) => {
const item = cart.value.find(item => item.variant.id === variantId && item.price.type === priceType);
if (item) {
const newQty = item.quantity + change;
if (newQty > 0 && newQty <= item.variant.stock) {
item.quantity = newQty;
}
}
};
const cartTotal = computed(() => {
return cart.value.reduce((total, item) => total + (item.price.price * item.quantity), 0);
});
const cartCount = computed(() => {
return cart.value.reduce((count, item) => count + item.quantity, 0);
});
// Centered Modal State
const activeProduct = ref<Product | null>(null);
const activeVariant = ref<Variant | null>(null);
const activePriceType = ref<string>('ecer');
const openQuickView = (product: Product) => {
activeProduct.value = product;
activeVariant.value = product.variants[0] || null;
activePriceType.value = 'ecer';
};
const closeQuickView = () => {
activeProduct.value = null;
activeVariant.value = null;
};
const selectedPrice = computed(() => {
if (!activeVariant.value) {
return null;
}
return activeVariant.value.prices.find(p => p.type === activePriceType.value) || activeVariant.value.prices[0] || null;
});
// Checkout Mock / WhatsApp redirect
const isCheckingOut = ref(false);
const handleWhatsAppCheckout = () => {
if (cart.value.length === 0) {
return;
}
isCheckingOut.value = true;
// Construct WhatsApp message
let message = `Halo ${props.appName}, saya ingin memesan produk berikut:\n\n`;
cart.value.forEach((item, index) => {
message += `${index + 1}. *${item.product.name}*\n`;
message += ` - Varian/Ukuran: ${item.variant.name}\n`;
message += ` - Jenis Harga: ${item.price.type_label}\n`;
message += ` - Jumlah: ${item.quantity} pcs\n`;
message += ` - Subtotal: ${formatPrice(item.price.price * item.quantity)}\n\n`;
});
message += `*Total Pembayaran: ${formatPrice(cartTotal.value)}*\n\n`;
message += `Mohon info untuk detail rekening transfer dan pengirimannya. Terima kasih!`;
const formattedPhone = props.contactPhone.replace(/[^0-9]/g, '');
const finalPhone = formattedPhone.startsWith('0') ? '62' + formattedPhone.slice(1) : formattedPhone;
const waUrl = `https://wa.me/${finalPhone || '6281234567890'}?text=${encodeURIComponent(message)}`;
setTimeout(() => {
isCheckingOut.value = false;
window.open(waUrl, '_blank');
}, 800);
};
const resetPriceFilters = () => {
minPrice.value = null;
maxPrice.value = null;
sortBy.value = 'default';
};
onMounted(() => {
isDark.value = document.documentElement.classList.contains('dark');
countdownInterval = startCountdown();
});
onUnmounted(() => {
if (countdownInterval) {
clearInterval(countdownInterval);
}
});
</script>
<template>
<Head :title="`${appName} | Galeri Pakaian Modern & Sejuk`">
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
</Head>
<div class="min-h-screen bg-gradient-to-tr from-cyan-50/20 via-[#FDFDFC] to-sky-50/30 text-slate-800 transition-colors duration-300 dark:from-slate-950 dark:via-[#0E1726] dark:to-cyan-950/20 dark:text-slate-100 font-sans scroll-smooth">
<!-- Sticky Header & Navbar -->
<header class="sticky top-0 z-40 bg-white/70 backdrop-blur-lg dark:bg-[#0E1726]/70 border-b border-cyan-100/50 dark:border-cyan-950/30 transition-all">
<div class="max-w-7xl mx-auto px-6 h-20 flex items-center justify-between">
<!-- Logo Frame from settings -->
<div class="flex items-center space-x-3">
<a href="#" class="flex items-center space-x-2">
<img
v-if="logoUrl"
:src="logoUrl"
:alt="appName"
class="h-10 w-auto object-contain bg-white/80 p-1 rounded-lg shadow-sm"
/>
<span class="font-serif text-2xl font-bold tracking-[0.1em] text-cyan-600 dark:text-cyan-400">
{{ appName }}
</span>
</a>
</div>
<!-- Navigation Links (Indonesian) -->
<nav class="hidden lg:flex items-center space-x-8 text-xs font-semibold uppercase tracking-widest text-slate-600 dark:text-slate-300">
<a href="#hero" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Beranda</a>
<a href="#catalog" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Produk</a>
<a href="#about" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Tentang Kami</a>
<a href="#order-guide" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Cara Pesan</a>
<a href="#contact" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Hubungi Kami</a>
</nav>
<!-- Action Utilities (Search, Dark Mode, Cart) -->
<div class="flex items-center space-x-4">
<!-- Theme Toggle -->
<button
@click="toggleDarkMode"
class="p-2 rounded-full hover:bg-cyan-50 dark:hover:bg-cyan-950/20 text-slate-600 dark:text-slate-300 transition-colors cursor-pointer"
aria-label="Ubah Tema"
>
<Sun v-if="isDark" class="w-4.5 h-4.5 text-amber-400" />
<Moon v-else class="w-4.5 h-4.5 text-cyan-600" />
</button>
<!-- Cart button -->
<button
@click="isCartOpen = true"
class="relative p-2 rounded-full hover:bg-cyan-50 dark:hover:bg-cyan-950/20 text-slate-600 dark:text-slate-300 transition-colors cursor-pointer"
aria-label="Keranjang Belanja"
>
<ShoppingBag class="w-4.5 h-4.5 text-cyan-600 dark:text-cyan-400" />
<span
v-if="cartCount > 0"
class="absolute -top-0.5 -right-0.5 bg-cyan-600 text-white dark:bg-cyan-400 dark:text-slate-950 text-[9px] font-bold w-4.5 h-4.5 flex items-center justify-center rounded-full shadow-sm animate-bounce"
>
{{ cartCount }}
</span>
</button>
</div>
</div>
</header>
<!-- Hero Section: Split Screen Visual Masterpiece (Fresh Aqua Palette) -->
<section id="hero" class="relative min-h-[calc(100vh-80px)] flex flex-col justify-between overflow-hidden">
<!-- Split Background Grid with Fresh Cyan Gradients -->
<div class="absolute inset-0 grid grid-cols-1 lg:grid-cols-2 pointer-events-none z-0">
<div class="bg-white/40 dark:bg-slate-900/30"></div>
<div class="bg-gradient-to-tr from-cyan-100/30 via-sky-100/20 to-teal-50/30 dark:from-cyan-950/10 dark:via-sky-950/20 dark:to-teal-950/10 border-l border-cyan-100/20 dark:border-cyan-950/20"></div>
</div>
<!-- Main Layout Content -->
<div class="max-w-7xl mx-auto px-6 w-full lg:grow grid grid-cols-1 lg:grid-cols-12 gap-8 items-center py-12 z-10">
<!-- Left Column: Serif Text and CTA -->
<div class="lg:col-span-5 flex flex-col justify-center space-y-6 lg:pr-6">
<div class="inline-flex items-center space-x-2 bg-cyan-500/10 dark:bg-cyan-400/10 text-[10px] text-cyan-700 dark:text-cyan-300 uppercase tracking-widest px-3 py-1 rounded-full w-fit font-bold shadow-sm">
<span>Koleksi Segar 2026</span>
</div>
<h1 class="text-5xl md:text-6xl font-serif leading-[1.1] tracking-tight font-light select-none text-slate-800 dark:text-slate-100">
Ekspresikan <br />
Gaya <span class="font-normal italic text-cyan-600 dark:text-cyan-400 bg-cyan-100/40 dark:bg-cyan-900/20 px-2 rounded-lg">Segar Anda</span>.
</h1>
<p class="text-sm text-slate-500 dark:text-slate-300 max-w-md leading-relaxed font-light">
Selamat datang di {{ appName }}. Temukan keanggunan motif batik modern dan setelan pakaian santai berkualitas premium. Dirancang khusus dengan bahan adem yang menyejukkan aktivitas harian Anda.
</p>
<div class="flex items-center space-x-4 pt-4">
<a
href="#catalog"
class="inline-flex items-center justify-center bg-cyan-600 text-white px-8 py-3.5 text-xs font-semibold uppercase tracking-widest rounded-xl shadow-lg shadow-cyan-200/50 hover:bg-cyan-500 hover:shadow-cyan-300/50 hover:-translate-y-0.5 transition-all duration-300 group dark:bg-cyan-500 dark:text-slate-950 dark:shadow-none dark:hover:bg-cyan-400 cursor-pointer"
>
Beli Sekarang
<ArrowRight class="w-3.5 h-3.5 ml-2 transform group-hover:translate-x-1 transition-transform" />
</a>
<a
href="#about"
class="inline-flex items-center justify-center border border-cyan-200 text-xs font-semibold uppercase tracking-widest px-8 py-3.5 rounded-xl hover:bg-cyan-500/5 hover:border-cyan-400 transition-all dark:border-cyan-800 dark:hover:bg-cyan-950/20 cursor-pointer"
>
Tentang Kami
</a>
</div>
</div>
<!-- Center Column: Visual Model Showcase & Overlapping Titles (Cyan Aura) -->
<div class="lg:col-span-7 relative flex justify-center items-center h-[500px] lg:h-[650px] w-full">
<!-- Massive Overlapping Serif Heading (Behind Model) -->
<div class="absolute inset-0 flex justify-center items-center pointer-events-none select-none z-0">
<div class="text-[14vw] lg:text-[160px] font-serif font-bold leading-none tracking-tight flex opacity-10 dark:opacity-[0.03]">
<span class="text-slate-900 dark:text-white">DST</span>
<span class="text-cyan-600 dark:text-cyan-400">CLO</span>
</div>
</div>
<!-- Glowing Radial Background and Animated Morphing Blobs -->
<div class="absolute inset-0 -z-10 flex justify-center items-center pointer-events-none select-none">
<div class="absolute w-[350px] h-[350px] rounded-full bg-gradient-to-tr from-cyan-400/25 via-sky-400/20 to-teal-300/25 dark:from-cyan-900/15 dark:via-sky-900/20 dark:to-teal-900/15 blur-3xl animate-blob"></div>
<div class="absolute w-[400px] h-[400px] rounded-full bg-gradient-to-br from-indigo-300/20 via-cyan-200/20 to-emerald-300/20 dark:from-indigo-950/15 dark:via-cyan-950/15 dark:to-emerald-950/15 blur-3xl animate-blob animation-delay-2000"></div>
</div>
<!-- Main Model Central Image (With Floating Animation) -->
<div class="relative w-[310px] h-[440px] lg:w-[390px] lg:h-[540px] rounded-[2rem] overflow-hidden shadow-2xl shadow-cyan-100/50 dark:shadow-none z-10 transition-transform duration-500 hover:scale-[1.02] border-4 border-white dark:border-cyan-950/80 animate-float-model">
<img
src="https://images.unsplash.com/photo-1490481651871-ab68de25d43d?w=1000&auto=format&fit=crop&q=80"
alt="DSTCLO Model Unggulan"
class="w-full h-full object-cover"
/>
<!-- Subtle Gradient Overlay -->
<div class="absolute inset-0 bg-gradient-to-t from-cyan-950/30 via-transparent to-transparent"></div>
</div>
<!-- FLOATING HERO TAG 1: Product Showcase -->
<div
v-if="featuredProducts[0]"
@click="openQuickView(featuredProducts[0])"
class="absolute top-10 left-4 md:-left-6 z-20 flex items-center space-x-3 bg-white/90 backdrop-blur-md dark:bg-slate-900/90 p-3 rounded-2xl shadow-lg border border-cyan-100/40 dark:border-cyan-950/85 cursor-pointer transform hover:-translate-y-1 transition-all animate-float-tag-1"
>
<img
:src="getProductImage(featuredProducts[0])"
alt="Preview Produk"
class="w-10 h-10 object-cover rounded-lg"
/>
<div class="text-left">
<h4 class="text-[9px] font-bold text-cyan-600 dark:text-cyan-400 uppercase tracking-widest">Terpopuler</h4>
<p class="text-[11px] font-semibold truncate max-w-[100px]">{{ featuredProducts[0].name }}</p>
<span class="text-[11px] font-bold text-cyan-600 dark:text-cyan-400">{{ getProductPriceRange(featuredProducts[0]) }}</span>
</div>
<div class="flex items-center justify-center w-6 h-6 rounded-full bg-cyan-50 dark:bg-cyan-950/50 text-cyan-600 dark:text-cyan-400">
<Plus class="w-3.5 h-3.5" />
</div>
</div>
<!-- FLOATING HERO TAG 2: Rating -->
<div
v-if="featuredProducts[1]"
@click="openQuickView(featuredProducts[1])"
class="absolute top-28 right-4 md:-right-6 z-20 flex items-center space-x-3 bg-white/90 backdrop-blur-md dark:bg-slate-900/90 p-3 rounded-2xl shadow-lg border border-cyan-100/40 dark:border-cyan-950/85 cursor-pointer transform hover:-translate-y-1 transition-all animate-float-tag-2"
>
<img
:src="getProductImage(featuredProducts[1], 1)"
alt="Preview Produk"
class="w-10 h-10 object-cover rounded-lg"
/>
<div class="text-left">
<div class="flex items-center text-amber-400 space-x-0.5 mb-0.5">
<Star class="w-2.5 h-2.5 fill-current" v-for="i in 5" :key="i" />
</div>
<p class="text-[11px] font-semibold truncate max-w-[100px]">{{ featuredProducts[1].name }}</p>
<span class="text-[10px] font-semibold text-emerald-500">Stok Tersedia</span>
</div>
</div>
</div>
</div>
<!-- Scroll Indicator -->
<div class="max-w-7xl mx-auto px-6 w-full flex items-center justify-between pb-8 z-10 text-xs font-semibold text-slate-500 dark:text-slate-400">
<div class="flex items-center space-x-3">
<span class="text-cyan-600 dark:text-cyan-400">#DSTCollection</span>
<span class="w-1 h-1 rounded-full bg-cyan-300"></span>
<span>Bahan Adem & Lembut</span>
</div>
<a href="#catalog" class="flex items-center space-x-2 hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">
<span>Lihat katalog lengkap</span>
<ChevronRight class="w-3.5 h-3.5 transform rotate-90 text-cyan-600" />
</a>
</div>
</section>
<!-- Catalog Section with Price Filters & Categories -->
<section id="catalog" class="max-w-7xl mx-auto px-6 py-20 border-t border-cyan-100/30 dark:border-cyan-950/20">
<div class="space-y-6 mb-12">
<div class="text-center space-y-2">
<h2 class="text-xs uppercase tracking-[0.3em] font-bold text-cyan-600 dark:text-cyan-400">Katalog Eksklusif</h2>
<h3 class="text-3xl font-serif text-slate-800 dark:text-white">Koleksi Busana Pilihan</h3>
<p class="text-sm text-slate-500 dark:text-slate-300 max-w-lg mx-auto font-light">
Gunakan kategori dan filter di bawah untuk menyesuaikan pencarian busana idaman Anda dengan mudah.
</p>
</div>
<!-- Interactive Filters & Search Controls bar -->
<div class="bg-white/80 dark:bg-[#111A2E]/80 backdrop-blur-md p-6 rounded-3xl shadow-sm border border-cyan-100/30 dark:border-cyan-950/30 flex flex-col md:flex-row items-center gap-4 justify-between">
<!-- Search Input -->
<div class="relative w-full md:w-80">
<input
v-model="searchInput"
type="text"
placeholder="Cari nama pakaian..."
class="w-full bg-slate-50 dark:bg-slate-900 border border-slate-200 dark:border-cyan-950 text-xs px-4 py-3 pr-10 rounded-xl outline-none focus:border-cyan-500 dark:focus:border-cyan-400 text-slate-700 dark:text-white transition-all"
/>
<Search class="absolute right-3 top-3.5 w-4 h-4 text-slate-400" />
</div>
<!-- Category Chips -->
<div class="flex flex-wrap gap-2 w-full md:w-auto justify-center">
<button
@click="selectedCategory = null"
:class="[
'px-4 py-2 text-[11px] font-semibold uppercase tracking-wider rounded-xl border transition-all cursor-pointer',
!selectedCategory
? 'bg-cyan-600 text-white border-transparent shadow-md shadow-cyan-200/50 dark:bg-cyan-500 dark:text-slate-950 dark:shadow-none'
: 'bg-transparent text-slate-600 border-slate-200 hover:border-cyan-500 dark:text-slate-300 dark:border-cyan-900/60 dark:hover:border-cyan-400'
]"
>
Semua
</button>
<button
v-for="category in categories"
:key="category.id"
@click="toggleCategory(category.slug)"
:class="[
'px-4 py-2 text-[11px] font-semibold uppercase tracking-wider rounded-xl border transition-all cursor-pointer',
selectedCategory === category.slug
? 'bg-cyan-600 text-white border-transparent shadow-md shadow-cyan-200/50 dark:bg-cyan-500 dark:text-slate-950 dark:shadow-none'
: 'bg-transparent text-slate-600 border-slate-200 hover:border-cyan-500 dark:text-slate-300 dark:border-cyan-900/60 dark:hover:border-cyan-400'
]"
>
{{ category.name }}
</button>
</div>
<!-- Filter panel toggler -->
<button
@click="showFilters = !showFilters"
class="flex items-center space-x-2 px-4 py-2.5 rounded-xl border border-slate-200 dark:border-cyan-900 text-xs font-semibold text-slate-600 dark:text-slate-300 hover:border-cyan-500 dark:hover:border-cyan-400 cursor-pointer transition-colors"
>
<Filter class="w-3.5 h-3.5" />
<span>Filter & Urutkan</span>
<span v-if="minPrice || maxPrice || sortBy !== 'default'" class="w-2 h-2 rounded-full bg-cyan-600 dark:bg-cyan-400"></span>
</button>
</div>
<!-- Expandable Filter Panel -->
<div
v-if="showFilters"
class="bg-white/50 dark:bg-slate-900/40 p-6 rounded-2xl border border-cyan-100/20 dark:border-cyan-950/20 grid grid-cols-1 md:grid-cols-3 gap-6 animate-fade-in-up"
>
<!-- Price Range -->
<div class="space-y-2">
<label class="text-[11px] uppercase tracking-wider font-bold text-slate-500 dark:text-slate-400">Rentang Harga</label>
<div class="flex items-center space-x-2">
<RupiahInput
v-model="minPrice"
placeholder="Min"
class="w-full bg-white dark:bg-slate-950 text-slate-700 dark:text-white"
/>
<span class="text-xs text-slate-400">s/d</span>
<RupiahInput
v-model="maxPrice"
placeholder="Max"
class="w-full bg-white dark:bg-slate-950 text-slate-700 dark:text-white"
/>
</div>
</div>
<!-- Sort Options -->
<div class="space-y-2">
<label class="text-[11px] uppercase tracking-wider font-bold text-slate-500 dark:text-slate-400">Urutkan Berdasarkan</label>
<Select v-model="sortBy">
<SelectTrigger class="w-full bg-white dark:bg-slate-950 border border-slate-200 dark:border-cyan-950 px-3 py-2 text-xs rounded-lg outline-none focus:border-cyan-500 text-slate-700 dark:text-white h-10">
<SelectValue placeholder="Pilih urutan" />
</SelectTrigger>
<SelectContent>
<SelectItem value="default">Default (Urutan Utama)</SelectItem>
<SelectItem value="price_asc">Harga Terendah</SelectItem>
<SelectItem value="price_desc">Harga Tertinggi</SelectItem>
<SelectItem value="name_asc">Nama Produk (A-Z)</SelectItem>
</SelectContent>
</Select>
</div>
<!-- Reset Trigger -->
<div class="flex items-end">
<button
@click="resetPriceFilters"
class="w-full bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-750 text-slate-700 dark:text-slate-300 py-2.5 rounded-lg text-xs font-semibold uppercase tracking-wider transition-colors cursor-pointer"
>
Atur Ulang Filter
</button>
</div>
</div>
</div>
<!-- Product Grid -->
<div v-if="filteredProducts.length > 0" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-x-8 gap-y-12">
<div
v-for="(product, idx) in filteredProducts"
:key="product.id"
class="group relative flex flex-col space-y-4"
>
<!-- Product Image Container -->
<div class="relative aspect-[3/4] w-full overflow-hidden rounded-2xl bg-cyan-50/50 dark:bg-neutral-900 shadow-md transition-shadow hover:shadow-cyan-100/30">
<img
:src="getProductImage(product, idx)"
:alt="product.name"
class="h-full w-full object-cover object-center transition-transform duration-700 ease-out group-hover:scale-105"
/>
<!-- Subtle Gradient Overlay -->
<div class="absolute inset-0 bg-gradient-to-t from-cyan-950/20 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<!-- Absolute Status Badges -->
<div class="absolute top-4 left-4 flex flex-col space-y-2">
<span
v-if="getTotalStock(product) === 0"
class="bg-red-500 text-white text-[9px] font-bold uppercase tracking-wider px-3 py-1 rounded-md"
>
Habis
</span>
<span
v-else-if="getTotalStock(product) < 15"
class="bg-amber-500 text-white text-[9px] font-bold uppercase tracking-wider px-3 py-1 rounded-md animate-pulse"
>
Menipis
</span>
<span
v-else
class="bg-cyan-600 text-white text-[9px] font-bold uppercase tracking-wider px-3 py-1 rounded-md dark:bg-cyan-500 dark:text-slate-950"
>
Terbaru
</span>
</div>
<!-- Hover Action Quick View Button -->
<div class="absolute bottom-4 inset-x-4 flex justify-center opacity-0 transform translate-y-2 group-hover:opacity-100 group-hover:translate-y-0 transition-all duration-300">
<button
@click="openQuickView(product)"
class="w-full flex items-center justify-center bg-white/95 backdrop-blur-sm text-slate-800 hover:bg-cyan-600 hover:text-white px-4 py-3 rounded-xl shadow-md text-xs font-bold uppercase tracking-widest transition-all duration-200 cursor-pointer"
>
<Eye class="w-4 h-4 mr-2" />
Lihat Detail
</button>
</div>
</div>
<!-- Product Details Info -->
<div class="flex flex-col space-y-1">
<!-- Category Tags -->
<span class="text-[10px] font-bold uppercase tracking-widest text-cyan-600 dark:text-cyan-400">
{{ product.categories.map(c => c.name).join(', ') }}
</span>
<!-- Title -->
<h4 class="text-base font-semibold text-slate-700 dark:text-neutral-200 line-clamp-1 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors">
{{ product.name }}
</h4>
<!-- Price -->
<p class="text-sm font-bold text-cyan-600 dark:text-cyan-400">
{{ getProductPriceRange(product) }}
</p>
<!-- Stock Indicator -->
<span class="text-[11px] text-slate-400 dark:text-neutral-500 font-light mt-0.5">
Ukuran tersedia: {{ product.variants.map(v => v.name).join(', ') }}
</span>
</div>
</div>
</div>
<!-- Empty Product State -->
<div v-else class="text-center py-20 space-y-3">
<Info class="w-12 h-12 text-cyan-300 dark:text-neutral-700 mx-auto" />
<h3 class="text-lg font-medium text-slate-700 dark:text-neutral-300">Produk Tidak Ditemukan</h3>
<p class="text-sm text-slate-400 dark:text-neutral-500">
Mohon maaf, kami tidak menemukan pakaian yang cocok dengan kata kunci pencarian Anda.
</p>
<button
@click="searchInput = ''; selectedCategory = null; resetPriceFilters()"
class="mt-2 text-xs font-bold uppercase tracking-widest text-cyan-600 dark:text-cyan-400 underline underline-offset-4 cursor-pointer"
>
Reset Pencarian
</button>
</div>
</section>
<!-- Deal of the Month Section (Fresh Aqua Palette) -->
<section id="deal" class="w-full bg-gradient-to-br from-cyan-50/40 via-sky-50/20 to-teal-50/30 dark:from-[#0E1726]/40 dark:to-cyan-950/20 py-24 border-t border-b border-cyan-100/30 dark:border-cyan-950/20">
<div class="max-w-7xl mx-auto px-6 grid grid-cols-1 lg:grid-cols-12 gap-12 items-center">
<!-- Deal Highlight Left -->
<div class="lg:col-span-5 space-y-6">
<span class="text-xs uppercase tracking-[0.3em] font-bold text-cyan-600 dark:text-cyan-400">Promo Terbatas</span>
<h3 class="text-4xl font-serif leading-tight">Penawaran Bulan Ini</h3>
<p class="text-sm text-slate-500 dark:text-slate-300 leading-relaxed font-light">
Jangan lewatkan promosi batik dan daster eksklusif kami! Dapatkan potongan harga spesial up to <strong class="text-cyan-600 dark:text-cyan-400 font-semibold">25%</strong> untuk varian daster pilihan dan setelan pakaian modern. Dapatkan kenyamanan ekstra dengan bahan menyejukkan sebelum masa promo habis!
</p>
<!-- Countdown Cards -->
<div class="grid grid-cols-4 gap-4 py-4 max-w-sm">
<div class="bg-white dark:bg-slate-900 border border-cyan-100/30 dark:border-cyan-950/30 p-4 rounded-xl text-center shadow-sm">
<span class="block text-2xl font-bold font-serif text-cyan-600 dark:text-cyan-400">{{ String(days).padStart(2, '0') }}</span>
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-bold">Hari</span>
</div>
<div class="bg-white dark:bg-slate-900 border border-cyan-100/30 dark:border-cyan-950/30 p-4 rounded-xl text-center shadow-sm">
<span class="block text-2xl font-bold font-serif text-cyan-600 dark:text-cyan-400">{{ String(hours).padStart(2, '0') }}</span>
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-bold">Jam</span>
</div>
<div class="bg-white dark:bg-slate-900 border border-cyan-100/30 dark:border-cyan-950/30 p-4 rounded-xl text-center shadow-sm">
<span class="block text-2xl font-bold font-serif text-cyan-600 dark:text-cyan-400">{{ String(minutes).padStart(2, '0') }}</span>
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-bold">Menit</span>
</div>
<div class="bg-white dark:bg-slate-900 border border-cyan-100/30 dark:border-cyan-950/30 p-4 rounded-xl text-center shadow-sm">
<span class="block text-2xl font-bold font-serif text-cyan-600 dark:text-cyan-400">{{ String(seconds).padStart(2, '0') }}</span>
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-bold">Detik</span>
</div>
</div>
<a
href="#catalog"
class="inline-flex items-center justify-center bg-cyan-600 text-white px-8 py-3.5 text-xs font-semibold uppercase tracking-widest rounded-xl shadow-lg shadow-cyan-200/50 hover:bg-cyan-500 transition-all dark:bg-cyan-500 dark:text-slate-950 cursor-pointer"
>
Jelajahi Produk Diskon
</a>
</div>
<!-- Featured Image Right -->
<div class="lg:col-span-7 grid grid-cols-2 gap-4">
<div class="space-y-4">
<div class="aspect-[4/5] rounded-3xl overflow-hidden shadow-md border-2 border-white dark:border-cyan-950">
<img
src="https://images.unsplash.com/photo-1595777457583-95e059d581b8?w=800&auto=format&fit=crop&q=80"
alt="Daster Batik parang"
class="w-full h-full object-cover"
/>
</div>
<div class="aspect-square rounded-3xl overflow-hidden shadow-md border-2 border-white dark:border-cyan-950">
<img
src="https://images.unsplash.com/photo-1572804013309-59a88b7e92f1?w=800&auto=format&fit=crop&q=80"
alt="Daster busui styling"
class="w-full h-full object-cover"
/>
</div>
</div>
<div class="pt-8 space-y-4">
<div class="aspect-square rounded-3xl overflow-hidden shadow-md border-2 border-white dark:border-cyan-950">
<img
src="https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?w=800&auto=format&fit=crop&q=80"
alt="Blouse Batik Modern"
class="w-full h-full object-cover"
/>
</div>
<div class="aspect-[4/5] rounded-3xl overflow-hidden shadow-md border-2 border-white dark:border-cyan-950">
<img
src="https://images.unsplash.com/photo-1539109136881-3be0616acf4b?w=800&auto=format&fit=crop&q=80"
alt="Setelan Celana Batik"
class="w-full h-full object-cover"
/>
</div>
</div>
</div>
</div>
</section>
<!-- Cara Pesan Section -->
<section id="order-guide" class="max-w-7xl mx-auto px-6 py-20 border-b border-cyan-100/30 dark:border-cyan-950/20">
<div class="text-center space-y-3 mb-16">
<h3 class="text-xs uppercase tracking-[0.3em] font-bold text-cyan-600 dark:text-cyan-400">Langkah Pemesanan</h3>
<h4 class="text-3xl font-serif">Cara Melakukan Pemesanan</h4>
<p class="text-sm text-slate-500 dark:text-slate-300 max-w-md mx-auto font-light">
Sistem pemesanan kami sangat mudah dan terhubung langsung via WhatsApp untuk pelayanan cepat dan personal.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
<!-- Step 1 -->
<div class="bg-white dark:bg-slate-900/50 p-6 rounded-2xl border border-cyan-100/30 dark:border-cyan-950/30 shadow-sm relative overflow-hidden text-center group hover:-translate-y-1 transition-transform">
<div class="absolute -top-4 -right-4 w-16 h-16 bg-cyan-100/50 dark:bg-cyan-950/50 rounded-full flex items-center justify-center text-cyan-600 dark:text-cyan-400 font-bold text-xl font-serif">1</div>
<div class="w-12 h-12 rounded-xl bg-cyan-100 dark:bg-cyan-900/30 text-cyan-600 dark:text-cyan-400 flex items-center justify-center mx-auto mb-4">
<ShoppingBag class="w-5 h-5" />
</div>
<h5 class="font-bold text-base mb-2">Pilih Produk</h5>
<p class="text-xs text-slate-400 leading-relaxed font-light">
Jelajahi pakaian batik dan daster favorit Anda, lalu ketuk "Lihat Detail" untuk memeriksa ukuran.
</p>
</div>
<!-- Step 2 -->
<div class="bg-white dark:bg-slate-900/50 p-6 rounded-2xl border border-cyan-100/30 dark:border-cyan-950/30 shadow-sm relative overflow-hidden text-center group hover:-translate-y-1 transition-transform">
<div class="absolute -top-4 -right-4 w-16 h-16 bg-cyan-100/50 dark:bg-cyan-950/50 rounded-full flex items-center justify-center text-cyan-600 dark:text-cyan-400 font-bold text-xl font-serif">2</div>
<div class="w-12 h-12 rounded-xl bg-cyan-100 dark:bg-cyan-900/30 text-cyan-600 dark:text-cyan-400 flex items-center justify-center mx-auto mb-4">
<Star class="w-5 h-5" />
</div>
<h5 class="font-bold text-base mb-2">Pilih Varian & Harga</h5>
<p class="text-xs text-slate-400 leading-relaxed font-light">
Tentukan varian ukuran yang diinginkan dan pilih jenis harga (Eceran, Grosir, Agen, dll.).
</p>
</div>
<!-- Step 3 -->
<div class="bg-white dark:bg-slate-900/50 p-6 rounded-2xl border border-cyan-100/30 dark:border-cyan-950/30 shadow-sm relative overflow-hidden text-center group hover:-translate-y-1 transition-transform">
<div class="absolute -top-4 -right-4 w-16 h-16 bg-cyan-100/50 dark:bg-cyan-950/50 rounded-full flex items-center justify-center text-cyan-600 dark:text-cyan-400 font-bold text-xl font-serif">3</div>
<div class="w-12 h-12 rounded-xl bg-cyan-100 dark:bg-cyan-900/30 text-cyan-600 dark:text-cyan-400 flex items-center justify-center mx-auto mb-4">
<Plus class="w-5 h-5" />
</div>
<h5 class="font-bold text-base mb-2">Masukkan Keranjang</h5>
<p class="text-xs text-slate-400 leading-relaxed font-light">
Masukkan ke Keranjang Belanja untuk menampung seluruh daftar pakaian yang ingin Anda beli.
</p>
</div>
<!-- Step 4 -->
<div class="bg-white dark:bg-slate-900/50 p-6 rounded-2xl border border-cyan-100/30 dark:border-cyan-950/30 shadow-sm relative overflow-hidden text-center group hover:-translate-y-1 transition-transform">
<div class="absolute -top-4 -right-4 w-16 h-16 bg-cyan-100/50 dark:bg-cyan-950/50 rounded-full flex items-center justify-center text-cyan-600 dark:text-cyan-400 font-bold text-xl font-serif">4</div>
<div class="w-12 h-12 rounded-xl bg-cyan-100 dark:bg-cyan-900/30 text-cyan-600 dark:text-cyan-400 flex items-center justify-center mx-auto mb-4">
<Phone class="w-5 h-5" />
</div>
<h5 class="font-bold text-base mb-2">Kirim ke WhatsApp</h5>
<p class="text-xs text-slate-400 leading-relaxed font-light">
Klik tombol kirim pesanan, admin kami akan merespons rincian transfer bank dan pengiriman kurir.
</p>
</div>
</div>
</section>
<!-- About Us Section -->
<section id="about" class="max-w-7xl mx-auto px-6 py-20 border-b border-cyan-100/30 dark:border-cyan-950/20">
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12 items-center">
<!-- Column Left -->
<div class="lg:col-span-6 relative aspect-square max-w-md mx-auto lg:max-w-none rounded-3xl overflow-hidden shadow-lg border-2 border-white dark:border-cyan-950">
<img
src="https://images.unsplash.com/photo-1595777457583-95e059d581b8?w=800&auto=format&fit=crop&q=80"
alt="Proses Pembuatan Batik"
class="w-full h-full object-cover"
/>
</div>
<!-- Column Right -->
<div class="lg:col-span-6 space-y-6">
<span class="text-xs uppercase tracking-[0.3em] font-bold text-cyan-600 dark:text-cyan-400">Tentang Kami</span>
<h3 class="text-3xl font-serif">DST Collection (DSTCLO)</h3>
<p class="text-sm text-slate-500 dark:text-slate-300 leading-relaxed font-light whitespace-pre-line">
{{ aboutApp || 'Kami adalah rumah produksi busana dan batik lokal berkualitas tinggi. Berfokus pada keindahan motif, ketepatan detail jahitan, dan pemilihan kain adem yang mengedepankan aspek fungsionalitas dan estetika.' }}
</p>
<div class="flex items-center space-x-3 text-xs text-slate-500 font-semibold">
<Check class="w-4 h-4 text-emerald-500" />
<span>Kain Rayon Super Tebal & Menyerap Keringat</span>
</div>
<div class="flex items-center space-x-3 text-xs text-slate-500 font-semibold">
<Check class="w-4 h-4 text-emerald-500" />
<span>Motif Eksklusif & Tidak Pasaran</span>
</div>
<div class="flex items-center space-x-3 text-xs text-slate-500 font-semibold">
<Check class="w-4 h-4 text-emerald-500" />
<span>Dukungan Penuh Layanan Admin Via WhatsApp</span>
</div>
</div>
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="max-w-7xl mx-auto px-6 py-20">
<div class="bg-white/60 dark:bg-slate-900/60 backdrop-blur-md border border-cyan-100/30 dark:border-cyan-950/30 p-8 rounded-3xl grid grid-cols-1 lg:grid-cols-2 gap-12 items-center shadow-sm">
<!-- Left -->
<div class="space-y-6">
<span class="text-xs uppercase tracking-[0.3em] font-bold text-cyan-600 dark:text-cyan-400">Kontak Kami</span>
<h3 class="text-3xl font-serif">Ada Pertanyaan? Hubungi Kami</h3>
<p class="text-sm text-slate-500 dark:text-slate-300 leading-relaxed font-light">
Kami sangat senang mendengarkan pertanyaan Anda terkait spesifikasi produk, ketersediaan grosir, atau kemitraan. Hubungi tim admin kami melalui media di bawah.
</p>
<div class="space-y-4">
<div v-if="contactPhone" class="flex items-center space-x-3 text-sm">
<div class="w-8 h-8 rounded-lg bg-cyan-100 dark:bg-cyan-950/40 text-cyan-600 dark:text-cyan-400 flex items-center justify-center">
<Phone class="w-4 h-4" />
</div>
<span>{{ contactPhone }}</span>
</div>
<div v-if="contactEmail" class="flex items-center space-x-3 text-sm">
<div class="w-8 h-8 rounded-lg bg-cyan-100 dark:bg-cyan-950/40 text-cyan-600 dark:text-cyan-400 flex items-center justify-center">
<Mail class="w-4 h-4" />
</div>
<span>{{ contactEmail }}</span>
</div>
<div v-if="contactAddress" class="flex items-center space-x-3 text-sm">
<div class="w-8 h-8 rounded-lg bg-cyan-100 dark:bg-cyan-950/40 text-cyan-600 dark:text-cyan-400 flex items-center justify-center">
<MapPin class="w-4 h-4" />
</div>
<span>{{ contactAddress }}</span>
</div>
</div>
</div>
<!-- Right Form/Quick Message Mock -->
<div class="bg-slate-50 dark:bg-slate-950 p-6 rounded-2xl border border-slate-200 dark:border-cyan-950 space-y-4">
<h4 class="font-bold text-base">Kirim Pesan Cepat</h4>
<div class="space-y-3">
<input type="text" placeholder="Nama Anda" class="w-full text-xs px-4 py-3 bg-white dark:bg-slate-900 border border-slate-200 dark:border-cyan-950 rounded-lg outline-none focus:border-cyan-500 text-slate-700 dark:text-white" />
<textarea placeholder="Tulis pesan Anda di sini..." rows="4" class="w-full text-xs px-4 py-3 bg-white dark:bg-slate-900 border border-slate-200 dark:border-cyan-950 rounded-lg outline-none focus:border-cyan-500 text-slate-700 dark:text-white scrollbar-thin"></textarea>
<!-- WhatsApp Direct Send Button -->
<a
:href="`https://wa.me/${contactPhone.replace(/[^0-9]/g, '') || '6281234567890'}`"
target="_blank"
class="w-full inline-flex items-center justify-center bg-emerald-600 hover:bg-emerald-500 text-white py-3 rounded-lg text-xs font-bold uppercase tracking-wider transition-colors"
>
<Phone class="w-4 h-4 mr-2" />
Kirim Melalui WhatsApp
</a>
</div>
</div>
</div>
</section>
<!-- Brand Footer -->
<footer class="bg-white text-slate-600 dark:bg-[#070D19] dark:text-slate-400 py-16 border-t border-cyan-100/30 dark:border-cyan-950/20">
<div class="max-w-7xl mx-auto px-6 grid grid-cols-1 md:grid-cols-4 gap-8">
<!-- Brand Profile -->
<div class="space-y-4">
<div class="flex items-center space-x-2">
<img
v-if="logoUrl"
:src="logoUrl"
:alt="appName"
class="h-8 w-auto object-contain bg-white p-0.5 rounded shadow-sm"
/>
<span class="font-serif text-xl font-bold tracking-widest text-black dark:text-white text-cyan-600 dark:text-cyan-400">
{{ appName }}
</span>
</div>
<p class="text-xs text-slate-400 leading-relaxed font-light">
Galeri resmi {{ appName }}. Pilihan busana lokal premium berpotongan modern dengan kenyamanan menyejukkan.
</p>
</div>
<!-- Shop Links -->
<div class="space-y-3">
<h4 class="text-xs uppercase font-bold text-slate-800 dark:text-white tracking-widest">Koleksi Kami</h4>
<ul class="space-y-2 text-xs">
<li><a href="#catalog" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Batik Daster</a></li>
<li><a href="#catalog" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Setelan Celana</a></li>
<li><a href="#catalog" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Daster Menyusui (Busui)</a></li>
<li><a href="#catalog" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Atasan & Bawahan</a></li>
</ul>
</div>
<!-- Info links -->
<div class="space-y-3">
<h4 class="text-xs uppercase font-bold text-slate-800 dark:text-white tracking-widest">Tautan</h4>
<ul class="space-y-2 text-xs">
<li><a href="#about" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Tentang Kami</a></li>
<li><a href="#order-guide" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Cara Pemesanan</a></li>
<li><a href="#contact" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors">Hubungi Kami</a></li>
<li><a href="/admin/dashboard" class="hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors font-semibold">Login Dashboard Admin</a></li>
</ul>
</div>
<!-- Socials -->
<div class="space-y-3">
<h4 class="text-xs uppercase font-bold text-slate-800 dark:text-white tracking-widest">Sosial Media</h4>
<p class="text-xs text-slate-400 font-light">Ikuti sosial media kami untuk mendapatkan update produk terbaru.</p>
<div class="flex space-x-3 text-xs">
<a v-if="instagramUrl" :href="instagramUrl" target="_blank" class="text-slate-400 hover:text-cyan-600 dark:hover:text-cyan-400">Instagram</a>
<a v-if="facebookUrl" :href="facebookUrl" target="_blank" class="text-slate-400 hover:text-cyan-600 dark:hover:text-cyan-400">Facebook</a>
<a v-if="tiktokUrl" :href="tiktokUrl" target="_blank" class="text-slate-400 hover:text-cyan-600 dark:hover:text-cyan-400">TikTok</a>
</div>
</div>
</div>
<div class="max-w-7xl mx-auto px-6 mt-12 pt-8 border-t border-cyan-100/30 dark:border-cyan-950/20 flex flex-col sm:flex-row justify-between items-center text-[10px] text-slate-400 font-light space-y-4 sm:space-y-0">
<p>&copy; 2026 {{ appName }} (DSTCLO). Hak Cipta Dilindungi.</p>
<div class="flex space-x-6">
<a href="#" class="hover:underline">Kebijakan Privasi</a>
<a href="#" class="hover:underline">Syarat Ketentuan</a>
</div>
</div>
</footer>
<!-- CENTERED DETAIL MODAL (MODA DETAIL) -->
<div
v-if="activeProduct"
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm transition-opacity duration-300"
@click.self="closeQuickView"
>
<div
class="w-full max-w-3xl bg-[#FDFDFC] dark:bg-[#0E1726] rounded-3xl p-6 md:p-8 shadow-2xl flex flex-col md:flex-row gap-8 max-h-[90vh] overflow-y-auto border border-cyan-100/30 dark:border-cyan-950/30 scrollbar-thin"
>
<!-- Image Side -->
<div class="w-full md:w-1/2 flex flex-col space-y-3">
<div class="aspect-[3/4] rounded-2xl overflow-hidden bg-slate-50 dark:bg-neutral-900 border border-slate-200/50 dark:border-cyan-950/50 shadow-sm relative">
<img
:src="getProductImage(activeProduct)"
:alt="activeProduct.name"
class="w-full h-full object-cover"
/>
</div>
</div>
<!-- Info Side -->
<div class="w-full md:w-1/2 flex flex-col justify-between space-y-6 text-left">
<div class="space-y-4">
<div class="flex justify-between items-start">
<div>
<span class="text-[10px] font-bold uppercase tracking-widest text-cyan-600 dark:text-cyan-400">
{{ activeProduct.categories.map(c => c.name).join(', ') }}
</span>
<h3 class="text-2xl font-serif mt-0.5 text-slate-800 dark:text-white">{{ activeProduct.name }}</h3>
</div>
<button
@click="closeQuickView"
class="p-1.5 rounded-full hover:bg-slate-100 dark:hover:bg-cyan-950/30 transition-colors cursor-pointer"
>
<X class="w-5 h-5 text-slate-500" />
</button>
</div>
<!-- Description (Scrollbar thin applied) -->
<div class="space-y-1.5">
<h4 class="text-[10px] uppercase font-bold tracking-widest text-slate-400">Deskripsi</h4>
<p class="text-xs text-slate-500 dark:text-neutral-300 font-light leading-relaxed max-h-32 overflow-y-auto scrollbar-thin pr-1">
{{ activeProduct.description || 'Pakaian batik modern dan nyaman hasil karya perajin terbaik kami. Menggunakan serat kain pilihan yang awet dan menyejukkan kulit.' }}
</p>
</div>
<!-- Variant Select -->
<div v-if="activeProduct.variants.length > 0" class="space-y-2">
<h4 class="text-[10px] uppercase font-bold tracking-widest text-slate-400">Pilih Ukuran / Varian</h4>
<div class="flex flex-wrap gap-1.5">
<button
v-for="variant in activeProduct.variants"
:key="variant.id"
@click="activeVariant = variant"
:class="[
'px-3 py-1.5 rounded-xl text-xs font-semibold uppercase tracking-wider border cursor-pointer transition-all',
activeVariant?.id === variant.id
? 'bg-cyan-600 text-white border-transparent dark:bg-cyan-500 dark:text-slate-950 shadow-md shadow-cyan-200/30'
: 'bg-transparent text-slate-600 border-slate-200 dark:text-slate-300 dark:border-cyan-900/60'
]"
>
{{ variant.name }}
<span class="text-[9px] opacity-70 ml-1">({{ variant.stock }} pcs)</span>
</button>
</div>
</div>
<!-- Price Tiers -->
<div v-if="activeVariant && activeVariant.prices.length > 0" class="space-y-2">
<h4 class="text-[10px] uppercase font-bold tracking-widest text-slate-400">Saluran Harga (Grosir/Eceran)</h4>
<div class="grid grid-cols-2 gap-1.5 max-h-36 overflow-y-auto scrollbar-thin pr-1">
<button
v-for="price in activeVariant.prices"
:key="price.id"
@click="activePriceType = price.type"
:class="[
'px-3 py-2 text-left rounded-xl border transition-all cursor-pointer',
activePriceType === price.type
? 'border-cyan-500 bg-cyan-500/5 dark:border-cyan-400 dark:bg-cyan-400/5'
: 'border-slate-200 dark:border-cyan-900/60 hover:border-cyan-500 dark:hover:border-cyan-400'
]"
>
<span class="block text-[8px] uppercase tracking-wider font-bold text-slate-400 dark:text-slate-500">
{{ price.type_label }}
</span>
<span class="block text-xs font-bold mt-0.5 text-cyan-600 dark:text-cyan-400">
{{ formatPrice(price.price) }}
</span>
</button>
</div>
</div>
</div>
<!-- Cart Call to Action -->
<div class="pt-4 border-t border-slate-100 dark:border-cyan-950/50 space-y-4">
<div class="flex justify-between items-center text-xs">
<div>
<span class="text-[10px] uppercase font-bold tracking-widest text-slate-400">Harga Terpilih</span>
<div class="text-xl font-bold text-cyan-600 dark:text-cyan-400 mt-0.5">
{{ selectedPrice ? formatPrice(selectedPrice.price) : 'Pilih ukuran' }}
</div>
</div>
<div class="text-right">
<span class="text-[10px] uppercase font-bold tracking-widest text-slate-400">Sisa Stok</span>
<div class="font-semibold text-slate-700 dark:text-slate-300 mt-0.5">
{{ activeVariant ? `${activeVariant.stock} pcs` : 'Pilih ukuran' }}
</div>
</div>
</div>
<button
v-if="activeVariant && selectedPrice"
@click="addToCart(activeProduct, activeVariant, selectedPrice)"
:disabled="activeVariant.stock <= 0"
:class="[
'w-full py-3.5 text-xs font-bold uppercase tracking-widest rounded-xl transition-all cursor-pointer shadow-md',
activeVariant.stock > 0
? 'bg-cyan-600 text-white hover:bg-cyan-500 shadow-cyan-200/50 dark:bg-cyan-500 dark:text-slate-950 dark:shadow-none'
: 'bg-slate-100 text-slate-400 cursor-not-allowed dark:bg-slate-800 dark:text-slate-600 shadow-none'
]"
>
{{ activeVariant.stock > 0 ? 'Masukkan ke Keranjang' : 'Stok Habis' }}
</button>
</div>
</div>
</div>
</div>
<!-- FLOATING CART SLIDE-OVER SHEET -->
<div
v-if="isCartOpen"
class="fixed inset-0 z-50 flex items-center justify-end bg-black/60 backdrop-blur-sm transition-opacity duration-300"
@click.self="isCartOpen = false"
>
<div
class="w-full max-w-md h-full bg-[#FDFDFC] dark:bg-[#0E1726] p-6 shadow-2xl flex flex-col justify-between overflow-y-auto transform translate-x-0 transition-transform duration-300 border-l border-cyan-100/30 dark:border-cyan-950/30"
>
<div class="space-y-6">
<!-- Header -->
<div class="flex justify-between items-center">
<div class="flex items-center space-x-2">
<ShoppingBag class="w-5 h-5 text-cyan-600 dark:text-cyan-400" />
<h3 class="text-lg font-serif text-slate-800 dark:text-white">Keranjang Belanja ({{ cartCount }})</h3>
</div>
<button
@click="isCartOpen = false"
class="p-2 rounded-full hover:bg-slate-100 dark:hover:bg-cyan-950/30 transition-colors cursor-pointer"
>
<X class="w-5 h-5 text-slate-500" />
</button>
</div>
<!-- Cart List -->
<div v-if="cart.length > 0" class="space-y-4 divide-y divide-slate-100 dark:divide-cyan-950/45 max-h-[calc(100vh-280px)] overflow-y-auto pr-1 scrollbar-thin">
<div
v-for="item in cart"
:key="`${item.variant.id}-${item.price.type}`"
class="flex space-x-4 pt-4 first:pt-0"
>
<img
:src="getProductImage(item.product)"
:alt="item.product.name"
class="w-16 h-20 object-cover rounded-xl bg-slate-50 dark:bg-slate-900 border border-slate-200/50 dark:border-cyan-950/50"
/>
<div class="flex-1 flex flex-col justify-between">
<div class="space-y-1">
<div class="flex justify-between items-start">
<h4 class="text-xs font-semibold text-slate-700 dark:text-neutral-200 line-clamp-1">
{{ item.product.name }}
</h4>
<button
@click="removeFromCart(item.variant.id, item.price.type)"
class="text-slate-400 hover:text-red-500 p-1 cursor-pointer transition-colors"
>
<X class="w-3.5 h-3.5" />
</button>
</div>
<p class="text-[10px] text-slate-400 dark:text-neutral-500">
Ukuran: <strong class="text-slate-700 dark:text-neutral-300 font-semibold">{{ item.variant.name }}</strong> • Tipe: <strong class="text-slate-700 dark:text-neutral-300 font-semibold">{{ item.price.type_label }}</strong>
</p>
</div>
<div class="flex justify-between items-center mt-2">
<!-- Quantity Stepper -->
<div class="flex items-center space-x-2 border border-slate-200 dark:border-cyan-900 rounded-lg px-2 py-0.5">
<button
@click="updateCartQuantity(item.variant.id, item.price.type, -1)"
class="p-1 text-slate-500 hover:text-black dark:hover:text-white cursor-pointer"
>
<Minus class="w-3 h-3" />
</button>
<span class="text-xs font-semibold px-1 text-slate-800 dark:text-slate-100">{{ item.quantity }}</span>
<button
@click="updateCartQuantity(item.variant.id, item.price.type, 1)"
class="p-1 text-slate-500 hover:text-black dark:hover:text-white cursor-pointer"
>
<Plus class="w-3 h-3" />
</button>
</div>
<span class="text-xs font-bold text-cyan-600 dark:text-cyan-400">
{{ formatPrice(item.price.price * item.quantity) }}
</span>
</div>
</div>
</div>
</div>
<!-- Empty Cart -->
<div v-else class="text-center py-20 space-y-4">
<ShoppingBag class="w-12 h-12 text-cyan-200 dark:text-neutral-800 mx-auto" />
<h4 class="text-sm font-semibold text-slate-700 dark:text-slate-300">Keranjang Anda masih kosong</h4>
<p class="text-xs text-slate-400 dark:text-neutral-500 max-w-xs mx-auto font-light leading-relaxed">
Jelajahi produk batik dan pakaian pilihan kami untuk menambahkan item ke dalam keranjang.
</p>
<button
@click="isCartOpen = false"
class="bg-cyan-600 text-white dark:bg-cyan-500 dark:text-slate-950 px-6 py-2.5 text-xs font-semibold uppercase tracking-widest rounded-xl hover:opacity-90 transition-all cursor-pointer shadow-md shadow-cyan-200/50"
>
Lanjutkan Belanja
</button>
</div>
</div>
<!-- Footer Summary Card -->
<div v-if="cart.length > 0" class="border-t border-cyan-100/30 dark:border-cyan-950/30 pt-6 space-y-4">
<div class="space-y-1.5">
<div class="flex justify-between text-xs text-slate-500">
<span>Subtotal</span>
<span>{{ formatPrice(cartTotal) }}</span>
</div>
<div class="flex justify-between text-xs text-slate-500">
<span>Biaya Kirim</span>
<span class="text-emerald-500 font-medium">GRATIS</span>
</div>
<div class="flex justify-between text-sm font-bold text-slate-800 dark:text-white pt-1.5 border-t border-slate-100 dark:border-cyan-950/30">
<span>Estimasi Total</span>
<span class="text-cyan-600 dark:text-cyan-400">{{ formatPrice(cartTotal) }}</span>
</div>
</div>
<button
@click="handleWhatsAppCheckout"
:disabled="isCheckingOut"
class="w-full flex items-center justify-center bg-emerald-600 text-white hover:bg-emerald-500 py-3.5 text-xs font-bold uppercase tracking-widest rounded-xl transition-all cursor-pointer shadow-md shadow-emerald-100/50"
>
<span v-if="isCheckingOut" class="inline-block border-2 border-current border-t-transparent animate-spin rounded-full w-4 h-4 mr-2"></span>
<Phone class="w-4 h-4 mr-2" />
{{ isCheckingOut ? 'Mengalihkan...' : 'Pesan Via WhatsApp' }}
</button>
</div>
</div>
</div>
</div>
</template>
<style>
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}
/* Custom marquee animation */
@keyframes marquee {
0% {
transform: translateX(0%);
}
100% {
transform: translate3d(-50%, 0, 0);
}
}
.animate-marquee {
display: inline-block;
animation: marquee 25s linear infinite;
}
/* Float Animation */
@keyframes float-hero {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-12px);
}
}
.animate-float-model {
animation: float-hero 6s infinite ease-in-out;
}
.animate-float-tag-1 {
animation: float-hero 5s infinite ease-in-out;
}
.animate-float-tag-2 {
animation: float-hero 7s infinite ease-in-out;
}
/* Blob morphing background animation */
@keyframes blob-bounce {
0%, 100% {
transform: translate(0px, 0px) scale(1);
}
33% {
transform: translate(30px, -40px) scale(1.12);
}
66% {
transform: translate(-20px, 20px) scale(0.95);
}
}
.animate-blob {
animation: blob-bounce 14s infinite alternate ease-in-out;
}
.animation-delay-2000 {
animation-delay: 2s;
}
/* Animation classes */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translate3d(0, 20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
.animate-fade-in-up {
animation: fadeInUp 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
</style>