ok
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import dayjs from "dayjs";
|
||||
import Chart from "react-apexcharts";
|
||||
import styles from "./BarChart.module.css"; // Import the CSS module
|
||||
|
||||
@@ -85,7 +86,7 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
|
||||
}
|
||||
let totalValue = seriesData.reduce((acc, val) => acc + val, 0);
|
||||
return {
|
||||
date: new Date(dayData.date).toLocaleDateString(),
|
||||
date: dayData.date,
|
||||
categories,
|
||||
series: [
|
||||
{
|
||||
@@ -113,15 +114,11 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
|
||||
}
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
const date = new Date(dateString);
|
||||
const monthNames = [
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
];
|
||||
const month = monthNames[date.getMonth()];
|
||||
const day = date.getDate();
|
||||
return { month, day };
|
||||
const d = dayjs(dateString, ["YYYY-MM-DD", "YYYY-MM-DDTHH:mm:ssZ"]);
|
||||
return { month: d.format("MMM"), day: d.format("D") };
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className={`${styles.chartItemContainer} ${selectedIndex !== -1 ? styles.expanded : ''}`}>
|
||||
|
||||
@@ -142,27 +139,37 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
|
||||
key={indexx}
|
||||
className={`${styles.dateSelector} ${index === indexx ? styles.dateSelectorActive : styles.dateSelectorInactive
|
||||
}`}
|
||||
style={{ position: 'relative' }}
|
||||
style={{ position: 'relative', width: 'calc(100% / 7)' }}
|
||||
onClick={() =>
|
||||
type == 'yesterday' && selectedIndex == -1 || type != 'yesterday' && selectedIndex !== index ? setSelectedIndex(index) : setSelectedIndex(-1)
|
||||
}
|
||||
>
|
||||
|
||||
<div style={{ position: 'absolute', bottom: '28px', left: '10%', right: '10%', borderBottom: index == indexx ? `2px solid ${colors[index % colors.length]}` : 'none' }}></div>
|
||||
<div style={{ position: 'absolute', bottom: '21px', left: '10%', right: '10%', borderBottom: index == indexx ? `2px solid ${colors[index % colors.length]}` : 'none' }}></div>
|
||||
<div
|
||||
style={{ color: index === indexx ? 'black' : 'transparent' }}>
|
||||
{indexx !== chartData.length - 1 ? (
|
||||
<>
|
||||
{day}{" "}
|
||||
{(indexx === 0 || (formatDate(chartData[indexx - 1].date).month !== month && type != 'weekly')) && month}
|
||||
</>
|
||||
<p style={{ fontSize: '13px' }}>{day}{" "}
|
||||
{(
|
||||
indexx === 0 ||
|
||||
(indexx > 0 &&
|
||||
dayjs(chartData[indexx - 1].date).month() !== dayjs(item.date).month() &&
|
||||
type !== "weekly")
|
||||
) && month}
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
{type != 'weekly' ? 'Hari ini' : day}
|
||||
</>
|
||||
<p style={{ fontSize: '13px' }}>
|
||||
{type != 'weekly' ? 'Hari ini' : day + ' ' + month}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{index == indexx && <p style={{ margin: '7px 0 0 0', fontSize: '12px', color: 'black' }}>
|
||||
{index == indexx && <p style={{
|
||||
margin: '7px 0 0 0', fontSize: '9px', color: 'black',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
left: 0,
|
||||
bottom: 6
|
||||
}}>
|
||||
{graphFilter === 'transactions'
|
||||
? chartData[indexx].totalValue
|
||||
: formatRupiah(chartData[indexx].totalValue)}
|
||||
|
||||
@@ -252,10 +252,9 @@ export const handlePaymentFromClerk = async (
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
// Handle success response
|
||||
console.log("Transaction successful!");
|
||||
// Optionally return response data or handle further actions upon success
|
||||
return true;
|
||||
const data = await response.json();
|
||||
console.log("Transaction successful!", data);
|
||||
return data;
|
||||
} else {
|
||||
// Handle error response
|
||||
console.error("Transaction failed:", response.statusText);
|
||||
|
||||
@@ -103,6 +103,17 @@ function CafePage({
|
||||
// };
|
||||
const [isTablet, setIsTablet] = useState(window.innerWidth >= 768);
|
||||
|
||||
const [isFullscreen, setIsFullscreen] = useState(!!document.fullscreenElement);
|
||||
|
||||
useEffect(() => {
|
||||
function fullscreenChangeHandler() {
|
||||
setIsFullscreen(!!document.fullscreenElement);
|
||||
}
|
||||
|
||||
document.addEventListener("fullscreenchange", fullscreenChangeHandler);
|
||||
return () => document.removeEventListener("fullscreenchange", fullscreenChangeHandler);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setIsTablet(window.innerWidth >= 768);
|
||||
@@ -259,6 +270,70 @@ function CafePage({
|
||||
}
|
||||
}
|
||||
};
|
||||
const FullscreenButton = ({ onClick }) => {
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: "50%",
|
||||
backgroundColor: "#7272729e",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
userSelect: "none",
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 1000
|
||||
}}
|
||||
title="Toggle Fullscreen"
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
display: "inline-block",
|
||||
transform: "rotate(45deg)",
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
fontSize: 24,
|
||||
lineHeight: 1,
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
<>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const handleFullscreen = () => {
|
||||
const elem = document.documentElement; // fullscreen seluruh halaman
|
||||
|
||||
if (!document.fullscreenElement) {
|
||||
// masuk fullscreen
|
||||
if (elem.requestFullscreen) {
|
||||
elem.requestFullscreen();
|
||||
} else if (elem.mozRequestFullScreen) { /* Firefox */
|
||||
elem.mozRequestFullScreen();
|
||||
} else if (elem.webkitRequestFullscreen) { /* Chrome, Safari & Opera */
|
||||
elem.webkitRequestFullscreen();
|
||||
} else if (elem.msRequestFullscreen) { /* IE/Edge */
|
||||
elem.msRequestFullscreen();
|
||||
}
|
||||
} else {
|
||||
// keluar fullscreen
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) { /* Firefox */
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitExitFullscreen) { /* Chrome, Safari and Opera */
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.msExitFullscreen) { /* IE/Edge */
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (loading)
|
||||
return (
|
||||
@@ -297,6 +372,7 @@ function CafePage({
|
||||
)}
|
||||
<div style={{ width: isTablet ? "60%" : "100%" }}>
|
||||
<div className="App-header">
|
||||
{isTablet && !isFullscreen && <FullscreenButton onClick={handleFullscreen} />}
|
||||
<Header
|
||||
HeaderText={"Menu"}
|
||||
showProfile={true}
|
||||
|
||||
@@ -70,7 +70,7 @@ export default function Invoice({
|
||||
if (lastTransaction?.payment_type == "paylater")
|
||||
methods.isOpenBillAvailable = false;
|
||||
setPaymentMethods(methods);
|
||||
} catch (err) {}
|
||||
} catch (err) { }
|
||||
};
|
||||
|
||||
if (shopId) {
|
||||
@@ -275,6 +275,75 @@ export default function Invoice({
|
||||
);
|
||||
}
|
||||
};
|
||||
const handlePrint = (transaction) => {
|
||||
console.log(transaction)
|
||||
const formatWaktu = (() => {
|
||||
const date = transaction?.createdAt
|
||||
? new Date(transaction.createdAt)
|
||||
: new Date(); // UTC now
|
||||
const tanggal = date.toLocaleDateString("id-ID");
|
||||
const jam = date.toLocaleTimeString("id-ID", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
});
|
||||
return `${tanggal} ${jam}`;
|
||||
})();
|
||||
|
||||
|
||||
const itemsStr = transaction.DetailedTransactions.map((dt) => {
|
||||
const name =
|
||||
dt.Item.name.length > 11
|
||||
? dt.Item.name.slice(0, 11)
|
||||
: dt.Item.name.padEnd(11);
|
||||
const qty = dt.qty.toString().padStart(3);
|
||||
const total = formatRupiah(dt.qty * (dt.promoPrice || dt.price)).padStart(15);
|
||||
return `${name} ${qty} ${total}`;
|
||||
}).join("\n");
|
||||
|
||||
const totalHarga = transaction.DetailedTransactions.reduce((acc, dt) => {
|
||||
return acc + dt.qty * (dt.promoPrice || dt.price);
|
||||
}, 0);
|
||||
|
||||
const totalStr = `Total: ${formatRupiah(totalHarga)}`;
|
||||
|
||||
const receiptText = ` CAFE HOREE
|
||||
Jl. Ahmad Yani No. 12, Kediri
|
||||
Telp: 0812-1617-6963
|
||||
|
||||
==============================
|
||||
Tanggal : ${formatWaktu}
|
||||
Bayar : ${transaction.payment_type}
|
||||
------------------------------
|
||||
Item Qty Total
|
||||
------------------------------
|
||||
${itemsStr}
|
||||
${totalStr}
|
||||
==============================
|
||||
Terima kasih atas kunjungannya!
|
||||
~~
|
||||
supported by kedaimaster.com
|
||||
|
||||
|
||||
\n\n\n\n\n`;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append("content", receiptText);
|
||||
params.append("encode_format", "UTF-8");
|
||||
|
||||
const printUrl = `btprinter://print?${params.toString()}`;
|
||||
|
||||
window.location.href = printUrl;
|
||||
};
|
||||
|
||||
const formatRupiah = (value) => {
|
||||
if (typeof value !== "number") return value;
|
||||
return value.toLocaleString("id-ID", {
|
||||
style: "currency",
|
||||
currency: "IDR",
|
||||
minimumFractionDigits: 0,
|
||||
});
|
||||
};
|
||||
|
||||
const handlePay = async (orderMethod) => {
|
||||
setIsPaymentLoading(true);
|
||||
@@ -300,9 +369,16 @@ export default function Invoice({
|
||||
tableNumber,
|
||||
textareaRef.current.value
|
||||
);
|
||||
if (pay) window.location.reload();
|
||||
|
||||
} else if (deviceType == "guestSide") {
|
||||
if (pay) {
|
||||
handlePrint(pay.transaction);
|
||||
localStorage.removeItem("cart");
|
||||
localStorage.removeItem("lastTransaction");
|
||||
setCartItems([]);
|
||||
setTotalPrice(0);
|
||||
window.dispatchEvent(new Event("localStorageUpdated"));
|
||||
}
|
||||
}
|
||||
else if (deviceType == "guestSide") {
|
||||
const pay = await handlePaymentFromGuestSide(
|
||||
shopId,
|
||||
email,
|
||||
@@ -437,7 +513,7 @@ export default function Invoice({
|
||||
>
|
||||
<path d="M48,256c0,114.87,93.13,208,208,208s208-93.13,208-208S370.87,48,256,48,48,141.13,48,256Zm212.65-91.36a16,16,0,0,1,.09,22.63L208.42,240H342a16,16,0,0,1,0,32H208.42l52.32,52.73A16,16,0,1,1,238,347.27l-79.39-80a16,16,0,0,1,0-22.54l79.39-80A16,16,0,0,1,260.65,164.64Z" />
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
Keranjang
|
||||
</div>
|
||||
|
||||
@@ -452,7 +528,7 @@ export default function Invoice({
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ width: isTablet ? "30%":"50%" }}>
|
||||
<div style={{ width: isTablet ? "30%" : "50%" }}>
|
||||
<svg viewBox="0 0 32 32" style={{ fill: "#8F8787" }}>
|
||||
<path d="M9.79175 24.75C8.09591 24.75 6.72383 26.1375 6.72383 27.8333C6.72383 29.5292 8.09591 30.9167 9.79175 30.9167C11.4876 30.9167 12.8751 29.5292 12.8751 27.8333C12.8751 26.1375 11.4876 24.75 9.79175 24.75ZM0.541748 0.0833435V3.16668H3.62508L9.17508 14.8679L7.09383 18.645C6.84717 19.0767 6.70842 19.5854 6.70842 20.125C6.70842 21.8208 8.09591 23.2083 9.79175 23.2083H28.2917V20.125H10.4392C10.2234 20.125 10.0538 19.9554 10.0538 19.7396L10.1001 19.5546L11.4876 17.0417H22.973C24.1292 17.0417 25.1467 16.4096 25.6709 15.4538L31.1901 5.44834C31.3134 5.23251 31.3751 4.97043 31.3751 4.70834C31.3751 3.86043 30.6813 3.16668 29.8334 3.16668H7.03217L5.583 0.0833435H0.541748ZM25.2084 24.75C23.5126 24.75 22.1405 26.1375 22.1405 27.8333C22.1405 29.5292 23.5126 30.9167 25.2084 30.9167C26.9042 30.9167 28.2917 29.5292 28.2917 27.8333C28.2917 26.1375 26.9042 24.75 25.2084 24.75Z" />
|
||||
</svg>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import qs from 'qs';
|
||||
import '../print.css'; // Kamu bisa pakai styling temanmu atau buat file baru
|
||||
import '../print.css';
|
||||
|
||||
export default function PrintPage() {
|
||||
const location = useLocation();
|
||||
const [orientation, setOrientation] = useState('portrait');
|
||||
|
||||
// Parse data dari query string
|
||||
const data = useMemo(() => {
|
||||
try {
|
||||
const query = qs.parse(location.search, { ignoreQueryPrefix: true });
|
||||
@@ -19,8 +18,57 @@ export default function PrintPage() {
|
||||
|
||||
if (!data) return <div>Invalid data</div>;
|
||||
|
||||
const handlePrint = () => {
|
||||
window.print();
|
||||
const formatWaktu = (() => {
|
||||
const date = new Date(data.date);
|
||||
const tanggal = date.toLocaleDateString('id-ID');
|
||||
const jam = date.toLocaleTimeString('id-ID', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
});
|
||||
return `${tanggal} ${jam}`;
|
||||
})();
|
||||
|
||||
const itemsStr = data.items.map((item) => {
|
||||
const name = item.name.length > 11 ? item.name.slice(0, 11) : item.name.padEnd(11);
|
||||
const qty = item.qty.toString().padStart(2);
|
||||
const total = (item.qty * item.price).toString().padStart(6);
|
||||
return `${name} ${qty} ${total}`;
|
||||
}).join('\n');
|
||||
|
||||
const getReceiptText = () => {
|
||||
return (
|
||||
` CAFE HOREE
|
||||
Jl. Merdeka No. 123, Jakarta
|
||||
Telp: (021) 12345678
|
||||
|
||||
==============================
|
||||
Tanggal : ${formatWaktu}
|
||||
Kasir : ${data.cashier || 'UNKNOWN'}
|
||||
Bayar : ${data.payment_type}
|
||||
------------------------------
|
||||
Item Q Total
|
||||
------------------------------
|
||||
${itemsStr}
|
||||
------------------------------
|
||||
Terima kasih atas kunjungan Anda!
|
||||
~ Cafe Horee ~
|
||||
www.kedaimaster.com`
|
||||
);
|
||||
};
|
||||
|
||||
const handlePrintBluetooth = () => {
|
||||
const content = getReceiptText();
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append("content", content);
|
||||
params.append("encode_format", "UTF-8");
|
||||
|
||||
// Optional: jika ingin spesifik printer Bluetooth
|
||||
// params.append("device_address", "00:11:22:33:44:55");
|
||||
|
||||
const printUrl = `btprinter://print?${params.toString()}`;
|
||||
window.location.href = printUrl;
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -41,85 +89,14 @@ export default function PrintPage() {
|
||||
Landscape
|
||||
</button>
|
||||
</div>
|
||||
<button className="print-button" onClick={handlePrint}>
|
||||
Cetak Struk ({orientation})
|
||||
<button className="print-button" onClick={handlePrintBluetooth}>
|
||||
🖨️ Print ke Bluetooth
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="print-area">
|
||||
<h2>Struk Pembayaran</h2>
|
||||
<p><strong>Transaction ID:
|
||||
</strong> {data.transactionId}</p>
|
||||
<p><strong>Waktu:</strong> {
|
||||
(() => {
|
||||
const date = new Date(data.date);
|
||||
const options = { day: '2-digit', month: 'long', year: 'numeric' };
|
||||
const tanggal = date.toLocaleDateString('id-ID', options);
|
||||
const jam = date.toLocaleTimeString('id-ID', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
});
|
||||
return `${tanggal}, ${jam}`;
|
||||
})()
|
||||
}</p>
|
||||
|
||||
{/* <p><strong>Table:
|
||||
</strong> {data.table}</p> */}
|
||||
<p><strong>Metode Pembayaran:
|
||||
</strong> {data.payment_type}</p>
|
||||
<div>
|
||||
{data.items.map((item, idx) => (
|
||||
<p key={idx} style={{ marginBottom: '12px' }}>
|
||||
<div>{item.name} x {item.qty} Rp{item.price.toLocaleString('id-ID')}</div>
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
<h3>Total: Rp{data.total.toLocaleString('id-ID')}</h3>
|
||||
<div
|
||||
style={{
|
||||
marginTop: '24px',
|
||||
fontStyle: 'italic',
|
||||
fontSize: '12px',
|
||||
lineHeight: '1.5',
|
||||
}}
|
||||
>
|
||||
<p style={{ margin: 0 }}>Terima kasih atas kunjungan Anda!</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
fontStyle: 'italic',
|
||||
fontSize: '12px',
|
||||
lineHeight: '1.5',
|
||||
|
||||
textAlign: 'center'
|
||||
|
||||
}}
|
||||
>
|
||||
<strong style={{
|
||||
marginLeft: '-60px'
|
||||
|
||||
}}>~ Cafe Horee ~</strong>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: '24px',
|
||||
fontStyle: 'italic',
|
||||
fontSize: '12px',
|
||||
lineHeight: '1.5',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
</div>
|
||||
<p style={{
|
||||
fontSize: '9px',
|
||||
marginBottom: '30px',
|
||||
marginLeft: '60px',
|
||||
|
||||
}}>www.kedaimaster.com</p>
|
||||
</div>
|
||||
<pre className="print-area">
|
||||
{getReceiptText()}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";import qs from 'qs';
|
||||
import React, { useEffect, useState } from "react"; import qs from 'qs';
|
||||
|
||||
import styles from "./Transactions.module.css";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
@@ -62,6 +62,12 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
|
||||
return total + dt.qty * (dt.promoPrice ? dt.promoPrice : dt.price);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const formatRupiah = (number) => {
|
||||
return 'Rp' + number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
};
|
||||
|
||||
|
||||
const calculateAllTransactionsTotal = (transactions) => {
|
||||
return transactions
|
||||
.filter(transaction => transaction.confirmed > 1) // Filter transactions where confirmed > 1
|
||||
@@ -147,26 +153,63 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
|
||||
};
|
||||
|
||||
const handlePrint = (transaction) => {
|
||||
// Pilih data yang ingin dikirim
|
||||
const printableData = {
|
||||
transactionId: transaction?.transactionId,
|
||||
items: transaction?.DetailedTransactions.map(dt => ({
|
||||
name: dt.Item.name,
|
||||
qty: dt.qty,
|
||||
price: dt.promoPrice || dt.price,
|
||||
})),
|
||||
total: calculateTotalPrice(transaction.DetailedTransactions),
|
||||
date: transaction.createdAt,
|
||||
payment_type: transaction.payment_type,
|
||||
table: transaction.Table?.tableNo || "N/A",
|
||||
const formatWaktu = (() => {
|
||||
const date = new Date(transaction.createdAt);
|
||||
const tanggal = date.toLocaleDateString('id-ID');
|
||||
const jam = date.toLocaleTimeString('id-ID', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
});
|
||||
return `${tanggal} ${jam}`;
|
||||
})();
|
||||
|
||||
const itemsStr = transaction.DetailedTransactions.map((dt) => {
|
||||
const name = dt.Item.name.length > 11
|
||||
? dt.Item.name.slice(0, 11)
|
||||
: dt.Item.name.padEnd(11);
|
||||
const qty = dt.qty.toString().padStart(3);
|
||||
const total = formatRupiah(dt.qty * (dt.promoPrice || dt.price)).padStart(15);
|
||||
return `${name} ${qty} ${total}`;
|
||||
}).join('\n');
|
||||
|
||||
const totalHarga = calculateTotalPrice(transaction.DetailedTransactions);
|
||||
|
||||
const totalStr = `Total: ${formatRupiah(totalHarga)}`;
|
||||
|
||||
const receiptText = (
|
||||
` CAFE HOREE
|
||||
Jl. Ahmad Yani No. 12, Kediri
|
||||
Telp: 0812-1617-6963
|
||||
|
||||
==============================
|
||||
Tanggal : ${formatWaktu}
|
||||
Bayar : ${transaction.payment_type}
|
||||
------------------------------
|
||||
Item Qty Total
|
||||
------------------------------
|
||||
${itemsStr}
|
||||
${totalStr}
|
||||
==============================
|
||||
Terima kasih atas kunjungannya!
|
||||
~~
|
||||
supported by kedaimaster.com
|
||||
|
||||
|
||||
\n\n\n\n\n`
|
||||
);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append("content", receiptText);
|
||||
params.append("encode_format", "UTF-8");
|
||||
|
||||
const printUrl = `btprinter://print?${params.toString()}`;
|
||||
|
||||
// Trigger aplikasi printer via URL scheme
|
||||
window.location.href = printUrl;
|
||||
};
|
||||
|
||||
// Serialize to query string
|
||||
const queryString = qs.stringify({ data: JSON.stringify(printableData) });
|
||||
|
||||
// Navigate to /print with query string
|
||||
navigate(`/${shopIdentifier}/print?${queryString}`);
|
||||
};
|
||||
if (loading)
|
||||
return (
|
||||
<div className="Loader">
|
||||
@@ -179,7 +222,7 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
|
||||
return (
|
||||
<div className={styles.Transactions}>
|
||||
<h2 className={styles["Transactions-title"]}>
|
||||
Transaksi selesai Rp {calculateAllTransactionsTotal(transactions)}
|
||||
Transaksi selesai {formatRupiah(calculateAllTransactionsTotal(transactions))}
|
||||
</h2>
|
||||
|
||||
<input
|
||||
@@ -208,7 +251,7 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
|
||||
</ul>
|
||||
<div className={styles.TotalContainer}>
|
||||
<span>Total:</span>
|
||||
<span>Rp {item.totalPrice}</span>
|
||||
<span>{formatRupiah(item.totalPrice)}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -329,8 +372,11 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
|
||||
<ul>
|
||||
{transaction.DetailedTransactions.map((detail) => (
|
||||
<li key={detail.detailedTransactionId}>
|
||||
<span>{detail.Item.name}</span> - {detail.qty < 1 ? 'tidak tersedia' : `${detail.qty} x Rp
|
||||
${detail.promoPrice ? detail.promoPrice : detail.price}`}
|
||||
<span>{detail.Item.name}</span> - {detail.qty < 1
|
||||
? 'tidak tersedia'
|
||||
: `${detail.qty} x ${formatRupiah(detail.promoPrice || detail.price)}`
|
||||
}
|
||||
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
@@ -375,7 +421,7 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
|
||||
<div className={styles.TotalContainer}>
|
||||
<span>Total:</span>
|
||||
<span>
|
||||
Rp {calculateTotalPrice(transaction.DetailedTransactions)}
|
||||
{formatRupiah(calculateTotalPrice(transaction.DetailedTransactions))}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -415,9 +461,7 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
|
||||
{deviceType == 'guestDevice' && transaction.confirmed < 2 && transaction.payment_type != 'cash' && transaction.payment_type != 'paylater/cash' &&
|
||||
<ButtonWithReplica
|
||||
paymentUrl={paymentUrl}
|
||||
price={
|
||||
"Rp" + calculateTotalPrice(transaction.DetailedTransactions)
|
||||
}
|
||||
price={formatRupiah(calculateTotalPrice(transaction.DetailedTransactions))}
|
||||
disabled={isPaymentLoading}
|
||||
isPaymentLoading={isPaymentLoading}
|
||||
handleClick={() => handleConfirm(transaction.transactionId)}
|
||||
|
||||
115
src/print.css
115
src/print.css
@@ -3,13 +3,12 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
font-family: Arial, sans-serif;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.controls {
|
||||
background-color: #2c3e50;
|
||||
background-color: #333;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
@@ -19,121 +18,67 @@
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.controls h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.orientation-selector {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.orientation-selector button {
|
||||
background-color: #61dafb;
|
||||
color: #282c34;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
margin: 0 10px;
|
||||
border-radius: 5px;
|
||||
.orientation-selector button,
|
||||
.print-button {
|
||||
margin: 10px;
|
||||
padding: 10px 20px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.orientation-selector button.active {
|
||||
background-color: #21a9c7;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.orientation-selector button:hover:not(.active) {
|
||||
background-color: #4bc5e0;
|
||||
}
|
||||
|
||||
.print-button {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
font-size: 18px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
margin-top: 15px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.print-button:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.print-area {
|
||||
background-color: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
background: white;
|
||||
white-space: pre-wrap;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
padding: 10px;
|
||||
width: 48mm;
|
||||
max-width: 48mm;
|
||||
color: black;
|
||||
box-shadow: none;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* Orientation styles */
|
||||
.print-test.portrait .print-area {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.print-test.landscape .print-area {
|
||||
max-width: 1100px;
|
||||
}
|
||||
|
||||
/* Print specific styles */
|
||||
/* Print media query */
|
||||
@media print {
|
||||
@page {
|
||||
margin: 58mm;
|
||||
}
|
||||
.print-test.portrait @page {
|
||||
size: portrait;
|
||||
}
|
||||
.print-test.landscape @page {
|
||||
size: landscape;
|
||||
}
|
||||
body {
|
||||
background-color: white;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.print-area {
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 15mm;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
body * {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.print-area, .print-area * {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.print-area {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.print-area {
|
||||
font-size: 12px; /* lebih kecil dari default */
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.print-area h2 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.print-area h3 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user