fix bug
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<section class="container-page py-10">
|
||||
<RouterLink to="/marketplace" class="mb-6 inline-flex text-sm font-bold text-brand-700">
|
||||
<- Kembali ke Marketplace
|
||||
</RouterLink>
|
||||
|
||||
<div v-if="loading" class="card p-10 text-center">
|
||||
<h1 class="text-2xl font-extrabold">Memuat produk...</h1>
|
||||
</div>
|
||||
|
||||
<div v-else-if="product" class="grid gap-8 lg:grid-cols-[minmax(0,1fr)_minmax(380px,520px)]">
|
||||
<div class="grid gap-4 sm:grid-cols-[88px_1fr]">
|
||||
<div class="order-2 flex gap-3 overflow-x-auto sm:order-1 sm:grid sm:content-start sm:max-h-[78vh] sm:overflow-y-auto">
|
||||
<button
|
||||
v-for="(image, index) in productImages"
|
||||
:key="`${image}-${index}`"
|
||||
type="button"
|
||||
class="h-20 w-20 shrink-0 overflow-hidden rounded-xl border-2 bg-white"
|
||||
:class="selectedImage === image ? 'border-brand-600' : 'border-slate-200'"
|
||||
@click="selectedImage = image"
|
||||
>
|
||||
<img :src="getProductImageUrl(image)" :alt="`${product.title} ${index + 1}`" class="h-full w-full object-cover" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card order-1 overflow-hidden sm:order-2">
|
||||
<div class="flex min-h-[260px] items-center justify-center bg-black">
|
||||
<img
|
||||
:src="getProductImageUrl(selectedImage)"
|
||||
:alt="product.title"
|
||||
class="h-auto max-h-[78vh] w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span class="rounded-full bg-brand-50 px-3 py-1 text-sm font-bold text-brand-700">
|
||||
{{ product.category }}
|
||||
</span>
|
||||
<h1 class="mt-4 text-3xl font-extrabold text-slate-950">{{ product.title }}</h1>
|
||||
<p class="mt-3 text-3xl font-extrabold text-brand-700">{{ formatRupiah(product.price) }}</p>
|
||||
|
||||
<div class="mt-6 grid gap-3 sm:grid-cols-2">
|
||||
<div class="card p-4">
|
||||
<p class="text-xs font-bold uppercase text-slate-500">Kondisi</p>
|
||||
<p class="mt-1 font-bold">{{ product.condition }}</p>
|
||||
</div>
|
||||
<div class="card p-4">
|
||||
<p class="text-xs font-bold uppercase text-slate-500">Lokasi</p>
|
||||
<p class="mt-1 font-bold">{{ product.location }}</p>
|
||||
</div>
|
||||
<div class="card p-4">
|
||||
<p class="text-xs font-bold uppercase text-slate-500">Penjual</p>
|
||||
<p class="mt-1 font-bold">{{ product.seller }}</p>
|
||||
</div>
|
||||
<div class="card p-4">
|
||||
<p class="text-xs font-bold uppercase text-slate-500">WhatsApp</p>
|
||||
<p class="mt-1 font-bold">{{ product.whatsapp }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<h2 class="text-lg font-extrabold">Deskripsi</h2>
|
||||
<p class="mt-2 leading-7 text-slate-600">{{ product.description }}</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex flex-wrap gap-3">
|
||||
<a :href="waLink" target="_blank" class="btn-primary">Hubungi via WhatsApp</a>
|
||||
<button class="btn-secondary" @click="toggleSave">
|
||||
{{ saved ? "Hapus dari Tersimpan" : "Simpan Barang" }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="card p-10 text-center">
|
||||
<h1 class="text-2xl font-extrabold">Produk tidak ditemukan</h1>
|
||||
<RouterLink to="/marketplace" class="btn-primary mt-5">Lihat Marketplace</RouterLink>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, ref, watch } from "vue"
|
||||
import { useRoute } from "vue-router"
|
||||
import { products as defaultProducts } from "../data/products"
|
||||
import { fetchProduct, formatRupiah, getProductImageUrl, getSavedProducts, setSavedProducts } from "../utils"
|
||||
|
||||
const route = useRoute()
|
||||
const id = Number(route.params.id)
|
||||
const product = ref(null)
|
||||
const loading = ref(true)
|
||||
const selectedImage = ref("")
|
||||
|
||||
const productImages = computed(() => {
|
||||
if (!product.value) return []
|
||||
const images = product.value.images?.length ? product.value.images : [product.value.image]
|
||||
return [...new Set(images.filter(Boolean))]
|
||||
})
|
||||
|
||||
watch(
|
||||
productImages,
|
||||
(images) => {
|
||||
if (!images.includes(selectedImage.value)) {
|
||||
selectedImage.value = images[0] || ""
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const savedIds = ref(getSavedProducts())
|
||||
const saved = computed(() => savedIds.value.includes(id))
|
||||
|
||||
const waLink = computed(() => {
|
||||
if (!product.value) return "#"
|
||||
const text = encodeURIComponent(`Halo, saya tertarik dengan ${product.value.title} di SecondTech Market.`)
|
||||
return `https://wa.me/${product.value.whatsapp}?text=${text}`
|
||||
})
|
||||
|
||||
function toggleSave() {
|
||||
if (saved.value) {
|
||||
savedIds.value = savedIds.value.filter((item) => item !== id)
|
||||
} else {
|
||||
savedIds.value.push(id)
|
||||
}
|
||||
setSavedProducts(savedIds.value)
|
||||
}
|
||||
|
||||
async function loadProduct() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await fetchProduct(id)
|
||||
product.value = data.product
|
||||
} catch {
|
||||
const fallbackProduct = defaultProducts.find((item) => Number(item.id) === id) || null
|
||||
product.value = fallbackProduct ? { ...fallbackProduct, images: [fallbackProduct.image] } : null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadProduct)
|
||||
</script>
|
||||
Reference in New Issue
Block a user