Compare commits

...

10 Commits

Author SHA1 Message Date
Vassshhh
dd0227ab80 ok 2025-08-26 01:46:40 +07:00
Vassshhh
67cf759b31 ok 2025-08-25 23:41:35 +07:00
Vassshhh
53e091d3a4 ok 2025-07-28 01:15:07 +07:00
everythingonblack
3a431b1b14 ok 2025-05-23 10:50:39 +07:00
everythingonblack
69a07be3cd ok 2025-05-22 16:43:50 +07:00
everythingonblack
b012517568 ok 2025-05-22 02:15:12 +07:00
everythingonblack
3e35468f2c ok 2025-05-21 16:52:38 +07:00
everythingonblack
df7c4f737c ok 2025-05-20 17:47:43 +07:00
everythingonblack
b726ae6919 ok 2025-05-16 19:54:09 +07:00
everythingonblack
da317f83c9 ok 2025-05-15 17:52:24 +07:00
25 changed files with 6997 additions and 4585 deletions

10942
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
"html-to-image": "^1.11.11", "html-to-image": "^1.11.11",
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"jsqr": "^1.4.0", "jsqr": "^1.4.0",
"qr-scanner": "^1.4.2",
"qrcode.react": "^3.1.0", "qrcode.react": "^3.1.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-apexcharts": "^1.7.0", "react-apexcharts": "^1.7.0",

View File

@@ -28,6 +28,15 @@
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>KedaiMaster</title> <title>KedaiMaster</title>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-2SKSCVFB2N"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-2SKSCVFB2N');
</script>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -3,17 +3,17 @@
"name": "jangan pernah ragukan pelanggan", "name": "jangan pernah ragukan pelanggan",
"icons": [ "icons": [
{ {
"src": "favicon.ico", "src": "kedai.png",
"sizes": "64x64 32x32 24x24 16x16", "sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon" "type": "image/x-icon"
}, },
{ {
"src": "logo192.png", "src": "kedai.png",
"type": "image/png", "type": "image/png",
"sizes": "192x192" "sizes": "192x192"
}, },
{ {
"src": "logo512.png", "src": "kedai.png",
"type": "image/png", "type": "image/png",
"sizes": "512x512" "sizes": "512x512"
} }

View File

@@ -3,12 +3,10 @@
@import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@200;300;400;500;600;700;800&ital,wght@0,200..800;1,200..800&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@200;300;400;500;600;700;800&ital,wght@0,200..800;1,200..800&display=swap");
html, html,
body { body {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */ scrollbar-width: none; /* Firefox */
} }
.App { .App {
/* overflow-x: hidden; */
} }
.Cafe { .Cafe {

View File

@@ -71,6 +71,12 @@ function App() {
const [depth, setDepth] = useState(-1); const [depth, setDepth] = useState(-1);
const [queue, setQueue] = useState([]); const [queue, setQueue] = useState([]);
const [newTransaction, setNewTransaction] = useState({});
const queryParams = new URLSearchParams(location.search);
const tokenParams = queryParams.get("token");
if(tokenParams) localStorage.setItem('auth', tokenParams)
const validTransactionStates = [ const validTransactionStates = [
'new_transaction', 'new_transaction',
@@ -109,6 +115,10 @@ function App() {
const handleStorageChange = () => { const handleStorageChange = () => {
calculateTotalsFromLocalStorage(); calculateTotalsFromLocalStorage();
if (!localStorage.getItem("lastTransaction")) {
setLastTransaction(null);
}
}; };
window.addEventListener("localStorageUpdated", handleStorageChange); window.addEventListener("localStorageUpdated", handleStorageChange);
@@ -135,6 +145,7 @@ function App() {
return; return;
} }
setModal('transaction_confirmed', { transactionId: lastTransaction.transactionId })
const myLastTransaction = await checkIsMyTransaction(lastTransaction.transactionId); const myLastTransaction = await checkIsMyTransaction(lastTransaction.transactionId);
console.log(myLastTransaction) console.log(myLastTransaction)
if (myLastTransaction.isMyTransaction) { if (myLastTransaction.isMyTransaction) {
@@ -219,7 +230,7 @@ function App() {
}); });
} else { } else {
socket.emit("checkUserToken", { socket.emit("checkUserToken", {
token: getLocalStorage("auth"), token: getLocalStorage("auth") || tokenParams,
shopId, shopId,
}); });
} }
@@ -236,24 +247,23 @@ function App() {
}); });
socket.on("transaction_confirmed", async (data) => { socket.on("transaction_confirmed", async (data) => {
console.log("transaction notification: " + data); console.log(JSON.stringify(data));
setModal("transaction_confirmed", data); setModal("transaction_confirmed", data);
localStorage.setItem('cart', []); localStorage.setItem('cart', []);
const startTime = Date.now(); // Capture the start time // const startTime = Date.now(); // Capture the start time
const timeout = 10000; // 10 seconds timeout in milliseconds // const timeout = 10000; // 10 seconds timeout in milliseconds
calculateTotalsFromLocalStorage(); calculateTotalsFromLocalStorage();
while (localStorage.getItem("lastTransaction") === null) { // while (localStorage.getItem("lastTransaction") === null) {
if (Date.now() - startTime > timeout) { // if (Date.now() - startTime > timeout) {
return; // Exit the function and don't proceed further // return; // Exit the function and don't proceed further
} // }
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second
}
// await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second
// }
// If 'lastTransaction' exists, proceed // If 'lastTransaction' exists, proceed
const lastTransaction = JSON.parse(localStorage.getItem("lastTransaction")); const lastTransaction = JSON.parse(localStorage.getItem("lastTransaction"));
@@ -270,17 +280,23 @@ function App() {
setModal("transaction_success", data); setModal("transaction_success", data);
// If 'lastTransaction' exists, proceed // If 'lastTransaction' exists, proceed
localStorage.removeItem("lastTransaction"); if (localStorage.getItem("lastTransaction")) {
if (lastTransaction != null) {
setLastTransaction(null); setLastTransaction(null);
console.log('remove last transaction') localStorage.removeItem("lastTransaction");
window.dispatchEvent(new Event("localStorageUpdated"));
} }
}); });
socket.on("transaction_end", async (data) => { socket.on("transaction_end", async (data) => {
console.log("transaction notification"); console.log("transaction notification");
setModal("transaction_end", data); setModal("transaction_end", data);
// If 'lastTransaction' exists, proceed
if (localStorage.getItem("lastTransaction")) {
setLastTransaction(null);
localStorage.removeItem("lastTransaction");
window.dispatchEvent(new Event("localStorageUpdated"));
}
}); });
socket.on("payment_claimed", async (data) => { socket.on("payment_claimed", async (data) => {
@@ -289,8 +305,15 @@ function App() {
}); });
socket.on("transaction_failed", async (data) => { socket.on("transaction_failed", async (data) => {
console.log("transaction notification"); console.log(JSON.stringify(data));
setModal("transaction_failed", data); setModal("transaction_failed", data);
// If 'lastTransaction' exists, proceed
if (localStorage.getItem("lastTransaction")) {
setLastTransaction(null);
localStorage.removeItem("lastTransaction");
window.dispatchEvent(new Event("localStorageUpdated"));
}
}); });
@@ -378,30 +401,32 @@ function App() {
}; };
}, [socket, shopId]); }, [socket, shopId]);
async function checkIfStillViewingOtherTransaction() { async function checkIfStillViewingOtherTransaction(data) {
console.log("transaction notification"); console.log("transaction notification");
console.log(modalContent); console.log(modalContent);
let response; let response;
response = await getTransactionsFromCafe(shopId, 0, true); response = await getTransactionsFromCafe(shopId, 0, true);
transactionList.current = response;
console.log(response); console.log(response);
// Get current URL's search parameters inside the socket event handler // Get current URL's search parameters inside the socket event handler
const searchParams = new URLSearchParams(location.search); const searchParams = new URLSearchParams(location.search);
let transaction_info = searchParams.get("transactionId") || ''; // Get transactionId or set it to empty string let transaction_info = searchParams.get("transactionId") || ''; // Get transactionId or set it to empty string
console.log(transaction_info); // Log the updated transaction_info
if(response[0].transactionId != transaction_info) transactionList.current = response;
const depthh = transactionList.current.findIndex( let depthh = transactionList.current.findIndex(
item => item.transactionId.toString() === transaction_info.toString() item => item.transactionId.toString() === transaction_info.toString()
); );
if (transaction_info != response[0].transactionId)
setDepth(depthh); setDepth(depthh);
else setModal("new_transaction", data);
console.log(transaction_info == response[0].transactionId) console.log(transaction_info == response[0].transactionId)
// If transaction_info is an empty string, set the modal // If transaction_info is an empty string, set the modal
if (transaction_info == '' || transaction_info == response[0].transactionId) return false; if (transaction_info.toString() == '') return false;
else return true; else return true;
} }
@@ -409,12 +434,15 @@ function App() {
// This will ensure that searchParams and transaction_info get updated on each render // This will ensure that searchParams and transaction_info get updated on each render
socket.on("transaction_created", async (data) => { socket.on("transaction_created", async (data) => {
console.log("transaction notification"); console.log("transaction notification");
const isViewingOtherTransaction = await checkIfStillViewingOtherTransaction(); setNewTransaction(data);
if(!location.pathname.endsWith('/transactions')){
const isViewingOtherTransaction = await checkIfStillViewingOtherTransaction(data);
// If transaction_info is an empty string, set the modal // If transaction_info is an empty string, set the modal
if (!isViewingOtherTransaction) { if (!isViewingOtherTransaction) {
setModal("new_transaction", data); setModal("new_transaction", data);
} }
}
// Show browser notification // Show browser notification
let permission = Notification.permission; let permission = Notification.permission;
if (permission !== "granted") return; if (permission !== "granted") return;
@@ -429,12 +457,15 @@ function App() {
socket.on("transaction_canceled", async (data) => { socket.on("transaction_canceled", async (data) => {
console.log("transaction notification"); console.log("transaction notification");
const isViewingOtherTransaction = await checkIfStillViewingOtherTransaction(); setNewTransaction(data);
if(location.pathname != '/transactions'){
const isViewingOtherTransaction = await checkIfStillViewingOtherTransaction(data);
// If transaction_info is an empty string, set the modal // If transaction_info is an empty string, set the modal
if (!isViewingOtherTransaction) { if (!isViewingOtherTransaction) {
setModal("new_transaction", data); setModal("new_transaction", data);
navigate(`?transactionId=${data.transactionId}`, { replace: true }); navigate(`?transactionId=${data.transactionId}`, { replace: true });
} }
}
}); });
// Clean up the socket event listener on unmount or when dependencies change // Clean up the socket event listener on unmount or when dependencies change
@@ -444,17 +475,6 @@ function App() {
}; };
}, [socket, shopId, location]); // Ensure location is in the dependencies to respond to changes in the URL }, [socket, shopId, location]); // Ensure location is in the dependencies to respond to changes in the URL
// useEffect(() => {
// if (user.cafeId != null && user.cafeId !== shopId) {
// // Preserve existing query parameters
// const currentParams = new URLSearchParams(location.search).toString();
// // Navigate to the new cafeId while keeping existing params
// navigate(`/${user.cafeId}?${currentParams}`, { replace: true });
// }
// }, [user, shopId]);
function handleMoveToTransaction(direction, from) { function handleMoveToTransaction(direction, from) {
console.log(direction); console.log(direction);
console.log(from); console.log(from);
@@ -683,36 +703,6 @@ function App() {
totalPrice={totalPrice} totalPrice={totalPrice}
lastTransaction={lastTransaction} lastTransaction={lastTransaction}
/> />
{/* <Footer
showTable={true}
shopId={shopIdentifier}
table={table}
cartItemsLength={totalItemsCount}
selectedPage={0}
/> */}
</>
}
/>
<Route
path="/:shopIdentifier/:tableCode?/search"
element={
<>
<SearchResult
cafeId={shopId}
sendParam={handleSetParam}
user={user}
shopItems={shopItems}
guestSides={guestSides}
guestSideOfClerk={guestSideOfClerk}
removeConnectedGuestSides={rmConnectedGuestSides}
setModal={setModal} // Pass the function to open modal
/>
{/* <Footer
shopId={shopIdentifier}
table={table}
cartItemsLength={totalItemsCount}
selectedPage={1}
/> */}
</> </>
} }
/> />
@@ -732,12 +722,6 @@ function App() {
shopItems={shopItems} shopItems={shopItems}
setShopItems={setShopItems} setShopItems={setShopItems}
/> />
{/* <Footer
shopId={shopIdentifier}
table={table}
cartItemsLength={totalItemsCount}
selectedPage={2}
/> */}
</> </>
} }
/> />
@@ -771,6 +755,8 @@ function App() {
sendParam={handleSetParam} sendParam={handleSetParam}
deviceType={deviceType} deviceType={deviceType}
paymentUrl={shop.qrPayment} paymentUrl={shop.qrPayment}
setModal={setModal}
newTransaction={newTransaction}
/> />
{/* <Footer {/* <Footer
shopId={shopIdentifier} shopId={shopIdentifier}

View File

@@ -44,10 +44,10 @@ const App = () => {
useEffect(() => { useEffect(() => {
const shopId = window.location.pathname.split('/')[1]; // Get shopId from the URL const shopId = window.location.pathname.split('/')[1]; // Get shopId from the URL
const userId = localStorage.getItem('userId'); const user_id = localStorage.getItem('user_id');
// Connect to Socket.IO if userId is present // Connect to Socket.IO if user_id is present
// if (userId) { // if (user_id) {
// connectSocket(shopId, 1); // connectSocket(shopId, 1);
// } // }

View File

@@ -359,7 +359,7 @@ const Header = ({
{shopId && user.roleId == 1 && ( {shopId && user.roleId == 1 && (
<Child onClick={goToAdminCafes}>Dashboard</Child>)} <Child onClick={goToAdminCafes}>Dashboard</Child>)}
{shopId && {shopId &&
user.userId == shopOwnerId && user.user_id == shopOwnerId &&
user.username !== undefined && user.username !== undefined &&
user.roleId === 1 && ( user.roleId === 1 && (
<> <>

View File

@@ -598,7 +598,7 @@ const ItemLister = ({
return ( return (
<> <>
{(items.length > 0 || {(items.length > 0 ||
(user && (user.cafeId == shopId || user.userId == shopOwnerId))) && ( (user && (user.cafeId == shopId || user.user_id == shopOwnerId))) && (
<div <div
key={itemTypeId} key={itemTypeId}
className={`${styles["item-lister"]} ${isEdit ? styles["fullscreen"] : "" className={`${styles["item-lister"]} ${isEdit ? styles["fullscreen"] : ""
@@ -866,7 +866,7 @@ const ItemLister = ({
<h2 className={styles["item-list-title"]}>{items && items.length < 1 ? 'Buat item' : 'Daftar item'}</h2></div>} <h2 className={styles["item-list-title"]}>{items && items.length < 1 ? 'Buat item' : 'Daftar item'}</h2></div>}
<div className={styles["item-list"]}> <div className={styles["item-list"]}>
{user && ( {user && (
user.userId == shopOwnerId || user.cafeId == shopId) && user.user_id == shopOwnerId || user.cafeId == shopId) &&
isEditMode && ( isEditMode && (
<> <>
{!isAddingNewItem && ( {!isAddingNewItem && (
@@ -1113,7 +1113,7 @@ const ItemLister = ({
{user && {user &&
user.roleId == 1 && user.roleId == 1 &&
user.userId == shopOwnerId && user.user_id == shopOwnerId &&
isEdit && ( isEdit && (
<> <>
{/* <button {/* <button

View File

@@ -103,7 +103,7 @@ const ItemTypeLister = ({
{isEditMode && {isEditMode &&
!isAddingNewItem && !isAddingNewItem &&
user && ( user && (
user.userId == shopOwnerId || user.cafeId == shopId) && ( user.user_id == shopOwnerId || user.cafeId == shopId) && (
<ItemType <ItemType
onClick={toggleAddNewItem} onClick={toggleAddNewItem}
name={"buat baru"} name={"buat baru"}
@@ -111,7 +111,7 @@ const ItemTypeLister = ({
/> />
)} )}
{user &&( {user &&(
user.userId == shopOwnerId || user.cafeId == shopId) && user.user_id == shopOwnerId || user.cafeId == shopId) &&
isAddingNewItem && ( isAddingNewItem && (
<> <>
<ItemLister <ItemLister
@@ -141,7 +141,7 @@ const ItemTypeLister = ({
itemTypes.map( itemTypes.map(
(itemType) => (itemType) =>
( (
itemType.itemList.length > 0 || (user && (user.userId == shopOwnerId || user.cafeId == shopId))) && ( itemType.itemList.length > 0 || (user && (user.user_id == shopOwnerId || user.cafeId == shopId))) && (
<ItemType <ItemType
key={itemType.itemTypeId} key={itemType.itemTypeId}
name={itemType.name} name={itemType.name}

View File

@@ -88,7 +88,7 @@ const Modal = ({ user, shop, isOpen, onClose, modalContent, deviceType, setModal
{modalContent === "create_tenant" && <CreateTenant shopId={shop.cafeId} />} {modalContent === "create_tenant" && <CreateTenant shopId={shop.cafeId} />}
{modalContent === "edit_tables" && <TablesPage shop={shop} />} {modalContent === "edit_tables" && <TablesPage shop={shop} />}
{modalContent === "new_transaction" && ( {modalContent === "new_transaction" && (
<Transaction propsShopId={shop.cafeId} handleMoveToTransaction={handleMoveToTransaction} depth={depth} shopImg={shopImg} /> <Transaction propsShopId={shop.cafeId} handleMoveToTransaction={handleMoveToTransaction} depth={depth} shopImg={shopImg} setModal={setModal}/>
)} )}
{modalContent === "transaction_canceled" && ( {modalContent === "transaction_canceled" && (
<Transaction propsShopId={shop.cafeId} /> <Transaction propsShopId={shop.cafeId} />

View File

@@ -479,7 +479,7 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
className={`expandable-container ${expanded ? "expanded" : ""}`} className={`expandable-container ${expanded ? "expanded" : ""}`}
ref={expandableContainerRef} ref={expandableContainerRef}
> >
{user.cafeId == shopId || user.userId == shopOwnerId && ( {user.cafeId == shopId || user.user_id == shopOwnerId && (
<> <>
<div className="auth-box"> <div className="auth-box">
<div <div

View File

@@ -1,5 +1,5 @@
import React, { useState, useRef, useEffect } from "react"; import React, { useState, useRef, useEffect } from "react";
import jsQR from "jsqr"; import QrScanner from "qr-scanner"; // Import qr-scanner
import { getImageUrl } from "../helpers/itemHelper"; import { getImageUrl } from "../helpers/itemHelper";
import { import {
getCafe, getCafe,
@@ -20,6 +20,8 @@ const SetPaymentQr = ({ shopId,
const [isQRISavailable, setIsQRISavailable] = useState(0); const [isQRISavailable, setIsQRISavailable] = useState(0);
const qrPaymentInputRef = useRef(null); const qrPaymentInputRef = useRef(null);
const qrCodeContainerRef = useRef(null); const qrCodeContainerRef = useRef(null);
const [qrCodeData, setQrCodeData] = useState(null);
const [cafe, setCafe] = useState({}); const [cafe, setCafe] = useState({});
const [isConfigQRIS, setIsConfigQRIS] = useState(false); const [isConfigQRIS, setIsConfigQRIS] = useState(false);
@@ -45,12 +47,6 @@ const SetPaymentQr = ({ shopId,
fetchCafe(); fetchCafe();
}, [shopId]); }, [shopId]);
// Detect QR code when qrPayment updates
useEffect(() => {
if (qrPayment && isConfigQRIS) {
detectQRCodeFromContainer();
}
}, [qrPayment, isConfigQRIS]);
// Handle file input change // Handle file input change
const handleFileChange = (e) => { const handleFileChange = (e) => {
@@ -62,25 +58,48 @@ const SetPaymentQr = ({ shopId,
} }
}; };
// Detect QR code from the container useEffect(() => {
if (qrPayment && isConfigQRIS) {
detectQRCodeFromContainer();
}
}, [qrPayment, isConfigQRIS]);
const detectQRCodeFromContainer = () => { const detectQRCodeFromContainer = () => {
const container = qrCodeContainerRef.current; const container = qrCodeContainerRef.current;
const canvas = document.createElement("canvas"); if (!container) return;
const context = canvas.getContext("2d");
const img = new Image(); const img = new Image();
img.crossOrigin = "Anonymous"; img.crossOrigin = "Anonymous";
img.onload = () => {
canvas.width = container.offsetWidth;
canvas.height = container.offsetHeight;
context.drawImage(img, 0, 0, canvas.width, canvas.height);
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
const qrCode = jsQR(imageData.data, canvas.width, canvas.height);
setQrCodeDetected(!!qrCode);
if (qrCode) {
console.log("QR Code detected:", qrCode.data);
}
};
img.src = qrPayment; img.src = qrPayment;
img.onload = () => {
// Buat canvas dari image
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0);
// Ambil data URL dari canvas (png)
const imageDataUrl = canvas.toDataURL();
QrScanner.scanImage(imageDataUrl, { returnDetailedScanResult: true })
.then(result => {
setQrCodeDetected(true);
setQrCodeData(result.data);
console.log("QR Code detected:", result.data);
})
.catch(() => {
setQrCodeDetected(false);
setQrCodeData(null);
console.log("QR Code not detected");
});
};
img.onerror = () => {
setQrCodeDetected(false);
setQrCodeData(null);
};
}; };
// Save cafe details // Save cafe details

View File

@@ -106,10 +106,10 @@ export async function getCafeByIdentifier(cafeIdentifyName) {
return -1; return -1;
} }
} }
export async function getOwnedCafes(userId) { export async function getOwnedCafes(user_id) {
try { try {
const response = await fetch( const response = await fetch(
`${API_BASE_URL}/cafe/get-cafe-by-ownerId/` + userId, `${API_BASE_URL}/cafe/get-cafe-by-ownerId/` + user_id,
{ {
method: "POST", method: "POST",
headers: { headers: {

View File

@@ -77,17 +77,28 @@ function CafePage({
const [beingEditedType, setBeingEditedType] = useState(0); const [beingEditedType, setBeingEditedType] = useState(0);
const checkWelcomePageConfig = () => { // const checkWelcomePageConfig = () => {
const parsedConfig = JSON.parse(welcomePageConfig); // const parsedConfig = JSON.parse(welcomePageConfig);
if (parsedConfig.isWelcomePageActive == "true") { // if (parsedConfig.isWelcomePageActive == "true") {
const clicked = sessionStorage.getItem("getStartedClicked"); // const clicked = sessionStorage.getItem("getStartedClicked");
if (!clicked) { // if (!clicked) {
sessionStorage.setItem("getStartedClicked", true); // sessionStorage.setItem("getStartedClicked", true);
document.body.style.overflow = "hidden"; // document.body.style.overflow = "hidden";
setIsStarted(true); // setIsStarted(true);
// }
// }
// };
useEffect(() => {
if (window.gtag && shopIdentifier) {
window.gtag('event', 'page_view', {
page_title: `Cafe - ${shopIdentifier}`,
page_location: window.location.href,
page_path: `/` + shopIdentifier,
shop_id: shopId || null, // opsional jika kamu mau track ID juga
});
} }
} }, [shopIdentifier]);
};
useEffect(() => { useEffect(() => {
if (welcomePageConfig) { if (welcomePageConfig) {
@@ -100,16 +111,16 @@ function CafePage({
isActive: parsedConfig.isWelcomePageActive === "true", isActive: parsedConfig.isWelcomePageActive === "true",
}); });
} }
checkWelcomePageConfig(); // checkWelcomePageConfig();
}, [welcomePageConfig]); }, [welcomePageConfig]);
useEffect(() => { useEffect(() => {
function fetchData() { function fetchData() {
console.log(user.userId == shopOwnerId) console.log(user.user_id == shopOwnerId)
setModal("create_item"); setModal("create_item");
} }
console.log(getLocalStorage('auth')) console.log(getLocalStorage('auth'))
if (getLocalStorage("auth") != null) { if (getLocalStorage("auth") != null) {
const executeFetch = async () => { const executeFetch = async () => {
@@ -118,7 +129,7 @@ function CafePage({
} }
console.log(user) console.log(user)
console.log('open') console.log('open')
if (user.length != 0 && user.userId == shopOwnerId && shopItems.length == 0) fetchData(); if (user.length != 0 && user.user_id == shopOwnerId && shopItems.length == 0) fetchData();
}; };
executeFetch(); executeFetch();
} }

View File

@@ -272,6 +272,8 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
socketId socketId
); );
localStorage.removeItem('lastTransaction') localStorage.removeItem('lastTransaction')
// Dispatch the custom event
window.dispatchEvent(new Event("localStorageUpdated"));
} }
else else
@@ -442,6 +444,18 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
)} )}
<div className={styles.NoteContainer}> <div className={styles.NoteContainer}>
<span>Atas Nama :</span>
<span></span>
</div>
<div className={styles.NoteContainer} >
<input
className={styles.NoteInput}
placeholder="Tambahkan catatan..."
/>
</div>
<div className={styles.NoteContainer}style={{height: '18px'}}>
<span>Catatan :</span> <span>Catatan :</span>
<span></span> <span></span>
</div> </div>

View File

@@ -49,7 +49,7 @@ const Dashboard = ({ user, setModal }) => {
// Create admin functionality // Create admin functionality
createCafeOwner(newItem.email, newItem.username, newItem.password) createCafeOwner(newItem.email, newItem.username, newItem.password)
.then((newitem) => { .then((newitem) => {
setItems([...items, { userId: newitem.userId, name: newitem.username }]); setItems([...items, { user_id: newitem.user_id, name: newitem.username }]);
setIsCreating(false); setIsCreating(false);
setNewItem({ name: "", type: "" }); setNewItem({ name: "", type: "" });
}) })

View File

@@ -71,9 +71,9 @@ const LinktreePage = ({ user, setModal }) => {
// Handle manual coupon code check // Handle manual coupon code check
const handleGetkCoupons = async () => { const handleGetkCoupons = async () => {
const result = await getUserCoupons(); // const result = await getUserCoupons();
setCoupons(result.coupons); // setCoupons(result.coupons);
console.log(result) // console.log(result)
}; };
// Handle user transactions // Handle user transactions
@@ -95,24 +95,20 @@ const LinktreePage = ({ user, setModal }) => {
} }
}; };
// Handle login const handleLogin = () => {
const handleLogin = async () => { const baseUrl = "https://kediritechnopark.com/";
try { const modal = "product";
setError(false); const productId = 1;
setLoading(true);
const response = await loginUser(username, password); const authorizedUri = "http://localhost:3000?token=";
if (response.success) { const unauthorizedUri = `${baseUrl}?modal=${modal}&product_id=${productId}`;
localStorage.setItem('auth', response.token);
console.log(response) const url =
window.location.href = response.cafeIdentifyName ? `/${response.cafeIdentifyName}` : '/'; `${baseUrl}?modal=${modal}&product_id=${productId}` +
} else { `&authorized_uri=${encodeURIComponent(authorizedUri)}` +
setError(true); `&unauthorized_uri=${encodeURIComponent(unauthorizedUri)}`;
}
} catch (error) { window.location.href = url;
setError(true);
} finally {
setLoading(false);
}
}; };
// Handle logout // Handle logout
@@ -152,7 +148,7 @@ const LinktreePage = ({ user, setModal }) => {
try { try {
if (user.roleId < 1) { if (user.roleId < 1) {
const newOwner = await createCafeOwner(newItem.email, newItem.username, newItem.password); const newOwner = await createCafeOwner(newItem.email, newItem.username, newItem.password);
setItems([...items, { userId: newOwner.userId, name: newOwner.username }]); setItems([...items, { user_id: newOwner.user_id, name: newOwner.username }]);
} else { } else {
const newCafe = await createCafe(newItem.name); const newCafe = await createCafe(newItem.name);
setItems([...items, { cafeId: newCafe.cafeId, name: newCafe.name }]); setItems([...items, { cafeId: newCafe.cafeId, name: newCafe.name }]);
@@ -202,7 +198,7 @@ const LinktreePage = ({ user, setModal }) => {
]; ];
console.log(items) console.log(items)
const selectedItems = items?.items?.find(item => (item.userId || item.cafeId) === selectedItemId); const selectedItems = items?.items?.find(item => (item.user_id || item.cafeId) === selectedItemId);
// If the selected tenant is found, extract the cafes // If the selected tenant is found, extract the cafes
const selectedSubItems = selectedItems?.subItems || []; const selectedSubItems = selectedItems?.subItems || [];
@@ -278,7 +274,7 @@ const LinktreePage = ({ user, setModal }) => {
))} ))}
</div> </div>
</div> </div>
gratis 3 bulan pertama gratis 1 bulan pertama
</div> </div>
: :
<div className={styles.mainHeading}> <div className={styles.mainHeading}>
@@ -290,57 +286,20 @@ const LinktreePage = ({ user, setModal }) => {
))} ))}
</div> </div>
</div> </div>
Gratis 3 bulan pertama Gratis 1 bulan pertama
</div> </div>
} }
<div className={styles.subHeading}> <div className={styles.subHeading}>
Solusi berbasis web untuk memudahkan pengelolaan kedai, dengan fitur yang mempermudah pemilik, kasir, dan tamu berinteraksi. Solusi berbasis web untuk memudahkan pengelolaan kedai, dengan fitur yang mempermudah pemilik, kasir, dan tamu berinteraksi.
</div> </div>
{getLocalStorage('auth') == null && (
<div className={styles.LoginForm}> <div className={styles.LoginForm}>
<div className={`${styles.FormUsername} ${inputtingPassword ? styles.animateForm : wasInputtingPassword ? styles.reverseForm : ''}`}> <div className={`${styles.FormUsername} ${inputtingPassword ? styles.animateForm : wasInputtingPassword ? styles.reverseForm : ''}`}>
<label htmlFor="username" className={styles.usernameLabel}>---- Masuk -----------------------------</label> <button onClick={() => handleLogin()} className={styles.claimButton}>
<input <span>Masuk</span>
id="username"
placeholder="username"
maxLength="30"
className={!error ? styles.usernameInput : styles.usernameInputError}
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<button onClick={() => { setInputtingPassword(true); setWasInputtingPassword(true) }} className={styles.claimButton}>
<span></span>
</button>
</div>
<div className={`${styles.FormPassword} ${inputtingPassword ? styles.animateForm : wasInputtingPassword ? styles.reverseForm : styles.idleForm}`}>
<span>
<label onClick={() => setInputtingPassword(false)} htmlFor="password" className={styles.usernameLabel}> &lt;--- &lt;-- Kembali </label>
<label htmlFor="password" className={styles.usernameLabel}> &nbsp; ----- &nbsp; </label>
<label onClick={() => setModal('reset-password', { username: username })} className={styles.usernameLabel}>
lupa password?
</label>
</span>
<input
id="password"
placeholder="password"
type="password"
maxLength="30"
className={!error ? styles.usernameInput : styles.usernameInputError}
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button
onClick={handleLogin}
className={`${styles.claimButton} ${loading ? styles.loading : ''}`}
disabled={loading}
>
<span>{loading ? 'Loading...' : 'Masuk'}</span>
</button> </button>
</div> </div>
</div> </div>
)}
<div className={styles.footer}> <div className={styles.footer}>
<div className={styles.footerLinks}> <div className={styles.footerLinks}>

View File

@@ -191,7 +191,7 @@
.NoteInput { .NoteInput {
width: 78vw; width: 78vw;
height: 12vw; height: 18px;
border-radius: 20px; border-radius: 20px;
margin: 0 auto; margin: 0 auto;
padding: 10px; padding: 10px;

View File

@@ -110,7 +110,7 @@ const LinktreePage = ({ data, setModal }) => {
</div> </div>
<div className={styles.linktreeForm}> <div className={styles.linktreeForm}>
<button onClick={()=>window.open("https://api.whatsapp.com/send?phone=6281318894994&text=Saya%20ingin%20coba%20gratis%203%20bulan")} className={styles.claimButton}> <button onClick={()=>window.open("https://api.whatsapp.com/send?phone=6281318894994&text=Saya%20ingin%20coba%20gratis%203%20bulan")} className={styles.claimButton}>
<span>Dapatkan voucher gratis 3 bulan</span> <span>Dapatkan voucher gratis 1 bulan</span>
</button> </button>
</div> </div>
<div className={styles.footer}> <div className={styles.footer}>

View File

@@ -338,7 +338,6 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
margin-top: 2rem;
} }
.footerLinks { .footerLinks {

View File

@@ -82,7 +82,7 @@ const RoundedRectangle = ({
}; };
const percentageStyle = { const percentageStyle = {
fontSize: "16px", fontSize: "14px",
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
textAlign: "right", textAlign: "right",
@@ -282,11 +282,11 @@ const App = ({ forCafe = true, cafeId = -1,
if (amount >= 1_000_000_000) { if (amount >= 1_000_000_000) {
// Format for billions // Format for billions
const billions = amount / 1_000_000_000; const billions = amount / 1_000_000_000;
return billions.toFixed(0) + "b"; // No decimal places for billions return billions.toFixed(0) + "m"; // No decimal places for billions
} else if (amount >= 1_000_000) { } else if (amount >= 1_000_000) {
// Format for millions // Format for millions
const millions = amount / 1_000_000; const millions = amount / 1_000_000;
return millions.toFixed(2).replace(/\.00$/, "") + "m"; // Two decimal places, remove trailing '.00' return millions.toFixed(2).replace(/\.00$/, "") + "jt"; // Two decimal places, remove trailing '.00'
} else if (amount >= 1_000) { } else if (amount >= 1_000) {
// Format for thousands // Format for thousands
const thousands = amount / 1_000; const thousands = amount / 1_000;
@@ -326,7 +326,7 @@ const App = ({ forCafe = true, cafeId = -1,
setSelectedCafeId(-1); setSelectedCafeId(-1);
} else if (otherCafes.length === 1) { } else if (otherCafes.length === 1) {
updatedFullTexts = [ updatedFullTexts = [
[otherCafes[0].cafeIdentifyName || otherCafes[0].username, otherCafes[0].cafeId || otherCafes[0].userId], [otherCafes[0].cafeIdentifyName || otherCafes[0].username, otherCafes[0].cafeId || otherCafes[0].user_id],
// Only add the "Buat Bisnis" option for user.roleId == 1 // Only add the "Buat Bisnis" option for user.roleId == 1
...(user.roleId == 1 ? [["Buat Bisnis", -1]] : []) ...(user.roleId == 1 ? [["Buat Bisnis", -1]] : [])
]; ];
@@ -335,7 +335,7 @@ const App = ({ forCafe = true, cafeId = -1,
} else { } else {
updatedFullTexts = [ updatedFullTexts = [
["semua", 0], // First entry is "semua" ["semua", 0], // First entry is "semua"
...otherCafes.map(item => [item.cafeIdentifyName || item.username, item.cafeId || item.userId]), // Map over cafes to get name and cafeId pairs ...otherCafes.map(item => [item.cafeIdentifyName || item.username, item.cafeId || item.user_id]), // Map over cafes to get name and cafeId pairs
// Only add "Buat Bisnis +" option for user.roleId == 1 // Only add "Buat Bisnis +" option for user.roleId == 1
...(user.roleId == 1 ? [["Buat Bisnis +", -1]] : []) ...(user.roleId == 1 ? [["Buat Bisnis +", -1]] : [])
]; ];
@@ -411,10 +411,10 @@ const App = ({ forCafe = true, cafeId = -1,
console.log(analytics) console.log(analytics)
if (user && user.roleId === 0 && analytics) { if (user && user.roleId === 0 && analytics) {
// Filter the analytics items based on userId // Filter the analytics items based on user_id
if(selectedItem[1] != 0 && selectedItem[1] != -1){ if(selectedItem[1] != 0 && selectedItem[1] != -1){
const filteredData = analytics.items.filter( const filteredData = analytics.items.filter(
(data) => data.userId === nextSelectedId (data) => data.user_id === nextSelectedId
); );
// Extract coupons from the filtered data // Extract coupons from the filtered data

View File

@@ -11,7 +11,7 @@ import { getTables } from "../helpers/tableHelper";
import TableCanvas from "../components/TableCanvas"; import TableCanvas from "../components/TableCanvas";
import { useSearchParams } from "react-router-dom"; import { useSearchParams } from "react-router-dom";
export default function Transactions({ propsShopId, sendParam, deviceType, handleMoveToTransaction, depth, shopImg }) { export default function Transactions({ propsShopId, sendParam, deviceType, handleMoveToTransaction, depth, shopImg, setModal }) {
const { shopId, tableId } = useParams(); const { shopId, tableId } = useParams();
if (sendParam) sendParam({ shopId, tableId }); if (sendParam) sendParam({ shopId, tableId });
@@ -231,13 +231,29 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
</div> </div>
))} ))}
</div> </div>
{!transaction.is_paid && transaction.confirmed > -1 &&
<div
onClick={() => {
localStorage.setItem('lastTransaction', JSON.stringify(transaction));
setModal("message", { captMessage: 'Silahkan tambahkan pesanan', descMessage: 'Pembayaran akan ditambahkan ke transaksi sebelumnya.' }, null, null);
// Dispatch the custom event
window.dispatchEvent(new Event("localStorageUpdated"));
}}
className={styles["addNewItem"]}
>
Tambah pesanan
</div>
}
<h2 className={styles["Transactions-detail"]}> <h2 className={styles["Transactions-detail"]}>
{transaction.serving_type === "pickup" {transaction.serving_type === "pickup"
? "Ambil sendiri" ? "Ambil sendiri"
: `Diantar ke ${transaction.Table ? transaction.Table.tableNo : "N/A" : `Diantar ke ${transaction.Table ? transaction.Table.tableNo : "N/A"
}`} }`}
</h2> </h2>
{transaction.notes != null && ( {transaction.notes != '' && (
<> <>
<div className={styles.NoteContainer}> <div className={styles.NoteContainer}>
<span>Note :</span> <span>Note :</span>

View File

@@ -192,7 +192,7 @@ export default function Transactions({
))} ))}
</ul> </ul>
{transaction.payment_type != 'paylater/cash' && transaction.payment_type != 'paylater/cashless' && {(transaction.payment_type != 'paylater/cash' && transaction.payment_type != 'paylater/cashless') &&
<div <div
onClick={() => { onClick={() => {
localStorage.setItem('lastTransaction', JSON.stringify(transaction)); localStorage.setItem('lastTransaction', JSON.stringify(transaction));

View File

@@ -11,13 +11,14 @@ import {
import dayjs from "dayjs"; import dayjs from "dayjs";
import utc from "dayjs/plugin/utc"; import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone"; import timezone from "dayjs/plugin/timezone";
import { ThreeDots } from "react-loader-spinner";
import ButtonWithReplica from "../components/ButtonWithReplica"; import ButtonWithReplica from "../components/ButtonWithReplica";
dayjs.extend(utc); dayjs.extend(utc);
dayjs.extend(timezone); dayjs.extend(timezone);
export default function Transactions({ shop, shopId, propsShopId, sendParam, deviceType, paymentUrl }) { export default function Transactions({ shop, shopId, propsShopId, sendParam, deviceType, paymentUrl, setModal, newTransaction }) {
const { shopIdentifier, tableId } = useParams(); const { shopIdentifier, tableId } = useParams();
if (sendParam) sendParam({ shopIdentifier, tableId }); if (sendParam) sendParam({ shopIdentifier, tableId });
@@ -27,33 +28,40 @@ export default function Transactions({ shop, shopId, propsShopId, sendParam, dev
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [matchedItems, setMatchedItems] = useState([]); const [matchedItems, setMatchedItems] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => { useEffect(() => {
setMatchedItems(searchAndAggregateItems(transactions, searchTerm)); setMatchedItems(searchAndAggregateItems(transactions, searchTerm));
}, [searchTerm, transactions]); }, [searchTerm, transactions]);
useEffect(() => { useEffect(() => {
const fetchTransactions = async () => { const fetchTransactions = async () => {
try { try {
let response = await getTransactionsFromCafe(shopId || propsShopId, 5, false);
console.log(response) // response = await getMyTransactions(shopId || propsShopId, 5);
// setMyTransactions(response);
setLoading(true);
let response = await getTransactionsFromCafe(shopId || propsShopId, -1, false);
setLoading(false);
if (response) setTransactions(response); if (response) setTransactions(response);
} catch (error) { } catch (error) {
console.error("Error fetching transactions:", error); console.error("Error fetching transactions:", error);
} }
}; };
console.log(deviceType)
fetchTransactions(); fetchTransactions();
}, [deviceType]); }, [deviceType, newTransaction]);
const calculateTotalPrice = (detailedTransactions) => { const calculateTotalPrice = (detailedTransactions) => {
return detailedTransactions.reduce((total, dt) => { return detailedTransactions.reduce((total, dt) => {
return total + dt.qty * (dt.promoPrice ? dt.promoPrice : dt.price); return total + dt.qty * (dt.promoPrice ? dt.promoPrice : dt.price);
}, 0); }, 0);
}; };
const calculateAllTransactionsTotal = (transactions) => { const calculateAllTransactionsTotal = (transactions) => {
return transactions.reduce((grandTotal, transaction) => { return transactions
.filter(transaction => transaction.confirmed > 1) // Filter transactions where confirmed > 1
.reduce((grandTotal, transaction) => {
return grandTotal + calculateTotalPrice(transaction.DetailedTransactions); return grandTotal + calculateTotalPrice(transaction.DetailedTransactions);
}, 0); }, 0);
}; };
@@ -61,15 +69,20 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
if (!searchTerm.trim()) return []; if (!searchTerm.trim()) return [];
const normalizedTerm = searchTerm.trim().toLowerCase(); const normalizedTerm = searchTerm.trim().toLowerCase();
// Map with key = `${itemId}-${confirmedGroup}` to keep confirmed groups separate
const aggregatedItems = new Map(); const aggregatedItems = new Map();
transactions.forEach(transaction => { transactions.forEach(transaction => {
// Determine confirmed group as a string key
const confirmedGroup = transaction.confirmed >= 0 && transaction.confirmed > 1 ? 'confirmed_gt_1' : 'confirmed_le_1';
transaction.DetailedTransactions.forEach(detail => { transaction.DetailedTransactions.forEach(detail => {
const itemName = detail.Item.name; const itemName = detail.Item.name;
const itemNameLower = itemName.toLowerCase(); const itemNameLower = itemName.toLowerCase();
if (itemNameLower.includes(normalizedTerm)) { if (itemNameLower.includes(normalizedTerm)) {
const key = detail.itemId; // Combine itemId and confirmedGroup to keep them separated
const key = `${detail.itemId}-${confirmedGroup}`;
if (!aggregatedItems.has(key)) { if (!aggregatedItems.has(key)) {
aggregatedItems.set(key, { aggregatedItems.set(key, {
@@ -77,6 +90,7 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
name: itemName, name: itemName,
totalQty: 0, totalQty: 0,
totalPrice: 0, totalPrice: 0,
confirmedGroup, // Keep track of which group this belongs to
}); });
} }
@@ -86,11 +100,12 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
} }
}); });
}); });
console.log(aggregatedItems.values())
return Array.from(aggregatedItems.values()); return Array.from(aggregatedItems.values());
}; };
const handleConfirm = async (transactionId) => { const handleConfirm = async (transactionId) => {
setIsPaymentLoading(true); setIsPaymentLoading(true);
try { try {
@@ -98,7 +113,7 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
if (result) { if (result) {
setTransactions(prev => setTransactions(prev =>
prev.map(t => prev.map(t =>
t.transactionId === transactionId ? { ...t, confirmed: 1 } : t t.transactionId === transactionId ? result : t
) )
); );
} }
@@ -127,10 +142,19 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
} }
}; };
if (loading)
return (
<div className="Loader">
<div className="LoaderChild">
<ThreeDots />
<h1></h1>
</div>
</div>
);
return ( return (
<div className={styles.Transactions}> <div className={styles.Transactions}>
<h2 className={styles["Transactions-title"]}> <h2 className={styles["Transactions-title"]}>
Daftar transaksi Rp {calculateAllTransactionsTotal(transactions)} Transaksi selesai Rp {calculateAllTransactionsTotal(transactions)}
</h2> </h2>
<input <input
@@ -148,7 +172,7 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
{matchedItems.length > 0 && matchedItems.map(item => ( {matchedItems.length > 0 && matchedItems.map(item => (
<div <div
key={item.itemId} key={`${item.itemId}-${item.confirmedGroup}`}
className={styles.RoundedRectangle} className={styles.RoundedRectangle}
style={{ overflow: "hidden" }} style={{ overflow: "hidden" }}
> >
@@ -174,7 +198,7 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
<div className={styles['receipt-header']}> <div className={styles['receipt-header']}>
{transaction.confirmed === 1 ? ( {transaction.confirmed === 1 ? (
<ColorRing className={styles['receipt-logo']} /> <ColorRing className={styles['receipt-logo']} />
) : transaction.confirmed === -1 || transaction.confirmed === -2 ? ( ) : transaction.confirmed === -1 && !transaction.is_paid || transaction.confirmed === -2 && !transaction.is_paid ? (
<div style={{ display: 'flex', justifyContent: 'center', margin: '16px 0px' }}> <div style={{ display: 'flex', justifyContent: 'center', margin: '16px 0px' }}>
<svg <svg
style={{ width: '60px', transform: 'Rotate(45deg)' }} style={{ width: '60px', transform: 'Rotate(45deg)' }}
@@ -191,9 +215,9 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
/> />
</svg> </svg>
</div> </div>
) : transaction.confirmed === 2 ? ( ) : transaction.confirmed === 2 && !transaction.is_paid ? (
<ColorRing className={styles['receipt-logo']} /> <ColorRing className={styles['receipt-logo']} />
) : transaction.confirmed === 3 ? ( ) : transaction.confirmed === 3 || transaction.is_paid ? (
<div> <div>
<svg <svg
height="60px" height="60px"
@@ -228,15 +252,15 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
<div className={styles['receipt-info']}> <div className={styles['receipt-info']}>
{deviceType == 'clerk' ? {deviceType == 'clerk' ?
<h3>{transaction.confirmed === 1 ? ( <h3>{transaction.confirmed === 1 && !transaction.is_paid ? (
"Silahkan Cek Pembayaran" "Silahkan Cek Pembayaran"
) : transaction.confirmed === -1 ? ( ) : transaction.confirmed === -1 && !transaction.is_paid ? (
"Dibatalkan Oleh Kasir" "Dibatalkan Oleh Kasir"
) : transaction.confirmed === -2 ? ( ) : transaction.confirmed === -2 && !transaction.is_paid ? (
"Dibatalkan Oleh Pelanggan" "Dibatalkan Oleh Pelanggan"
) : transaction.confirmed === 2 ? ( ) : transaction.confirmed === 2 && !transaction.is_paid ? (
"Sedang Diproses" "Sedang Diproses"
) : transaction.confirmed === 3 ? ( ) : transaction.confirmed === 3 || transaction.is_paid ? (
"Transaksi Sukses" "Transaksi Sukses"
) : ( ) : (
"Silahkan Cek Ketersediaan" "Silahkan Cek Ketersediaan"
@@ -285,6 +309,20 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
</li> </li>
))} ))}
</ul> </ul>
{!transaction.is_paid && transaction.confirmed > -1 &&
<div
onClick={() => {
localStorage.setItem('lastTransaction', JSON.stringify(transaction));
setModal("message", { captMessage: 'Silahkan tambahkan pesanan', descMessage: 'Pembayaran akan ditambahkan ke transaksi sebelumnya.' }, null, null);
// Dispatch the custom event
window.dispatchEvent(new Event("localStorageUpdated"));
}}
className={styles["addNewItem"]}
>
Tambah pesanan
</div>
}
<h2 className={styles["Transactions-detail"]}> <h2 className={styles["Transactions-detail"]}>
{transaction.serving_type === "pickup" {transaction.serving_type === "pickup"
? "Self pickup" ? "Self pickup"
@@ -317,7 +355,7 @@ const searchAndAggregateItems = (transactions, searchTerm) => {
</div> </div>
<div className={styles.TotalContainer}> <div className={styles.TotalContainer}>
{(deviceType == 'clerk' && (transaction.confirmed == 0 || transaction.confirmed == 1 || transaction.confirmed == 2)) && {(deviceType == 'clerk' && !transaction.is_paid && (transaction.confirmed == 0 || transaction.confirmed == 1 || transaction.confirmed == 2)) &&
<button <button
className={styles.PayButton} className={styles.PayButton}
onClick={() => handleConfirm(transaction.transactionId)} onClick={() => handleConfirm(transaction.transactionId)}