Merge branch 'production' of https://git.abbauf.com/Magang-2025/Kasir into production

This commit is contained in:
dhilanradya 2025-08-28 13:46:16 +07:00
commit 0bb5b23ead
4 changed files with 205 additions and 59 deletions

View File

@ -0,0 +1,48 @@
<template>
<div class="relative inline-block text-left">
<!-- Tombol Dropdown -->
<button
@click="isOpen = !isOpen"
class="px-4 py-2 bg-white border rounded-md shadow-sm hover:bg-gray-100"
>
Manajemen Produk
</button>
<!-- Isi Dropdown -->
<div
v-if="isOpen"
class="absolute left-0 mt-2 w-48 bg-white border rounded-md shadow-lg z-50"
>
<ul>
<li
v-for="(item, index) in items"
:key="index"
@click="goTo(item.route)"
class="px-4 py-2 hover:bg-[#DAC0A3] cursor-pointer"
>
{{ item.label }}
</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
const isOpen = ref(false);
const router = useRouter();
const items = [
{ label: "Brankas", route: "/brankas" },
{ label: "Nampan", route: "/nampan" },
{ label: "Produk", route: "/produk" },
{ label: "Sales", route: "/sales" },
];
const goTo = (route) => {
isOpen.value = false;
router.push(route);
};
</script>

View File

@ -1,17 +1,70 @@
<script setup> <script setup>
const items = ['Manajemen Produk', 'Kasir', 'Laporan', 'Akun']; import { ref } from "vue";
const isOpen = ref(false);
const items = [
{ label: "Kasir", route: "/kasir" },
{ label: "Laporan", route: "/laporan" },
{ label: "Akun", route: "/akun" },
];
const subItems = [
{ label: "Brankas", route: "/brankas" },
{ label: "Nampan", route: "/nampan" },
{ label: "Produk", route: "/produk" },
{ label: "Sales", route: "/sales" },
];
const toggleDropdown = () => {
isOpen.value = !isOpen.value;
};
</script> </script>
<template> <template>
<div class="h-25 shadow-lg shadow-D rounded-b-md"> <div class="h-25 shadow-lg shadow-D rounded-b-md">
<div class="bg-D h-5 rounded-b-md shadow-lg"> <div class="bg-D h-5 rounded-b-md shadow-lg">
<div class="h-15"></div> <div class="h-15"></div>
<div class="w-full px-50 flex justify-between items-center h-5"> <div class="w-full px-50 flex justify-between items-center h-5 relative">
<router-link to="/" v-for="item in items"
class="text-center text-lg text-D hover:underline cursor-pointer"> <!-- Dropdown khusus "Manajemen Produk" -->
{{ item }} <div class="relative">
<button
@click="toggleDropdown"
class="text-center text-lg text-D hover:underline cursor-pointer"
>
Manajemen Produk
</button>
<!-- isi dropdown -->
<div
v-if="isOpen"
class="absolute left-0 mt-2 w-48 bg-white border rounded-md shadow-lg z-50"
>
<ul>
<li
v-for="(sub, index) in subItems"
:key="index"
class="px-4 py-2 hover:bg-[#DAC0A3] cursor-pointer"
>
<router-link :to="sub.route" class="block w-full h-full">
{{ sub.label }}
</router-link> </router-link>
</div> </li>
</ul>
</div>
</div> </div>
<!-- menu lain -->
<router-link
v-for="item in items"
:key="item.label"
:to="item.route"
class="text-center text-lg text-D hover:underline cursor-pointer"
>
{{ item.label }}
</router-link>
</div>
</div> </div>
</div>
</template> </template>

View File

@ -112,6 +112,11 @@ const totalWeight = (tray) => {
if (!tray.items) return 0; if (!tray.items) return 0;
return tray.items.reduce((sum, item) => sum + (item.produk.berat || 0), 0); return tray.items.reduce((sum, item) => sum + (item.produk.berat || 0), 0);
}; };
// const totalWeight = (tray) => {
// if (!tray.items) return 0;
// const total = tray.items.reduce((sum, item) => sum + (item.produk.berat || 0), 0);
// return total.toFixed(2); // hasil string "12.34"
// };
// ambil data dari backend // ambil data dari backend
onMounted(async () => { onMounted(async () => {

View File

@ -1,34 +1,54 @@
<template> <template>
<mainLayout> <mainLayout>
<div class="flex justify-between items-center mb-4">
<p style="font-family: 'IM FELL Great Primer', serif; font-style: italic;font-size: 25px;">NAMPAN</p>
<div class="flex gap-2">
<button
@click="openModal"
class="px-4 py-2 hover:bg-green-700 rounded-md shadow font-semibold"
style="background-color: #DAC0A3; color: #102C57;">
Tambah Nampan
</button>
<button <!-- Header -->
@click="emptyTray" <div class="mb-4">
class="px-3 py-2 bg-red-500 hover:bg-red-600 text-white rounded-md"> <!-- Judul -->
Kosongkan <p style="font-family: 'IM FELL Great Primer', serif; font-style: italic; font-size: 25px;">
</button> NAMPAN
</div> </p>
</div>
<searchbar v-model:search="searchQuery" /> <!-- Searchbar -->
<TrayList :search="searchQuery" <div class="flex justify-end mt-2">
@edit="editTray" <div class="w-64">
@delete="deleteTray"/> <searchbar v-model:search="searchQuery" />
<div </div>
</div>
<!-- Tombol -->
<div class="flex gap-2 mt-3 justify-end">
<!-- Tambah Nampan -->
<button
@click="openModal"
class="px-4 py-2 hover:bg-green-700 rounded-md shadow font-semibold"
style="background-color: #DAC0A3; color: #102C57;">
Tambah Nampan
</button>
<!-- Kosongkan -->
<button
@click="openConfirmModal"
class="px-3 py-2 bg-red-500 hover:bg-red-600 text-white rounded-md">
Kosongkan
</button>
</div>
</div>
<!-- Search + List -->
<TrayList :search="searchQuery" @edit="editTray" @delete="deleteTray"/>
<!-- Modal Tambah/Edit Nampan -->
<div
v-if="showModal" v-if="showModal"
class="fixed inset-0 bg-black bg-opacity-40 flex justify-center items-center z-50" class="fixed inset-0 bg-black bg-opacity-40 flex justify-center items-center z-50"
> >
<div class="bg-white rounded-lg shadow-lg p-6 w-96"> <div class="bg-white rounded-lg shadow-lg p-6 w-96">
<h2 class="text-lg font-semibold mb-4">Tambah Nampan</h2> <h2 class="text-lg font-semibold mb-4" style="color: #102C57;">Tambah Nampan</h2>
<label class="block mb-2 text-sm font-medium">Nama Nampan</label> <label class="block mb-2 text-sm font-medium" style="color: #102C57;">Nama Nampan</label>
<input <input
v-model="trayName" v-model="trayName"
type="text" type="text"
@ -45,16 +65,43 @@
<button <button
@click="saveTray" @click="saveTray"
class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md"> class="px-4 py-2 bg-[#DAC0A3] hover:bg-[#C9A77E] rounded-md"
style="color: #102C57;">
Save Save
</button> </button>
</div> </div>
</div> </div>
</div>
<!-- Modal Konfirmasi Kosongkan -->
<div
v-if="showConfirmModal"
class="fixed inset-0 bg-black bg-opacity-40 flex justify-center items-center z-50"
>
<div class="bg-white rounded-lg shadow-lg p-6 w-96 text-center">
<h2 class="text-xl font-bold mb-3" style="color: #102C57;">Kosongkan semua nampan?</h2>
<p class="text-gray-600 mb-6">
Semua item akan dimasukkan ke brankas. <br/>
Masuk ke menu Brankas untuk mengembalikan item ke nampan.
</p>
<div class="flex justify-center gap-4">
<button
@click="closeConfirmModal"
class="px-5 py-2 bg-gray-300 hover:bg-gray-400 rounded-md font-semibold">
Batal
</button>
<button
@click="confirmEmptyTray"
class="px-5 py-2 bg-red-500 hover:bg-red-600 text-white rounded-md font-semibold">
Ya
</button>
</div>
</div> </div>
</mainLayout> </div>
</mainLayout>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
import axios from 'axios' import axios from 'axios'
@ -62,35 +109,31 @@ import mainLayout from '../layouts/mainLayout.vue'
import searchbar from '../components/searchbar.vue' import searchbar from '../components/searchbar.vue'
import TrayList from '../components/TrayList.vue' import TrayList from '../components/TrayList.vue'
const searchQuery = ref("") // buat search const searchQuery = ref("")
const showModal = ref(false) // <-- ini penting, biar tidak undefined const showModal = ref(false)
const trayName = ref("") // nama nampan baru const showConfirmModal = ref(false)
const trayName = ref("")
const editingTrayId = ref(null)
// buka modal // buka modal tambah/edit
const openModal = () => { const openModal = () => { showModal.value = true }
showModal.value = true
}
// tutup modal
const closeModal = () => { const closeModal = () => {
trayName.value = "" trayName.value = ""
editingTrayId.value = null editingTrayId.value = null
showModal.value = false showModal.value = false
} }
// simpan nampan baru
// simpan nampan
const saveTray = async () => { const saveTray = async () => {
if (!trayName.value.trim()) { if (!trayName.value.trim()) {
alert("Nama Nampan tidak boleh kosong") alert("Nama Nampan tidak boleh kosong")
return return
} }
try { try {
if (editingTrayId.value) { if (editingTrayId.value) {
// mode edit
await axios.put(`/api/nampan/${editingTrayId.value}`, { nama: trayName.value }) await axios.put(`/api/nampan/${editingTrayId.value}`, { nama: trayName.value })
alert("Nampan berhasil diupdate") alert("Nampan berhasil diupdate")
} else { } else {
// mode tambah
await axios.post("/api/nampan", { nama: trayName.value }) await axios.post("/api/nampan", { nama: trayName.value })
alert("Nampan berhasil ditambahkan") alert("Nampan berhasil ditambahkan")
} }
@ -102,14 +145,15 @@ const saveTray = async () => {
} }
} }
// === Konfirmasi kosongkan nampan ===
const openConfirmModal = () => { showConfirmModal.value = true }
const closeConfirmModal = () => { showConfirmModal.value = false }
// kosongkan semua nampan const confirmEmptyTray = async () => {
const emptyTray = async () => {
if (!confirm("Yakin ingin memindahkan semua item ke Brankas?")) return
try { try {
await axios.post("/api/brankas", { action: "move_all_from_tray" }) await axios.post("/api/brankas", { action: "move_all_from_tray" })
alert("Semua item berhasil dipindahkan ke Brankas") alert("Semua item berhasil dipindahkan ke Brankas")
closeConfirmModal()
location.reload() location.reload()
} catch (error) { } catch (error) {
console.error(error) console.error(error)
@ -118,7 +162,6 @@ const emptyTray = async () => {
} }
const editTray = (tray) => { const editTray = (tray) => {
// buka modal edit, bisa pake sama seperti modal tambah
trayName.value = tray.nama trayName.value = tray.nama
editingTrayId.value = tray.id editingTrayId.value = tray.id
showModal.value = true showModal.value = true
@ -135,7 +178,4 @@ const deleteTray = async (id) => {
alert("Gagal menghapus nampan") alert("Gagal menghapus nampan")
} }
} }
const editingTrayId = ref(null)
</script> </script>