Merge branch 'main' into production

This commit is contained in:
Baghaztra 2025-10-21 16:37:26 +07:00
commit ddaefdc57d
17 changed files with 1688 additions and 205 deletions

View File

@ -1,7 +1,7 @@
APP_NAME=Abbauf-Kasir APP_NAME=Abbauf-Kasir
APP_ENV=production APP_ENV=local
APP_KEY= APP_KEY=
APP_DEBUG=false APP_DEBUG=true
APP_URL=http://localhost APP_URL=http://localhost
APP_LOCALE=en APP_LOCALE=en

View File

@ -144,14 +144,17 @@ docker exec -it abbauf_kasir_app php artisan migrate --seed
# Siapkan penyimpanan file # Siapkan penyimpanan file
docker exec -it abbauf_kasir_app php artisan storage:link docker exec -it abbauf_kasir_app php artisan storage:link
# Atau import database secara manual
docker exec -i abbauf_kasir_db mysql -u kasir_user -pkasir_password kasir_db < ./toko_emas.sql
# Periksa database (opsional) # Periksa database (opsional)
docker exec -it abbauf_kasir_db bash docker exec -it abbauf_kasir_db bash
mysql -u kasir_user -pkasir_password mysql -u kasir_user -pkasir_password kasir_db
``` ```
### 8. Print Label ### 8. Print Label
- Install driver, ada pada folder `./driver/` untuk windows 64bit. - Install driver, ada pada folder `./driver/NiimbotPrinterDriverInstall_3.0.0.5.exe` untuk windows 64bit.
- Pilih `NIIMBOT B3S_P` pada saat install driver. - Pilih `NIIMBOT B3S_P` pada saat install driver.
- Sambungkan printer ke komputer via USB. - Sambungkan printer ke komputer via USB.
- Nyalakan printer. - Nyalakan printer.
@ -160,6 +163,16 @@ mysql -u kasir_user -pkasir_password
- Pilih printer `NIIMBOT B3S_P` dan atur kertas ke ukuran kertas `40mm x 30mm`, margin `Default`, scale `Default` - Pilih printer `NIIMBOT B3S_P` dan atur kertas ke ukuran kertas `40mm x 30mm`, margin `Default`, scale `Default`
- Klik print - Klik print
### 9. Print Nota
- Pastikan printer terhubung dengan komputer via USB.
- Nyalakan printer.
- Install driver, ada pada folder `./driver/L120_x64_213UsHomeExportAsiaML.exe`.
- Lakukan transaksi penjualan pada aplikasi, atau pilih nota yang akan diprint di `Laporan > Riwayat transaksi`.
- Klik tombol print pada halaman tersebut
- Pilih ukuran kertas A4, margin `Minimum`, scale `95`
- Klik print
--- ---
## 🌐 Akses Aplikasi ## 🌐 Akses Aplikasi

View File

@ -104,7 +104,7 @@ class TransaksiController extends Controller
'ongkos_bikin' => 'nullable|numeric|min:0', 'ongkos_bikin' => 'nullable|numeric|min:0',
'total_harga' => 'required|numeric', 'total_harga' => 'required|numeric',
'items' => 'required|array', 'items' => 'required|array',
'items.*.kode_item' => 'required|exists:items,id|numeric', 'items.*.kode_item' => 'required',
'items.*.harga_deal' => 'required|numeric', 'items.*.harga_deal' => 'required|numeric',
]); ]);

View File

@ -23,24 +23,15 @@ class Item extends Model
{ {
parent::boot(); parent::boot();
static::creating(function ($item) { static::created(function ($item) {
$prefix = 'TMJC'; if (!$item->kode_item || $item->kode_item === 'belum pak') {
$date = now()->format('Ymd'); $prefix = "TMJC";
$date = $item->created_at->format('Ymd');
$number = str_pad($item->id, 4, '0', STR_PAD_LEFT);
// Cari item terakhir yg dibuat hari ini $item->kode_item = $prefix . $date . $number;
$lastItem = self::whereDate('created_at', now()->toDateString()) $item->save();
->orderBy('id', 'desc')
->first();
$number = 1;
if ($lastItem && $lastItem->kode_item) {
// Ambil 4 digit terakhir dari kode_item
$lastNumber = intval(substr($lastItem->kode_item, -4));
$number = $lastNumber + 1;
} }
// Format: ITM202509090001
$item->kode_item = $prefix . $date . str_pad($number, 4, '0', STR_PAD_LEFT);
}); });
} }

View File

@ -12,10 +12,10 @@ return new class extends Migration
public function up() public function up()
{ {
Schema::table('items', function (Blueprint $table) { Schema::table('items', function (Blueprint $table) {
$table->string('kode_item')->unique()->after('id'); $table->string('kode_item')->unique()->default('belum pak')->after('id');
}); });
} }
public function down() public function down()
{ {
Schema::table('items', function (Blueprint $table) { Schema::table('items', function (Blueprint $table) {

View File

@ -37,17 +37,9 @@ class DatabaseSeeder extends Seeder
'updated_at' => now(), 'updated_at' => now(),
]); ]);
// Create sales record
Sales::create([
'nama' => 'Kasir',
'no_hp' => '-',
'alamat' => '-',
'created_at' => now(),
'updated_at' => now(),
]);
// Call other seeders // Call other seeders
$this->call(DataSeeder::class); $this->call(DataSeeder::class);
// $this->call(DummySeeder::class); $this->call(DummySeeder::class);
} }
} }

View File

@ -249,12 +249,14 @@ const tambahItem = () => {
return; return;
} }
item.value.kode_item = Number(kodeItem.value); item.value.kode_item = kodeItem.value;
item.value.harga_deal = Number(hargaJual.value); item.value.harga_deal = Number(hargaJual.value);
item.value.posisi = item.value.nampan ? item.value.nampan.nama : "Brankas"; item.value.posisi = item.value.nampan ? item.value.nampan.nama : "Brankas";
pesanan.value.push(item.value); pesanan.value.push(item.value);
console.log("Pesanan +:", item.value);
kodeItem.value = ""; kodeItem.value = "";
hargaJual.value = null; hargaJual.value = null;
hargaJualFormatted.value = ""; hargaJualFormatted.value = "";

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="space-y-3"> <div class="space-y-3">
<!-- Summary Card --> <!-- Summary Card -->
<div class="mt-3 bg-A border border-C rounded-lg p-3"> <div class="mt-3 bg-A border border-C rounded-lg p-3">
@ -51,7 +51,7 @@
<td class="border border-gray-200 p-2 text-center"> <td class="border border-gray-200 p-2 text-center">
<button <button
@click="lihatDetail(trx)" @click="lihatDetail(trx)"
class="px-3 py-1 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors text-xs whitespace-nowrap" class="px-3 py-1 bg-C text-D rounded-md hover:bg-blue-600 transition-colors text-xs whitespace-nowrap"
:disabled="isDetailLoading && selectedTransaksi.id === trx.id" :disabled="isDetailLoading && selectedTransaksi.id === trx.id"
> >
<span v-if="isDetailLoading && selectedTransaksi.id === trx.id"> <span v-if="isDetailLoading && selectedTransaksi.id === trx.id">
@ -72,7 +72,7 @@
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span>Menampilkan {{ pagination.from }} - {{ pagination.to }} dari {{ pagination.total }} transaksi hari ini</span> <span>Menampilkan {{ pagination.from }} - {{ pagination.to }} dari {{ pagination.total }} transaksi hari ini</span>
</div> </div>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<button <button
@click="$emit('page-change', pagination.current_page - 1)" @click="$emit('page-change', pagination.current_page - 1)"
@ -81,11 +81,11 @@
> >
Prev Prev
</button> </button>
<span class="px-2 text-gray-700"> <span class="px-2 text-gray-700">
{{ pagination.current_page }} / {{ pagination.last_page }} {{ pagination.current_page }} / {{ pagination.last_page }}
</span> </span>
<button <button
@click="$emit('page-change', pagination.current_page + 1)" @click="$emit('page-change', pagination.current_page + 1)"
:disabled="pagination.current_page === pagination.last_page" :disabled="pagination.current_page === pagination.last_page"
@ -101,7 +101,7 @@
<div v-else-if="!loading" class="text-center py-12 bg-gray-50 rounded-lg border border-gray-200"> <div v-else-if="!loading" class="text-center py-12 bg-gray-50 rounded-lg border border-gray-200">
<div class="text-gray-500 space-y-3"> <div class="text-gray-500 space-y-3">
<svg class="w-16 h-16 mx-auto text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-16 h-16 mx-auto text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1" /> d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1" />
</svg> </svg>
<div class="space-y-1"> <div class="space-y-1">
@ -219,4 +219,4 @@ const closeDetail = () => {
isDetailOpen.value = false isDetailOpen.value = false
selectedTransaksi.value = {} selectedTransaksi.value = {}
} }
</script> </script>

View File

@ -37,7 +37,9 @@ const printBarcode = () => {
const printWindow = window.open('', '_blank'); const printWindow = window.open('', '_blank');
const kode = props.code || 'N/A'; const kode = props.code || 'N/A';
const nama = props.item.nama || 'N/A'; const nama = props.item.nama || 'N/A';
const berat = props.item.berat ? `(${props.item.berat} g)` : ''; const berat = props.item.berat ? `${props.item.berat} g` : '';
const kadar = props.item.kadar ? `${props.item.kadar} K` : '';
const harga = props.item.harga_jual ? `Rp${props.item.harga_jual.toLocaleString('id-ID')},00` : '';
printWindow.document.write(` printWindow.document.write(`
<html> <html>
@ -70,29 +72,47 @@ const printBarcode = () => {
height: 38mm; height: 38mm;
} }
.barcode-container { .left-side {
width: 12mm; width: 12.5mm;
height: 38mm; height: 38mm;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} position: relative;
}
.barcode-img {
transform: rotate(90deg); .left-content {
transform-origin: center; position: absolute;
max-height: 12mm; width: fit-content;
max-width: 12mm; height: 12.5mm;
} transform: rotate(90deg);
transform-origin: center;
.details-container { display: flex;
width: 12mm; align-items: center;
gap: 2mm;
}
.barcode-img {
height: 10mm;
width: auto;
flex-shrink: 0;
}
.info-box {
display: flex;
flex-direction: column;
gap: 0.5mm;
font-size: 5pt;
line-height: 1.2;
}
.right-side {
width: 12.5mm;
height: 38mm; height: 38mm;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
position: relative; position: relative;
overflow: hidden;
} }
.item-name { .item-name {
@ -101,25 +121,30 @@ const printBarcode = () => {
white-space: normal; white-space: normal;
word-wrap: break-word; word-wrap: break-word;
position: absolute; position: absolute;
width: 32mm; width: 36mm;
text-align: center;
transform: rotate(270deg); transform: rotate(270deg);
top: 50%;
left: 50%;
transform-origin: center; transform-origin: center;
translate: -50% -50%;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="label"> <div class="label">
<div class="barcode-container"> <!-- Sisi Kiri: Barcode + Info -->
<img id="barcode-img" class="barcode-img" <div class="left-side">
src="${barcodeUrl.value}" alt="Barcode" /> <div class="left-content">
<img id="barcode-img" class="barcode-img" src="${barcodeUrl.value}" alt="Barcode" />
<div class="info-box">
${harga ? `<div>${harga}</div>` : ''}
${berat ? `<div>Berat: ${berat}</div>` : ''}
${kadar ? `<div>Kadar: ${kadar}</div>` : ''}
</div>
</div>
</div> </div>
<div class="details-container"> <div class="right-side">
<div class="item-name">${nama} ${berat}</div> <div class="item-name">${nama}</div>
</div> </div>
</div> </div>
</body> </body>

View File

@ -18,7 +18,7 @@
<p class="flex items-center gap-2"> <p class="flex items-center gap-2">
<i class="fab fa-whatsapp text-green-500 text-xl"></i> 08158851178 <i class="fab fa-whatsapp text-green-500 text-xl"></i> 08158851178
</p> </p>
<p class=" text-sm">{{ generateTransactionCode() }}</p> <p class="text-sm">TRSXXXXXXXXXXXX</p>
</div> </div>
<div class="absolute inset-x-0 top-[-48px] flex flex-col items-center"> <div class="absolute inset-x-0 top-[-48px] flex flex-col items-center">
@ -108,7 +108,7 @@
<div class="w-[20%] p-2 flex flex-col items-center justify-center"> <div class="w-[20%] p-2 flex flex-col items-center justify-center">
<p><strong>Hormat Kami</strong></p> <p><strong>Hormat Kami</strong></p>
<inputSelect v-model="selectedSales" :options="salesOptions" <inputSelect v-model="selectedSales" :options="salesOptions" placeholder="Pilih Sales"
class="mt-16 text-sm rounded bg-B cursor-pointer !w-[160px] text-center [option]:text-left" /> class="mt-16 text-sm rounded bg-B cursor-pointer !w-[160px] text-center [option]:text-left" />
</div> </div>
@ -189,18 +189,9 @@ import inputSelect from '@/components/InputSelect.vue'
import axios from 'axios' import axios from 'axios'
const props = defineProps({ const props = defineProps({
isOpen: { isOpen: Boolean,
type: Boolean, pesanan: Array,
default: false, total: Number
},
pesanan: {
type: Array,
default: () => []
},
total: {
type: Number,
default: 0
}
}) })
const emit = defineEmits(['close', 'confirm', 'transaksi-saved']) const emit = defineEmits(['close', 'confirm', 'transaksi-saved'])
@ -218,52 +209,31 @@ const showToast = ref(false)
const toastType = ref('error') const toastType = ref('error')
const toastMessage = ref('') const toastMessage = ref('')
// 🧾 kode transaksi tetap
const transactionCode = ref('')
const toastClasses = computed(() => { const toastClasses = computed(() => {
const baseClasses = 'text-white' const base = 'text-white'
const typeClasses = { const type = {
error: 'bg-red-500', error: 'bg-red-500',
success: 'bg-green-500', success: 'bg-green-500',
info: 'bg-blue-500' info: 'bg-blue-500'
} }
return `${baseClasses} ${typeClasses[toastType.value]}` return `${base} ${type[toastType.value]}`
}) })
const grandTotal = computed(() => { const grandTotal = computed(() => props.total + (ongkosBikin.value || 0))
return props.total + (ongkosBikin.value || 0)
})
const getRowStyle = () => { const getRowStyle = () => props.pesanan.length === 1 ? { height: '126px' } : { height: '63px' }
if (props.pesanan.length === 1) { const getImageClass = () => props.pesanan.length === 1 ? 'w-25 h-25' : 'w-12 h-12'
return { height: '126px' } const getTextClass = () => props.pesanan.length === 1 ? 'text-lg font-medium' : 'text-sm'
}
return { height: '63px' }
}
const getImageClass = () => {
if (props.pesanan.length === 1) {
return 'w-25 h-25'
}
return 'w-12 h-12'
}
const getTextClass = () => {
if (props.pesanan.length === 1) {
return 'text-lg font-medium'
}
return 'text-sm'
}
const getCurrentDate = () => { const getCurrentDate = () => {
const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'] const days = ['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu']
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
const now = new Date() const now = new Date()
const dayName = days[now.getDay()]
const day = String(now.getDate()).padStart(2, '0') const day = String(now.getDate()).padStart(2, '0')
const month = months[now.getMonth()] const month = String(now.getMonth() + 1).padStart(2, '0')
const year = now.getFullYear() return `${days[now.getDay()]}, ${day}-${month}-${now.getFullYear()}`
return `${dayName}, ${day}-${month}-${year}`
} }
const generateTransactionCode = () => { const generateTransactionCode = () => {
@ -276,55 +246,33 @@ const showSimpleToast = (type, message, duration = 3000) => {
toastType.value = type toastType.value = type
toastMessage.value = message toastMessage.value = message
showToast.value = true showToast.value = true
setTimeout(() => (showToast.value = false), duration)
setTimeout(() => {
showToast.value = false
}, duration)
} }
const fetchSales = async () => { const fetchSales = async () => {
try { try {
const response = await axios.get('/api/sales', { const res = await axios.get('/api/sales', {
headers: { headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
}) })
salesOptions.value = res.data.map(s => ({ value: s.id, label: s.nama }))
salesOptions.value = response.data.map(sales => ({ if (salesOptions.value.length > 0) selectedSales.value = salesOptions.value[0].value
value: sales.id, } catch (e) {
label: sales.nama console.error('Error fetching sales:', e)
}))
if (salesOptions.value.length > 0) {
selectedSales.value = salesOptions.value[0].value
}
} catch (error) {
console.error('Error fetching sales:', error)
} }
} }
// 🟢 Generate kode hanya saat menyimpan transaksi
const handleSimpan = () => { const handleSimpan = () => {
if (!namaPembeli.value.trim()) { if (!namaPembeli.value.trim()) return showSimpleToast('error', 'Nama pembeli harus diisi!')
showSimpleToast('error', 'Nama pembeli harus diisi!') if (!nomorTelepon.value.trim()) return showSimpleToast('error', 'Nomor telepon harus diisi!')
return if (!alamat.value.trim()) return showSimpleToast('error', 'Alamat harus diisi!')
} if (!selectedSales.value) return showSimpleToast('error', 'Sales harus dipilih!')
if (!nomorTelepon.value.trim()) { // Kode transaksi dibuat hanya saat simpan
showSimpleToast('error', 'Nomor telepon harus diisi!') transactionCode.value = generateTransactionCode()
return
}
if (!alamat.value.trim()) {
showSimpleToast('error', 'Alamat harus diisi!')
return
}
if (!selectedSales.value) {
showSimpleToast('error', 'Sales harus dipilih!')
return
}
simpanTransaksi({ simpanTransaksi({
kode_transaksi: transactionCode.value,
id_sales: selectedSales.value, id_sales: selectedSales.value,
nama_pembeli: namaPembeli.value, nama_pembeli: namaPembeli.value,
no_hp: nomorTelepon.value, no_hp: nomorTelepon.value,
@ -337,48 +285,37 @@ const handleSimpan = () => {
const simpanTransaksi = async (dataTransaksi) => { const simpanTransaksi = async (dataTransaksi) => {
if (isSaving.value) return if (isSaving.value) return
isSaving.value = true isSaving.value = true
try { try {
const response = await axios.post('/api/transaksi', dataTransaksi, { const res = await axios.post('/api/transaksi', dataTransaksi, {
headers: { headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
Authorization: `Bearer ${localStorage.getItem("token")}`, })
},
});
showSimpleToast('success', 'Transaksi berhasil disimpan!', 2000) showSimpleToast('success', 'Transaksi berhasil disimpan!', 2000)
// Emit event dengan data transaksi yang sudah disimpan
setTimeout(() => { setTimeout(() => {
emit('transaksi-saved', response.data); emit('transaksi-saved', res.data)
emit('close'); emit('close')
}, 2200); }, 2200)
} catch (err) {
} catch (error) { const msg = err.response?.data?.message || err.message || 'Terjadi kesalahan saat menyimpan transaksi'
console.error('Error saving transaksi:', error); showSimpleToast('error', `Error: ${msg}`, 4000)
const errorMessage = error.response?.data?.message || error.message || 'Terjadi kesalahan saat menyimpan transaksi';
showSimpleToast('error', `Error: ${errorMessage}`, 4000);
} finally { } finally {
isSaving.value = false isSaving.value = false
} }
}; }
onMounted(() => { onMounted(() => {
if (props.isOpen) { if (props.isOpen) fetchSales()
fetchSales()
}
}) })
function formatInput(e) { function formatInput(e) {
let value = e.target.value.replace(/\D/g, ""); let val = e.target.value.replace(/\D/g, "")
ongkosBikin.value = value ? parseInt(value, 10) : null; ongkosBikin.value = val ? parseInt(val, 10) : null
ongkosBikinFormatted.value = value ongkosBikinFormatted.value = val ? new Intl.NumberFormat("id-ID").format(val) : ""
? new Intl.NumberFormat("id-ID").format(value)
: "";
} }
</script> </script>
<style scoped> <style scoped>
@import url('https://fonts.googleapis.com/css2?family=PT+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=PT+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap');

View File

@ -73,10 +73,10 @@
<span v-if="item.harga_deal && item.harga_deal > 0">1</span> <span v-if="item.harga_deal && item.harga_deal > 0">1</span>
</td> </td>
<td class="flex items-center gap-2 p-2 border-r border-D" :style="getRowStyle()"> <td class="flex items-center gap-2 p-2 border-r border-D" :style="getRowStyle()">
<img <img
:src="item.produk?.foto?.[0]?.url || 'https://via.placeholder.com/50x50?text=No+Img'" :src="item.produk?.foto?.[0]?.url || 'https://via.placeholder.com/50x50?text=No+Img'"
:class="getImageClass()" :class="getImageClass()"
class="object-cover rounded" class="object-cover rounded"
/> />
@ -103,7 +103,7 @@
<div class="flex text-sm mt-2"> <div class="flex text-sm mt-2">
<div class="w-[40%] p-2 text-left"> <div class="w-[40%] p-2 text-left">
<p class="font-semibold">PERHATIAN</p> <p class="font-semibold">PERHATIAN</p>
<ol class="list-decimal ml-4 text-xs space-y-1"> <ol class="list-decimal ml-4 text-xs space-y-1">
@ -258,30 +258,40 @@ const formatNumber = (number) => {
@media print { @media print {
@page { @page {
size: A4; /* atau '80mm 200mm' kalau thermal */ size: A4;
margin: Minimum; margin: 0;
} }
html, body {
height: 100%;
overflow: hidden;
margin: 0;
padding: 0;
}
/* Sembunyikan semua elemen di luar print-area */ /* Sembunyikan semua elemen di luar print-area */
body * { body * {
visibility: hidden !important; visibility: hidden !important;
} }
.print-area * { .print-area * {
visibility: visible !important; visibility: visible !important;
-webkit-print-color-adjust: exact !important; -webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important; print-color-adjust: exact !important;
} }
.print-area { .print-area {
position: absolute; position: fixed !important;
top: 0; top: 0 !important;
left: 0; left: 0 !important;
width: 1224px; width: 1224px;
height: 528px; height: 528px;
margin: 0; margin: 0;
padding: 0; padding: 0;
transform: scale(0.6673); transform: scale(0.6673);
transform-origin: top left; transform-origin: top left;
page-break-after: avoid !important;
page-break-inside: avoid !important;
} }
/* Hilangkan tombol tutup & print */ /* Hilangkan tombol tutup & print */
@ -290,4 +300,3 @@ const formatNumber = (number) => {
} }
} }
</style> </style>

View File

@ -3,7 +3,7 @@
<div class="p-6"> <div class="p-6">
<p class="font-serif italic text-[25px] text-D">BRANKAS</p> <p class="font-serif italic text-[25px] text-D">BRANKAS</p>
<div class="flex justify-end"> <div class="flex justify-end">
<div class="w-full sm:w-64 my-3"> <div class="w-full md:w-64 my-3 mb-9">
<searchbar v-model:search="searchQuery"/> <searchbar v-model:search="searchQuery"/>
</div> </div>
</div> </div>

View File

@ -563,8 +563,22 @@ const closeItemModal = () => {
editedProduct.value = null; editedProduct.value = null;
}; };
const back = () => { const back = async () => {
router.push("/produk"); loading.value = true;
try{
console.log(localStorage.getItem("token"));
await axios.delete('/api/all/foto', {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});
router.push('/produk');
} catch (e){
console.error("Error image ", e);
} finally {
loading.value = false;
}
}; };
onMounted(async () => { onMounted(async () => {

View File

@ -468,8 +468,23 @@ const submitForm = async (addItem) => {
} }
}; };
const back = () => {
router.push('/produk'); const back = async () => {
loading.value = true;
try{
console.log(localStorage.getItem("token"));
await axios.delete('/api/all/foto', {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});
router.push('/produk');
} catch (e){
console.error("Error image ", e);
} finally {
loading.value = false;
}
}; };
const openCreateItemModal = (product) => { const openCreateItemModal = (product) => {

View File

@ -1,9 +1,9 @@
<template> <template>
<mainLayout> <mainLayout>
<div class="p-6 flex flex-col sm:flex-row justify-between items-start gap-3"> <div class="p-6">
<p class="font-serif italic text-[25px] text-D">NAMPAN</p> <p class="font-serif italic text-[25px] text-D">NAMPAN</p>
<div class="flex flex-col gap-3 justify-end w-full sm:w-auto"> <div class="flex flex-col gap-3 justify-end w-full sm:w-auto my-3">
<Searchbar v-model:search="searchQuery" /> <Searchbar v-model:search="searchQuery" />
<div class="flex w-full gap-2" v-if="isAdmin"> <div class="flex w-full gap-2" v-if="isAdmin">
<button @click="openModal" class="px-4 py-2 sm:px-2 sm:py-1 hover:bg-B bg-C rounded-md shadow w-full"> <button @click="openModal" class="px-4 py-2 sm:px-2 sm:py-1 hover:bg-B bg-C rounded-md shadow w-full">
@ -99,9 +99,9 @@ const saveTray = async () => {
} }
try { try {
const token = localStorage.getItem("token"); const token = localStorage.getItem("token");
const headers = { const headers = {
Accept: 'application/json', Accept: 'application/json',
Authorization: `Bearer ${token}` Authorization: `Bearer ${token}`
}; };
if (editingTrayId.value) { if (editingTrayId.value) {
await axios.put(`/api/nampan/${editingTrayId.value}`, { nama: trayName.value }, { headers }); await axios.put(`/api/nampan/${editingTrayId.value}`, { nama: trayName.value }, { headers });
@ -119,7 +119,7 @@ const saveTray = async () => {
const errors = error.response?.data?.errors?.nama || []; const errors = error.response?.data?.errors?.nama || [];
console.log(errors); console.log(errors);
errorCreate.value = errors[0] || 'Gagal menyimpan nampan.'; errorCreate.value = errors[0] || 'Gagal menyimpan nampan.';
clearTimeout(timer.value); clearTimeout(timer.value);
timer.value = setTimeout(() => { errorCreate.value = ""; }, 3000); timer.value = setTimeout(() => { errorCreate.value = ""; }, 3000);
} }

View File

@ -37,7 +37,7 @@ Route::prefix('api')->group(function () {
Route::post('foto', [FotoSementaraController::class, 'upload']); Route::post('foto', [FotoSementaraController::class, 'upload']);
Route::delete('foto/{id}', [FotoSementaraController::class, 'hapus']); Route::delete('foto/{id}', [FotoSementaraController::class, 'hapus']);
Route::get('produk/edit/{id}', [ProdukController::class, 'edit']); Route::get('produk/edit/{id}', [ProdukController::class, 'edit']);
Route::delete('foto/all', [FotoSementaraController::class, 'reset']); Route::delete('all/foto', [FotoSementaraController::class, 'reset']);
// Laporan // Laporan
Route::prefix('laporan')->group(function () { Route::prefix('laporan')->group(function () {

1485
storage/toko_emas.sql Normal file

File diff suppressed because one or more lines are too long