Kasir/resources/js/pages/Produk.vue
2025-08-28 14:35:04 +07:00

210 lines
5.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<mainLayout>
<div class="p-6">
<!-- Judul -->
<p class="font-serif italic text-[25px] text-D">PRODUK</p>
<!-- Filter -->
<div class="mt-3 flex flex-col md:flex-row md:items-center md:justify-between gap-3">
<!-- Dropdown Kategori -->
<select
v-model="selectedCategory"
class="border border-gray-300 rounded-md px-3 py-2 bg-B focus:outline-none focus:ring-2 focus:ring-B w-full md:w-48"
>
<option value="semua">Semua</option>
<option value="cincin">Cincin</option>
<option value="gelang">Gelang</option>
<option value="kalung">Kalung</option>
<option value="anting">Anting</option>
</select>
<!-- Search -->
<searchbar v-model:search="searchQuery" class="flex-1" />
</div>
<!-- Tombol Tambah Produk -->
<div class="mt-3 flex justify-end">
<button class="bg-C 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"
@click="openOverlay(item.id)"
/>
</div>
</div>
<!-- Overlay Detail Produk -->
<!-- Overlay Detail Produk -->
<!-- Overlay Detail Produk -->
<div
v-if="showOverlay"
class="fixed inset-0 bg-black/30 flex justify-center items-center z-50"
@click.self="closeOverlay"
>
<div
class="bg-white rounded-lg shadow-lg p-6 w-[450px] border-2 border-[#e6d3b3] relative flex flex-col items-center"
>
<!-- Foto Produk -->
<div class="relative w-72 h-72 border border-[#e6d3b3] flex items-center justify-center mb-3 overflow-hidden rounded">
<img
v-if="detail.foto && detail.foto.length > 0"
:src="detail.foto[currentFotoIndex].url"
:alt="detail.nama"
class="w-full h-full object-contain"
/>
<span v-else class="text-gray-400 text-sm">[gambar]</span>
<!-- Stok (pcs) pojok kiri atas -->
<div class="absolute top-1 left-1 bg-black/60 text-white text-xs px-2 py-1 rounded">
{{ detail.items_count }} pcs
</div>
<!-- Tombol Prev -->
<button
v-if="detail.foto && detail.foto.length > 1"
@click.stop="prevFoto"
class="absolute left-2 bg-white/80 hover:bg-white text-black px-2 py-1 rounded-full shadow"
>
</button>
<!-- Tombol Next -->
<button
v-if="detail.foto && detail.foto.length > 1"
@click.stop="nextFoto"
class="absolute right-2 bg-white/80 hover:bg-white text-black px-2 py-1 rounded-full shadow"
>
</button>
</div>
<!-- Nama Produk -->
<p class="text-lg font-semibold text-center mb-4">{{ detail.nama }}</p>
<!-- Detail Harga & Info -->
<div class="grid grid-cols-2 gap-y-2 gap-x-4 text-sm w-full mb-6">
<p class="col-span-1">Harga Jual :</p>
<p class="col-span-1 text-right">Rp. {{ formatNumber(detail.harga_jual) }}</p>
<p class="col-span-1">Kadar :</p>
<p class="col-span-1 text-right">{{ detail.kadar }} K</p>
<p class="col-span-1">Berat :</p>
<p class="col-span-1 text-right">{{ detail.berat }} gram</p>
<p class="col-span-1">Harga/gram :</p>
<p class="col-span-1 text-right">Rp. {{ formatNumber(detail.harga_per_gram) }}</p>
</div>
<!-- Tombol Aksi -->
<div class="flex w-full gap-3">
<button class="flex-1 bg-yellow-400 text-black py-2 rounded font-bold">
Ubah
</button>
<button class="flex-1 bg-green-400 text-black py-2 rounded font-bold">
Tambah
</button>
<button class="flex-1 bg-red-500 text-white 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("");
const selectedCategory = ref("semua");
// overlay state
const showOverlay = ref(false);
const detail = ref({});
const currentFotoIndex = ref(0);
// 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 gabungan (kategori + search)
const filteredProducts = computed(() => {
let hasil = products.value;
// filter kategori
if (selectedCategory.value !== "semua") {
hasil = hasil.filter(
(p) => p.kategori.toLowerCase() === selectedCategory.value
);
}
// filter search
if (searchQuery.value) {
hasil = hasil.filter((p) =>
p.nama.toLowerCase().includes(searchQuery.value.toLowerCase())
);
}
return hasil;
});
// buka overlay
function openOverlay(id) {
const produk = products.value.find((p) => p.id === id);
if (produk) {
detail.value = produk;
currentFotoIndex.value = 0; // reset ke foto pertama
showOverlay.value = true;
}
}
// tutup overlay
function closeOverlay() {
showOverlay.value = false;
detail.value = {};
currentFotoIndex.value = 0;
}
// foto navigation
function nextFoto() {
if (detail.value.foto && detail.value.foto.length > 0) {
currentFotoIndex.value =
(currentFotoIndex.value + 1) % detail.value.foto.length;
}
}
function prevFoto() {
if (detail.value.foto && detail.value.foto.length > 0) {
currentFotoIndex.value =
(currentFotoIndex.value - 1 + detail.value.foto.length) %
detail.value.foto.length;
}
}
// Format angka
function formatNumber(num) {
return new Intl.NumberFormat().format(num || 0);
}
</script>