Kasir/resources/js/pages/Produk.vue
2025-08-28 15:13:19 +07:00

212 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">
<router-link to="/produk/baru" class="bg-C text-[#0a1a3c] px-4 py-2 rounded-md shadow hover:bg-C transition">
Tambah Produk
</router-link>
</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 -->
<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-[400px] border-2 border-[#e6d3b3] relative flex flex-col items-center"
@mouseleave="closeOverlay"
>
<!-- Foto Produk dengan Slider -->
<div class="relative w-60 h-60 border border-[#e6d3b3] flex items-center justify-center mb-4 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.item_count }} pcs
</div>
<!-- Nama Produk di bawah -->
<div
class="absolute bottom-0 w-full bg-black/70 text-white text-center text-sm py-1"
>
{{ detail.nama }}
</div>
<!-- Tombol Prev -->
<button
v-if="detail.foto && detail.foto.length > 1"
@click.stop="prevFoto"
class="absolute left-2 bg-white/70 hover:bg-white text-black px-2 py-1 rounded"
>
</button>
<!-- Tombol Next -->
<button
v-if="detail.foto && detail.foto.length > 1"
@click.stop="nextFoto"
class="absolute right-2 bg-white/70 hover:bg-white text-black px-2 py-1 rounded"
>
</button>
</div>
<!-- Detail Harga & Info -->
<div class="grid grid-cols-2 gap-2 text-sm mb-4 w-full">
<!-- harga beli dihapus -->
<p>Harga Jual : Rp. {{ formatNumber(detail.harga_jual) }}</p>
<p class="text-right">{{ detail.kadar }} K</p>
<p class="col-span-2 text-center">
Berat : {{ 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 w-full">
<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("");
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("/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
async function openOverlay(id) {
try {
const res = await axios.get(`/api/produk/${id}`);
detail.value = res.data;
currentFotoIndex.value = 0; // reset ke foto pertama
showOverlay.value = true;
} catch (error) {
console.error("Gagal fetch detail produk:", error);
}
}
// 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>