Berikut adalah contoh implementasi Product Detail di frontend beserta desain nya. Selain itu, kita juga akan mengedit halaman home (frontend) agar produk dapat di-klik untuk menuju halaman detail produk.
1. Routing & Controller untuk Product Detail
Tambahkan route di routes/web.php
untuk halaman detail produk. Kita akan identifikasi produk menggunakan slug
:
use App\Http\Controllers\FrontendController;
Route::get('products/{slug}', [FrontendController::class, 'show'])->name('products.show');
Di FrontendController.php
, tambahkan method show
:
public function show($slug)
{
$product = Product::with('images', 'category')->where('slug', $slug)->firstOrFail();
return Inertia::render('Frontend/ProductDetail', [
'product' => $product
]);
}
2. Edit Halaman Home (Frontend/Homepage.vue)
Ubah bagian card product pada homepage agar klik nama produk (atau gambar) membawa user ke halaman detail produk. Gunakan Inertia <Link>
:
<!-- PRODUCT GRID -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-6">
<div
v-for="product in products.data"
:key="product.id"
class="bg-white p-4 rounded shadow hover:shadow-md transition-shadow"
>
<Link :href="route('products.show', product.slug)" class="block relative pb-1/1 overflow-hidden rounded">
<img
v-if="product.images && product.images.length > 0"
:src="`/storage/${product.images[0].image}`"
alt="Product Image"
class="w-full h-full object-cover rounded"
/>
<div v-else class="absolute h-full w-full flex items-center justify-center text-gray-400">
No Image
</div>
</Link>
<Link :href="route('products.show', product.slug)">
<h2 class="mt-2 font-semibold text-sm text-gray-800 line-clamp-2 hover:text-blue-600 transition-colors">{{ product.name }}</h2>
</Link>
<p class="text-xs text-gray-500">{{ product.category ? product.category.name : 'No Category' }}</p>
<p class="text-red-600 font-bold mt-1 text-sm">Rp {{ product.price }}</p>
</div>
</div>
3. Membuat ProductDetail.vue
Buat file resources/js/Pages/Frontend/ProductDetail.vue
:
<template>
<div class="min-h-screen flex flex-col bg-gray-50">
<!-- NAVBAR -->
<header class="bg-white shadow z-10">
<div class="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
<div class="text-2xl font-bold text-gray-800">MyShop</div>
<nav class="flex items-center space-x-6">
<Link href="/" class="text-gray-700 hover:text-gray-900 font-medium px-3 py-2 transition-colors">Home</Link>
<Link href="/categories" class="text-gray-700 hover:text-gray-900 font-medium px-3 py-2 transition-colors">Categories</Link>
<Link href="/about" class="text-gray-700 hover:text-gray-900 font-medium px-3 py-2 transition-colors">About</Link>
</nav>
</div>
</header>
<!-- MAIN CONTENT -->
<main class="flex-1 py-10">
<div class="max-w-6xl mx-auto px-4">
<!-- Back Link -->
<div class="mb-4">
<Link href="/" class="text-sm text-blue-600 hover:text-blue-800">← Back to Home</Link>
</div>
<div class="md:flex md:space-x-10">
<!-- Image Gallery -->
<div class="md:w-1/2 mb-8 md:mb-0">
<!-- Main Image -->
<div class="relative w-full h-96 bg-white rounded-lg shadow-lg">
<img
v-if="selectedImage || product.images.length > 0"
:src="`/storage/${selectedImage || product.images[0].image}`"
alt="Product Image"
class="absolute inset-0 w-full h-full object-cover rounded-lg"
/>
<div v-else class="absolute inset-0 w-full h-full flex items-center justify-center text-gray-400">
No Image Available
</div>
</div>
<!-- Thumbnails -->
<div class="flex space-x-2 mt-4 overflow-x-auto pb-2">
<div
v-for="(img, index) in product.images"
:key="index"
@click="selectedImage = img.image"
class="w-16 h-16 relative border rounded-lg overflow-hidden cursor-pointer hover:opacity-80 transition"
>
<img :src="`/storage/${img.image}`" class="absolute w-full h-full object-cover" />
</div>
</div>
</div>
<!-- Product Info -->
<div class="md:w-1/2">
<h1 class="text-3xl font-bold text-gray-800 mb-3">{{ product.name }}</h1>
<p class="text-sm text-gray-500 mb-1">Category: {{ product.category ? product.category.name : 'No Category' }}</p>
<p class="text-xl text-red-600 font-bold mb-6">Rp {{ product.price }}</p>
<p class="text-gray-700 leading-relaxed mb-8">{{ product.description }}</p>
<div class="flex items-center space-x-4 mb-8">
<button class="px-6 py-3 bg-blue-600 text-white font-semibold rounded hover:bg-blue-700 transition">Add to Cart</button>
<button class="px-6 py-3 bg-gray-200 text-gray-700 font-semibold rounded hover:bg-gray-300 transition">
Wishlist
</button>
</div>
<div class="border-t pt-6 space-y-4">
<h2 class="text-lg font-semibold text-gray-800">Product Details</h2>
<ul class="space-y-1 text-gray-600 text-sm list-disc list-inside">
<li>High-quality material</li>
<li>Available in various sizes</li>
<li>30-day return policy</li>
</ul>
</div>
</div>
</div>
</div>
</main>
<!-- FOOTER -->
<footer class="bg-white py-4">
<div class="max-w-7xl mx-auto px-4 text-center text-gray-600 text-sm">
© 2024 MyShop. All rights reserved.
</div>
</footer>
</div>
</template>
<script>
import { ref } from 'vue';
import { Link } from '@inertiajs/inertia-vue3';
export default {
props: {
product: Object
},
components: { Link },
setup(props) {
const selectedImage = ref(null); // Set initial image if no thumbnail clicked
return { selectedImage };
},
};
</script>
<style scoped>
/* Ensuring that the image maintains its aspect ratio */
.relative {
position: relative;
}
.object-cover {
object-fit: cover;
}
</style>
4. Lakukan Testing
Buka homepage (http://localhost:8000
). Kemudian klik gambar, atau nama productnya yang akan mengarah ke halaman detail Product.
Jika berhasil, akan seperti berikut:
Selamat mencoba!!! Jika ada error, silahkan tinggalkan kolom komentar