154 lines
4.1 KiB
Vue
154 lines
4.1 KiB
Vue
<template>
|
|
<mainLayout>
|
|
<div class="p-6">
|
|
<!-- Judul -->
|
|
<p class="font-serif italic text-[25px] text-D">PRODUK</p>
|
|
|
|
<!-- Search -->
|
|
<searchbar v-model:search="searchQuery" />
|
|
|
|
<!-- Tombol Tambah Produk -->
|
|
<div class="mt-3 flex justify-end">
|
|
<button
|
|
class="bg-B text-[#0a1a3c] px-4 py-2 rounded-md shadow hover:bg-C transition"
|
|
>
|
|
Tambah Produk
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Grid Produk -->
|
|
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4 mt-4">
|
|
<ProductCard
|
|
v-for="item in filteredProducts"
|
|
:key="item.id"
|
|
:product="item"
|
|
@showDetail="openOverlay"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Overlay Detail Produk -->
|
|
<div
|
|
v-if="showOverlay"
|
|
class="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50"
|
|
@click.self="closeOverlay"
|
|
>
|
|
<div
|
|
class="bg-white rounded-lg shadow-lg p-6 w-[400px] border-2 border-[#e6d3b3] relative"
|
|
>
|
|
<!-- Tombol Close -->
|
|
<button
|
|
@click="closeOverlay"
|
|
class="absolute top-2 right-2 text-gray-500 hover:text-black"
|
|
>
|
|
✕
|
|
</button>
|
|
|
|
<!-- Foto Produk -->
|
|
<div class="border border-[#e6d3b3] p-2 mb-4 flex justify-center">
|
|
<img
|
|
v-if="detail.gambar"
|
|
:src="`http://127.0.0.1:8000/storage/${detail.gambar}`"
|
|
:alt="detail.nama"
|
|
class="w-40 h-40 object-contain"
|
|
/>
|
|
<span v-else class="text-gray-400 text-sm">[gambar]</span>
|
|
</div>
|
|
|
|
<!-- Stok -->
|
|
<p class="text-sm mb-1">{{ detail.item_count }} pcs</p>
|
|
|
|
<!-- Nama Produk -->
|
|
<h2 class="text-xl font-semibold text-center mb-3">
|
|
{{ detail.nama }}
|
|
</h2>
|
|
|
|
<!-- Detail Harga & Info -->
|
|
<div class="grid grid-cols-2 gap-2 text-sm mb-4">
|
|
<p>Harga Beli : Rp. {{ formatNumber(detail.harga_beli) }}</p>
|
|
<p class="text-right">{{ detail.kadar }} K</p>
|
|
<p>Harga Jual : Rp. {{ formatNumber(detail.harga_jual) }}</p>
|
|
<p class="text-right">{{ detail.berat }} gram</p>
|
|
<p class="col-span-2">
|
|
Harga/gram : Rp. {{ formatNumber(detail.harga_per_gram) }}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Tombol Aksi -->
|
|
<div class="flex justify-between">
|
|
<button
|
|
class="bg-yellow-400 text-black px-4 py-2 rounded font-bold"
|
|
>
|
|
Ubah
|
|
</button>
|
|
<button
|
|
class="bg-green-400 text-black px-4 py-2 rounded font-bold"
|
|
>
|
|
Tambah
|
|
</button>
|
|
<button
|
|
class="bg-red-500 text-white px-4 py-2 rounded font-bold"
|
|
>
|
|
Hapus
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</mainLayout>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, computed } from "vue";
|
|
import axios from "axios";
|
|
import mainLayout from "../layouts/mainLayout.vue";
|
|
import ProductCard from "../components/ProductCard.vue";
|
|
import searchbar from "../components/searchbar.vue";
|
|
|
|
const products = ref([]);
|
|
const searchQuery = ref("");
|
|
|
|
// overlay state
|
|
const showOverlay = ref(false);
|
|
const detail = ref({});
|
|
|
|
// Fetch data awal
|
|
onMounted(async () => {
|
|
try {
|
|
const res = await axios.get("http://127.0.0.1:8000/api/produk");
|
|
products.value = res.data;
|
|
} catch (error) {
|
|
console.error("Gagal ambil data produk:", error);
|
|
}
|
|
});
|
|
|
|
// Filter
|
|
const filteredProducts = computed(() => {
|
|
if (!searchQuery.value) return products.value;
|
|
return products.value.filter((p) =>
|
|
p.nama.toLowerCase().includes(searchQuery.value.toLowerCase())
|
|
);
|
|
});
|
|
|
|
// Fungsi buka overlay
|
|
async function openOverlay(id) {
|
|
try {
|
|
const res = await axios.get(`http://127.0.0.1:8000/api/produk/${id}`);
|
|
detail.value = res.data;
|
|
showOverlay.value = true;
|
|
} catch (error) {
|
|
console.error("Gagal fetch detail produk:", error);
|
|
}
|
|
}
|
|
|
|
// Fungsi tutup overlay
|
|
function closeOverlay() {
|
|
showOverlay.value = false;
|
|
detail.value = {};
|
|
}
|
|
|
|
// Format angka
|
|
function formatNumber(num) {
|
|
return new Intl.NumberFormat().format(num || 0);
|
|
}
|
|
</script>
|