Merge branch 'main' into production
This commit is contained in:
commit
ddaefdc57d
@ -1,7 +1,7 @@
|
||||
APP_NAME=Abbauf-Kasir
|
||||
APP_ENV=production
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=false
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
APP_LOCALE=en
|
||||
|
||||
17
README.md
17
README.md
@ -144,14 +144,17 @@ docker exec -it abbauf_kasir_app php artisan migrate --seed
|
||||
# Siapkan penyimpanan file
|
||||
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)
|
||||
docker exec -it abbauf_kasir_db bash
|
||||
mysql -u kasir_user -pkasir_password
|
||||
mysql -u kasir_user -pkasir_password kasir_db
|
||||
```
|
||||
|
||||
### 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.
|
||||
- Sambungkan printer ke komputer via USB.
|
||||
- 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`
|
||||
- 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
|
||||
|
||||
@ -104,7 +104,7 @@ class TransaksiController extends Controller
|
||||
'ongkos_bikin' => 'nullable|numeric|min:0',
|
||||
'total_harga' => 'required|numeric',
|
||||
'items' => 'required|array',
|
||||
'items.*.kode_item' => 'required|exists:items,id|numeric',
|
||||
'items.*.kode_item' => 'required',
|
||||
'items.*.harga_deal' => 'required|numeric',
|
||||
]);
|
||||
|
||||
|
||||
@ -23,24 +23,15 @@ class Item extends Model
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($item) {
|
||||
$prefix = 'TMJC';
|
||||
$date = now()->format('Ymd');
|
||||
static::created(function ($item) {
|
||||
if (!$item->kode_item || $item->kode_item === 'belum pak') {
|
||||
$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
|
||||
$lastItem = self::whereDate('created_at', now()->toDateString())
|
||||
->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;
|
||||
$item->kode_item = $prefix . $date . $number;
|
||||
$item->save();
|
||||
}
|
||||
|
||||
// Format: ITM202509090001
|
||||
$item->kode_item = $prefix . $date . str_pad($number, 4, '0', STR_PAD_LEFT);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ return new class extends Migration
|
||||
public function up()
|
||||
{
|
||||
Schema::table('items', function (Blueprint $table) {
|
||||
$table->string('kode_item')->unique()->after('id');
|
||||
$table->string('kode_item')->unique()->default('belum pak')->after('id');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -37,17 +37,9 @@ class DatabaseSeeder extends Seeder
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
// Create sales record
|
||||
Sales::create([
|
||||
'nama' => 'Kasir',
|
||||
'no_hp' => '-',
|
||||
'alamat' => '-',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
// Call other seeders
|
||||
$this->call(DataSeeder::class);
|
||||
// $this->call(DummySeeder::class);
|
||||
$this->call(DummySeeder::class);
|
||||
}
|
||||
}
|
||||
@ -249,12 +249,14 @@ const tambahItem = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
item.value.kode_item = Number(kodeItem.value);
|
||||
item.value.kode_item = kodeItem.value;
|
||||
item.value.harga_deal = Number(hargaJual.value);
|
||||
item.value.posisi = item.value.nampan ? item.value.nampan.nama : "Brankas";
|
||||
|
||||
pesanan.value.push(item.value);
|
||||
|
||||
console.log("Pesanan +:", item.value);
|
||||
|
||||
kodeItem.value = "";
|
||||
hargaJual.value = null;
|
||||
hargaJualFormatted.value = "";
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
<td class="border border-gray-200 p-2 text-center">
|
||||
<button
|
||||
@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"
|
||||
>
|
||||
<span v-if="isDetailLoading && selectedTransaksi.id === trx.id">
|
||||
|
||||
@ -37,7 +37,9 @@ const printBarcode = () => {
|
||||
const printWindow = window.open('', '_blank');
|
||||
const kode = props.code || '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(`
|
||||
<html>
|
||||
@ -70,29 +72,47 @@ const printBarcode = () => {
|
||||
height: 38mm;
|
||||
}
|
||||
|
||||
.barcode-container {
|
||||
width: 12mm;
|
||||
height: 38mm;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.barcode-img {
|
||||
transform: rotate(90deg);
|
||||
transform-origin: center;
|
||||
max-height: 12mm;
|
||||
max-width: 12mm;
|
||||
}
|
||||
|
||||
.details-container {
|
||||
width: 12mm;
|
||||
.left-side {
|
||||
width: 12.5mm;
|
||||
height: 38mm;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.left-content {
|
||||
position: absolute;
|
||||
width: fit-content;
|
||||
height: 12.5mm;
|
||||
transform: rotate(90deg);
|
||||
transform-origin: center;
|
||||
display: flex;
|
||||
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;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
@ -101,25 +121,30 @@ const printBarcode = () => {
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
position: absolute;
|
||||
width: 32mm;
|
||||
width: 36mm;
|
||||
text-align: center;
|
||||
transform: rotate(270deg);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform-origin: center;
|
||||
translate: -50% -50%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="label">
|
||||
<div class="barcode-container">
|
||||
<img id="barcode-img" class="barcode-img"
|
||||
src="${barcodeUrl.value}" alt="Barcode" />
|
||||
<!-- Sisi Kiri: Barcode + Info -->
|
||||
<div class="left-side">
|
||||
<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 class="details-container">
|
||||
<div class="item-name">${nama} ${berat}</div>
|
||||
<div class="right-side">
|
||||
<div class="item-name">${nama}</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<p class="flex items-center gap-2">
|
||||
<i class="fab fa-whatsapp text-green-500 text-xl"></i> 08158851178
|
||||
</p>
|
||||
<p class=" text-sm">{{ generateTransactionCode() }}</p>
|
||||
<p class="text-sm">TRSXXXXXXXXXXXX</p>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<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" />
|
||||
</div>
|
||||
|
||||
@ -189,18 +189,9 @@ import inputSelect from '@/components/InputSelect.vue'
|
||||
import axios from 'axios'
|
||||
|
||||
const props = defineProps({
|
||||
isOpen: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
pesanan: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
isOpen: Boolean,
|
||||
pesanan: Array,
|
||||
total: Number
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close', 'confirm', 'transaksi-saved'])
|
||||
@ -218,52 +209,31 @@ const showToast = ref(false)
|
||||
const toastType = ref('error')
|
||||
const toastMessage = ref('')
|
||||
|
||||
// 🧾 kode transaksi tetap
|
||||
const transactionCode = ref('')
|
||||
|
||||
const toastClasses = computed(() => {
|
||||
const baseClasses = 'text-white'
|
||||
const typeClasses = {
|
||||
const base = 'text-white'
|
||||
const type = {
|
||||
error: 'bg-red-500',
|
||||
success: 'bg-green-500',
|
||||
info: 'bg-blue-500'
|
||||
}
|
||||
return `${baseClasses} ${typeClasses[toastType.value]}`
|
||||
return `${base} ${type[toastType.value]}`
|
||||
})
|
||||
|
||||
const grandTotal = computed(() => {
|
||||
return props.total + (ongkosBikin.value || 0)
|
||||
})
|
||||
const grandTotal = computed(() => props.total + (ongkosBikin.value || 0))
|
||||
|
||||
const getRowStyle = () => {
|
||||
if (props.pesanan.length === 1) {
|
||||
return { height: '126px' }
|
||||
}
|
||||
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 getRowStyle = () => props.pesanan.length === 1 ? { height: '126px' } : { height: '63px' }
|
||||
const getImageClass = () => props.pesanan.length === 1 ? 'w-25 h-25' : 'w-12 h-12'
|
||||
const getTextClass = () => props.pesanan.length === 1 ? 'text-lg font-medium' : 'text-sm'
|
||||
|
||||
const getCurrentDate = () => {
|
||||
const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu']
|
||||
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
|
||||
|
||||
const days = ['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu']
|
||||
const now = new Date()
|
||||
const dayName = days[now.getDay()]
|
||||
const day = String(now.getDate()).padStart(2, '0')
|
||||
const month = months[now.getMonth()]
|
||||
const year = now.getFullYear()
|
||||
|
||||
return `${dayName}, ${day}-${month}-${year}`
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0')
|
||||
return `${days[now.getDay()]}, ${day}-${month}-${now.getFullYear()}`
|
||||
}
|
||||
|
||||
const generateTransactionCode = () => {
|
||||
@ -276,55 +246,33 @@ const showSimpleToast = (type, message, duration = 3000) => {
|
||||
toastType.value = type
|
||||
toastMessage.value = message
|
||||
showToast.value = true
|
||||
|
||||
setTimeout(() => {
|
||||
showToast.value = false
|
||||
}, duration)
|
||||
setTimeout(() => (showToast.value = false), duration)
|
||||
}
|
||||
|
||||
const fetchSales = async () => {
|
||||
try {
|
||||
const response = await axios.get('/api/sales', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
const res = await axios.get('/api/sales', {
|
||||
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }
|
||||
})
|
||||
|
||||
salesOptions.value = response.data.map(sales => ({
|
||||
value: sales.id,
|
||||
label: sales.nama
|
||||
}))
|
||||
|
||||
if (salesOptions.value.length > 0) {
|
||||
selectedSales.value = salesOptions.value[0].value
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching sales:', error)
|
||||
salesOptions.value = res.data.map(s => ({ value: s.id, label: s.nama }))
|
||||
if (salesOptions.value.length > 0) selectedSales.value = salesOptions.value[0].value
|
||||
} catch (e) {
|
||||
console.error('Error fetching sales:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 🟢 Generate kode hanya saat menyimpan transaksi
|
||||
const handleSimpan = () => {
|
||||
if (!namaPembeli.value.trim()) {
|
||||
showSimpleToast('error', 'Nama pembeli harus diisi!')
|
||||
return
|
||||
}
|
||||
if (!namaPembeli.value.trim()) return showSimpleToast('error', 'Nama pembeli harus diisi!')
|
||||
if (!nomorTelepon.value.trim()) return showSimpleToast('error', 'Nomor telepon harus diisi!')
|
||||
if (!alamat.value.trim()) return showSimpleToast('error', 'Alamat harus diisi!')
|
||||
if (!selectedSales.value) return showSimpleToast('error', 'Sales harus dipilih!')
|
||||
|
||||
if (!nomorTelepon.value.trim()) {
|
||||
showSimpleToast('error', 'Nomor telepon harus diisi!')
|
||||
return
|
||||
}
|
||||
|
||||
if (!alamat.value.trim()) {
|
||||
showSimpleToast('error', 'Alamat harus diisi!')
|
||||
return
|
||||
}
|
||||
|
||||
if (!selectedSales.value) {
|
||||
showSimpleToast('error', 'Sales harus dipilih!')
|
||||
return
|
||||
}
|
||||
// Kode transaksi dibuat hanya saat simpan
|
||||
transactionCode.value = generateTransactionCode()
|
||||
|
||||
simpanTransaksi({
|
||||
kode_transaksi: transactionCode.value,
|
||||
id_sales: selectedSales.value,
|
||||
nama_pembeli: namaPembeli.value,
|
||||
no_hp: nomorTelepon.value,
|
||||
@ -337,48 +285,37 @@ const handleSimpan = () => {
|
||||
|
||||
const simpanTransaksi = async (dataTransaksi) => {
|
||||
if (isSaving.value) return
|
||||
|
||||
isSaving.value = true
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/transaksi', dataTransaksi, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
},
|
||||
});
|
||||
|
||||
const res = await axios.post('/api/transaksi', dataTransaksi, {
|
||||
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
||||
})
|
||||
showSimpleToast('success', 'Transaksi berhasil disimpan!', 2000)
|
||||
|
||||
// Emit event dengan data transaksi yang sudah disimpan
|
||||
setTimeout(() => {
|
||||
emit('transaksi-saved', response.data);
|
||||
emit('close');
|
||||
}, 2200);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error saving transaksi:', error);
|
||||
const errorMessage = error.response?.data?.message || error.message || 'Terjadi kesalahan saat menyimpan transaksi';
|
||||
showSimpleToast('error', `Error: ${errorMessage}`, 4000);
|
||||
emit('transaksi-saved', res.data)
|
||||
emit('close')
|
||||
}, 2200)
|
||||
} catch (err) {
|
||||
const msg = err.response?.data?.message || err.message || 'Terjadi kesalahan saat menyimpan transaksi'
|
||||
showSimpleToast('error', `Error: ${msg}`, 4000)
|
||||
} finally {
|
||||
isSaving.value = false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.isOpen) {
|
||||
fetchSales()
|
||||
}
|
||||
if (props.isOpen) fetchSales()
|
||||
})
|
||||
|
||||
function formatInput(e) {
|
||||
let value = e.target.value.replace(/\D/g, "");
|
||||
ongkosBikin.value = value ? parseInt(value, 10) : null;
|
||||
ongkosBikinFormatted.value = value
|
||||
? new Intl.NumberFormat("id-ID").format(value)
|
||||
: "";
|
||||
let val = e.target.value.replace(/\D/g, "")
|
||||
ongkosBikin.value = val ? parseInt(val, 10) : null
|
||||
ongkosBikinFormatted.value = val ? new Intl.NumberFormat("id-ID").format(val) : ""
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
@import url('https://fonts.googleapis.com/css2?family=PT+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap');
|
||||
|
||||
|
||||
@ -258,30 +258,40 @@ const formatNumber = (number) => {
|
||||
|
||||
@media print {
|
||||
@page {
|
||||
size: A4; /* atau '80mm 200mm' kalau thermal */
|
||||
margin: Minimum;
|
||||
size: A4;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Sembunyikan semua elemen di luar print-area */
|
||||
body * {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
.print-area * {
|
||||
visibility: visible !important;
|
||||
-webkit-print-color-adjust: exact !important;
|
||||
print-color-adjust: exact !important;
|
||||
|
||||
}
|
||||
|
||||
.print-area {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 1224px;
|
||||
height: 528px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
transform: scale(0.6673);
|
||||
transform-origin: top left;
|
||||
page-break-after: avoid !important;
|
||||
page-break-inside: avoid !important;
|
||||
}
|
||||
|
||||
/* Hilangkan tombol tutup & print */
|
||||
@ -290,4 +300,3 @@ const formatNumber = (number) => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<div class="p-6">
|
||||
<p class="font-serif italic text-[25px] text-D">BRANKAS</p>
|
||||
<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"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -563,8 +563,22 @@ const closeItemModal = () => {
|
||||
editedProduct.value = null;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<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>
|
||||
|
||||
<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" />
|
||||
<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">
|
||||
|
||||
@ -37,7 +37,7 @@ Route::prefix('api')->group(function () {
|
||||
Route::post('foto', [FotoSementaraController::class, 'upload']);
|
||||
Route::delete('foto/{id}', [FotoSementaraController::class, 'hapus']);
|
||||
Route::get('produk/edit/{id}', [ProdukController::class, 'edit']);
|
||||
Route::delete('foto/all', [FotoSementaraController::class, 'reset']);
|
||||
Route::delete('all/foto', [FotoSementaraController::class, 'reset']);
|
||||
|
||||
// Laporan
|
||||
Route::prefix('laporan')->group(function () {
|
||||
|
||||
1485
storage/toko_emas.sql
Normal file
1485
storage/toko_emas.sql
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user