parfum/resources/views/livewire/studio/manage/order/index.blade.php
Yoga Pangestu 44697030f8 feat(order): membuat tombol print di datatable
- membuat fitur print
- after create bisa pangsung print
2025-11-17 21:49:58 +07:00

237 lines
7.7 KiB
PHP

<flux:main>
<div class="flex justify-between items-center">
<div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div>
@can('create order')
<div class="flex">
<flux:button href="{{ route('studio.manage.order.create') }}" variant="primary" wire:navigate.hover
class="text-sm">
Tambah
</flux:button>
<flux:button variant="primary" color="cyan" class="ms-2" wire:click="$dispatch('connectUSB')">
Connect USB
</flux:button>
</div>
@endcan
</div>
<div class="mt-6">
<livewire:datatable.studio.manage.orders-table />
</div>
@include('components.confirmation.delete')
</flux:main>
@script
<script>
document.addEventListener('livewire:navigated', () => {
window.printerDevice = null;
Livewire.on('connectUSB', async (event) => {
try {
const device = await navigator.usb.requestDevice({
filters: [{}]
});
await device.open();
if (device.configuration === null) {
await device.selectConfiguration(1);
}
await device.claimInterface(0);
window.printerDevice = device;
const deviceInfo = {
vendorId: device.vendorId,
productId: device.productId,
productName: device.productName,
manufacturerName: device.manufacturerName,
serialNumber: device.serialNumber
};
localStorage.setItem('printerDevice', JSON.stringify(deviceInfo));
console.log('Connected to:', device.productName);
console.log('Saved to localStorage:', deviceInfo);
} catch (err) {
console.error('Gagal connect:', err);
}
});
async function autoReconnect() {
const saved = localStorage.getItem('printerDevice');
if (!saved) return;
const {
vendorId,
productId
} = JSON.parse(saved);
const devices = await navigator.usb.getDevices();
const target = devices.find(d => d.vendorId === vendorId && d.productId === productId);
if (!target) return;
try {
await target.open();
if (target.configuration === null) {
await target.selectConfiguration(1);
}
await target.claimInterface(0);
window.printerDevice = target;
console.log('Auto reconnected to:', target.productName);
} catch (err) {
console.error('Gagal autoreconnect:', err);
}
}
autoReconnect();
navigator.usb.addEventListener('disconnect', event => {
const device = event.device;
const saved = localStorage.getItem('printerDevice');
if (!saved) return;
const {
vendorId,
productId
} = JSON.parse(saved);
if (device.vendorId === vendorId && device.productId === productId) {
localStorage.removeItem('printerDevice');
window.printerDevice = null;
console.log('USB dicabut, localStorage dibersihkan.');
}
});
function getMaxChar(widthMM) {
if (widthMM <= 48) return 32;
if (widthMM <= 58) return 42;
return 48;
}
Livewire.on('printReceipt', async (event) => {
try {
const data = {
cashier: event.cashier,
customer: event.customer,
items: event.items,
subtotal: event.subtotal,
discount: event.discount,
total: event.total,
};
await printReceipt(data);
} catch (e) {
console.error("Print error:", e);
}
});
async function printReceipt(order) {
if (!window.printerDevice) {
console.error("Printer belum terkoneksi.");
return;
}
const encoder = new TextEncoder();
const device = window.printerDevice;
// ESC/POS Commands
const reset = new Uint8Array([0x1B, 0x40]);
const alignLeft = new Uint8Array([0x1B, 0x61, 0]);
const alignCenter = new Uint8Array([0x1B, 0x61, 1]);
const alignRight = new Uint8Array([0x1B, 0x61, 2]);
const cut = new Uint8Array([0x1D, 0x56, 0x00]);
const MAX = getMaxChar(48);
const line = "-".repeat(MAX) + "\n";
const formatItem = (name, qty, price, total, isLast) => {
let out = name.substring(0, 32) + "\n";
const left = `${qty} x ${price.toLocaleString()}`;
const right = total.toLocaleString();
const space = 32 - left.length - right.length;
out += left + " ".repeat(space > 0 ? space : 1) + right;
// Kalau terakhir, pakai satu newline.
// Kalau bukan, dua newline seperti biasa.
out += isLast ? "\n" : "\n\n";
return out;
};
// =========================================
// BUILD TEXT
// =========================================
let text = "";
// HEADER
// center and increase font size
text += "\x1B\x61\x01";
text += "\x1B\x21\x30";
text += "Yadi Parfum\n\n";
// reset to normal and left align
text += "\x1B\x21\x00";
text += "\x1B\x61\x00";
text += "Kasir : " + order.cashier + "\n";
text += "Customer : " + order.customer + "\n";
text += line;
// CONTENT
order.items.forEach((i, idx) => {
const isLast = idx === order.items.length - 1;
text += formatItem(i.name, i.quantity, i.unit_price, i.total, isLast);
});
text += line;
// FOOTER
text += "\x1B\x61\x02";
text += `${order.subtotal.toLocaleString()}\n`;
text += `-${order.discount.toLocaleString()}\n`;
// bold teks
text += "\x1B\x45\x01";
text += `${order.total.toLocaleString()}\n`;
// reset bold
text += "\x1B\x45\x00";
text += line;
// THANKS
text += "\x1B\x61\x01";
text += "Terima kasih telah berbelanja dan memilih Yadi Parfum :)\n";
const textBytes = encoder.encode(text);
// SEND
await device.transferOut(1, reset);
await device.transferOut(1, alignLeft);
await device.transferOut(1, textBytes);
await device.transferOut(1, cut);
}
});
</script>
@endscript