diff --git a/src/components/DailyCharts.js b/src/components/DailyCharts.js
index 74f3210..9c19ff6 100644
--- a/src/components/DailyCharts.js
+++ b/src/components/DailyCharts.js
@@ -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 (
@@ -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)
}
>
-
+
{indexx !== chartData.length - 1 ? (
- <>
- {day}{" "}
- {(indexx === 0 || (formatDate(chartData[indexx - 1].date).month !== month && type != 'weekly')) && month}
- >
+
{day}{" "}
+ {(
+ indexx === 0 ||
+ (indexx > 0 &&
+ dayjs(chartData[indexx - 1].date).month() !== dayjs(item.date).month() &&
+ type !== "weekly")
+ ) && month}
+
) : (
- <>
- {type != 'weekly' ? 'Hari ini' : day}
- >
+
+ {type != 'weekly' ? 'Hari ini' : day + ' ' + month}
+
)}
- {index == indexx &&
+ {index == indexx &&
{graphFilter === 'transactions'
? chartData[indexx].totalValue
: formatRupiah(chartData[indexx].totalValue)}
@@ -193,7 +200,7 @@ const DailyCharts = ({ incomeGraph, transactionGraph, materialGraph, colors, typ
} else {
return formatRupiah(val); // format Rupiah
}
-
+
},
style: {
colors: [(index == chartData.length - 1 || selectedIndex != -1) ? "#000" : "transparent"],
diff --git a/src/helpers/transactionHelpers.js b/src/helpers/transactionHelpers.js
index adec83c..21cdfd5 100644
--- a/src/helpers/transactionHelpers.js
+++ b/src/helpers/transactionHelpers.js
@@ -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);
diff --git a/src/pages/CafePage.js b/src/pages/CafePage.js
index e184609..0484cce 100644
--- a/src/pages/CafePage.js
+++ b/src/pages/CafePage.js
@@ -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 (
+
+
+ <>
+
+
+ );
+};
+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({
)}
+{isTablet && !isFullscreen &&
}
{
+ 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,
@@ -429,15 +505,15 @@ export default function Invoice({
}}
>
{!isTablet &&
-
-}
+
+ }
Keranjang
@@ -452,7 +528,7 @@ export default function Invoice({
alignItems: "center",
}}
>
-
+
@@ -671,7 +747,7 @@ export default function Invoice({
return (
total +
(transaction.promoPrice == 0 ||
- transaction.promoPrice == null
+ transaction.promoPrice == null
? transaction.price * transaction.qty
: transaction.promoPrice * transaction.qty)
);
diff --git a/src/pages/PrintPage.js b/src/pages/PrintPage.js
index f0805bb..e04e924 100644
--- a/src/pages/PrintPage.js
+++ b/src/pages/PrintPage.js
@@ -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
Invalid data
;
- 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
-
-
-
Struk Pembayaran
-
Transaction ID:
- {data.transactionId}
-
Waktu: {
- (() => {
- 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}`;
- })()
- }
-
- {/*
Table:
- {data.table}
*/}
-
Metode Pembayaran:
- {data.payment_type}
-
- {data.items.map((item, idx) => (
-
-
{item.name} x {item.qty} Rp{item.price.toLocaleString('id-ID')}
-
- ))}
-
-
Total: Rp{data.total.toLocaleString('id-ID')}
-
-
Terima kasih atas kunjungan Anda!
-
-
-
- ~ Cafe Horee ~
-
-
-
-
-
www.kedaimaster.com
-
+
+ {getReceiptText()}
+
);
}
diff --git a/src/pages/Transactions.js b/src/pages/Transactions.js
index 297305c..0acd9d9 100644
--- a/src/pages/Transactions.js
+++ b/src/pages/Transactions.js
@@ -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}`;
+ })();
- // Serialize to query string
- const queryString = qs.stringify({ data: JSON.stringify(printableData) });
+ 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');
- // Navigate to /print with query string
- navigate(`/${shopIdentifier}/print?${queryString}`);
+ 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;
};
+
+
if (loading)
return (
@@ -179,7 +222,7 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
return (
))}
@@ -329,8 +372,11 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
{transaction.DetailedTransactions.map((detail) => (
-
- {detail.Item.name} - {detail.qty < 1 ? 'tidak tersedia' : `${detail.qty} x Rp
- ${detail.promoPrice ? detail.promoPrice : detail.price}`}
+ {detail.Item.name} - {detail.qty < 1
+ ? 'tidak tersedia'
+ : `${detail.qty} x ${formatRupiah(detail.promoPrice || detail.price)}`
+ }
+
))}
@@ -375,7 +421,7 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
Total:
- Rp {calculateTotalPrice(transaction.DetailedTransactions)}
+ {formatRupiah(calculateTotalPrice(transaction.DetailedTransactions))}
@@ -404,20 +450,18 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
>
}
- {deviceType == 'clerk' && transaction.confirmed > 1 && (
-
handlePrint(transaction)}
- >
- Cetak struk
-
- )}
+ {deviceType == 'clerk' && transaction.confirmed > 1 && (
+
handlePrint(transaction)}
+ >
+ Cetak struk
+
+ )}
{deviceType == 'guestDevice' && transaction.confirmed < 2 && transaction.payment_type != 'cash' && transaction.payment_type != 'paylater/cash' &&
handleConfirm(transaction.transactionId)}
diff --git a/src/print.css b/src/print.css
index b721fad..226e566 100644
--- a/src/print.css
+++ b/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;
-}
-
}