diff --git a/src/App.js b/src/App.js index 9e0e3ed..828008a 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef } from "react"; import "./App.css"; import "./components/Loading.css"; import { @@ -26,6 +26,9 @@ import GuestSide from "./pages/GuestSide"; import { getItemTypesWithItems } from "./helpers/itemHelper.js"; import { getTableByCode } from "./helpers/tableHelper.js"; +import { + getTransactionsFromCafe, +} from "./helpers/transactionHelpers"; import { getConnectedGuestSides, getClerks, @@ -57,8 +60,21 @@ function App() { const [shopItems, setShopItems] = useState([]); const [isModalOpen, setIsModalOpen] = useState(false); const [modalContent, setModalContent] = useState(null); + const transactionList = useRef(null); const [queue, setQueue] = useState([]); + + const validTransactionStates = [ + 'new_transaction', + 'transaction_canceled', + 'transaction_pending', + 'transaction_confirmed', + 'payment_claimed', + 'transaction_success', + 'transaction_end', + 'transaction_failed', + ]; + useEffect(() => { const calculateTotalsFromLocalStorage = () => { const { totalCount } = calculateTotals(shopId); @@ -193,30 +209,16 @@ function App() { setModal("transaction_canceled", data); }); - //for clerk - socket.on("transaction_created", async (data) => { - console.log("transaction notification"); - setModal("new_transaction", data); - - let permission = Notification.permission; - if (permission != "granted") return; - navigator.serviceWorker.ready.then((registration) => { - registration.showNotification("New Transaction", { - body: `A new transaction was created: ${data.transactionDetails}`, // Customize as needed - icon: "icon.png", // Optional icon - }); - }); - }); const checkNotifications = () => { let permission = Notification.permission; - + // Check current permission if (permission !== "granted") { setModal("req_notification"); } }; - + socket.on("checkUserTokenRes", async (data) => { if (data.status !== 200) { removeLocalStorage("auth"); @@ -266,15 +268,85 @@ function App() { }); socket.on("updateQueue", (response) => { - setQueue(response); // Only set the queue if it's a valid non-empty array - console.log("Updated Queue:", response); // Log the valid queue - + setQueue(response); // Only set the queue if it's a valid non-empty array + console.log("Updated Queue:", response); // Log the valid queue + }); return () => { socket.off("signout-guest-session"); }; }, [socket, shopId]); + useEffect(() => { + // This will ensure that searchParams and transaction_info get updated on each render + socket.on("transaction_created", async (data) => { + console.log("transaction notification"); + console.log(modalContent); + + let response; + response = await getTransactionsFromCafe(shopId, 0, true); + console.log(data); + transactionList.current = response; + + // Get current URL's search parameters inside the socket event handler + const searchParams = new URLSearchParams(location.search); + let transaction_info = searchParams.get("transactionId") || ''; // Get transactionId or set it to empty string + console.log(transaction_info); // Log the updated transaction_info + + // If transaction_info is an empty string, set the modal + if (transaction_info === '') { + setModal("new_transaction", data); + } + + // Show browser notification + let permission = Notification.permission; + if (permission !== "granted") return; + navigator.serviceWorker.ready.then((registration) => { + registration.showNotification("New Transaction", { + body: `A new transaction was created: ${data.transactionDetails}`, + icon: "icon.png", // Optional icon + }); + }); + }); + + // Clean up the socket event listener on unmount or when dependencies change + return () => { + socket.off("transaction_created"); + }; + }, [socket, shopId, location]); // Ensure location is in the dependencies to respond to changes in the URL + + function handleMoveToTransaction(direction, from) { + console.log(direction); + console.log(from); + + // Find the current index based on the 'from' transactionId + const currentIndex = transactionList.current.findIndex(item => item.transactionId == from); + + // Determine the new transactionId based on the direction + let newTransactionId; + if (direction === 'next') { + // If we're not at the last transaction, get the next transactionId + newTransactionId = currentIndex < transactionList.current.length - 1 + ? transactionList.current[currentIndex + 1].transactionId + : from; // If already at the end, stay on the current transactionId + } else if (direction === 'previous') { + // If we're not at the first transaction, get the previous transactionId + newTransactionId = currentIndex > 0 + ? transactionList.current[currentIndex - 1].transactionId + : from; // If already at the beginning, stay on the current transactionId + } + + // Log the new transactionId + console.log('New Transaction ID:', newTransactionId); + + // Update the URL with the new transactionId using navigate + navigate(`?transactionId=${newTransactionId}`, { replace: true }); + + // Optionally, update state or perform further actions based on the new transactionId + // Example: + // setModalContent({ cafeId: shopId, transactionId: newTransactionId }); + } + const handleModalFromURL = () => { const queryParams = new URLSearchParams(location.search); @@ -307,7 +379,7 @@ function App() { document.body.style.overflow = "hidden"; setIsModalOpen(true); - setModalContent(content); + setModalContent(content) }; // Function to close the modal @@ -318,6 +390,7 @@ function App() { closeTheseContent.includes(modalContent)) ) { setIsModalOpen(false); + setModalContent(null); document.body.style.overflow = "auto"; const queryParams = new URLSearchParams(location.search); @@ -334,15 +407,15 @@ function App() { // useEffect(() => { // const askNotificationPermission = async () => { // let permission = Notification.permission; - + // // Check current permission // if (permission === "default") { // setModal("req_notification"); - + // // Request permission and wait for the result // permission = await Notification.requestPermission(); // } - + // // Handle permission results // if (permission === "granted") { // await resetNotificationSubscription(); @@ -352,22 +425,22 @@ function App() { // console.error("Notification permission denied."); // } // }; - + // const handleTransactionConfirmed = async (data) => { // console.log("transaction notification", data); // await askNotificationPermission(); // setModal("transaction_success", data); // }; - + // // Add socket listener for transaction confirmations // socket.on("transaction_success", handleTransactionConfirmed); - + // // Cleanup the socket listener on component unmount // return () => { // socket.off("transaction_success", handleTransactionConfirmed); // }; // }, [user]); - + useEffect(() => { const startPermissionPolling = async () => { const checkInterval = 5000; // Check every 5 seconds @@ -386,7 +459,7 @@ function App() { startPermissionPolling(); }, []); - + useEffect(() => { if ("serviceWorker" in navigator) { navigator.serviceWorker.register("/service-worker.js") @@ -398,7 +471,7 @@ function App() { }); } }, []); - + return (
Transaction ID: {transaction.transactionId}
+Payment Type: {transaction.payment_type}
+Transaction ID: {transaction.transactionId}
+Payment Type: {transaction.payment_type}
+tunggu konfirmasi
} -Bayar nanti
} > ) : isPaymentOpen ? ( "Claim has paid" // Display "Confirm has paid" if the transaction is confirmed (1) diff --git a/src/pages/Transaction_pending.js b/src/pages/Transaction_pending.js index 954acbc..00d2b98 100644 --- a/src/pages/Transaction_pending.js +++ b/src/pages/Transaction_pending.js @@ -1,35 +1,207 @@ -import React from "react"; -import { ColorRing } from "react-loader-spinner"; +import React, { useRef, useEffect, useState } from "react"; import styles from "./Transactions.module.css"; +import { useParams } from "react-router-dom"; +import { ColorRing } from "react-loader-spinner"; +import { + getTransaction, + confirmTransaction, + declineTransaction, +} from "../helpers/transactionHelpers"; +import { getTables } from "../helpers/tableHelper"; +import TableCanvas from "../components/TableCanvas"; +import { useSearchParams } from "react-router-dom"; -export default function Transaction_pending() { - const containerStyle = { - display: "flex", - justifyContent: "center", - alignItems: "center", - width: "100%", - height: "100%", // This makes the container stretch to the bottom of the viewport - backgroundColor: "#000", // Optional: Set a background color if you want to see the color ring clearly +export default function Transactions({ propsShopId, sendParam, deviceType, handleMoveToTransaction, shopImg }) { + const { shopId, tableId } = useParams(); + if (sendParam) sendParam({ shopId, tableId }); + + const [tables, setTables] = useState([]); + const [selectedTable, setSelectedTable] = useState(null); + const [isPaymentLoading, setIsPaymentLoading] = useState(false); + const [searchParams] = useSearchParams(); + const [transaction, setTransaction] = useState(null); + const noteRef = useRef(null); + + useEffect(() => { + const transactionId = searchParams.get("transactionId") || ""; + + const fetchData = async () => { + try { + const fetchedTransaction = await getTransaction(transactionId); + setTransaction(fetchedTransaction); + console.log(transaction); + } catch (error) { + console.error("Error fetching transaction:", error); + } + }; + fetchData(); + }, [searchParams]); + + useEffect(() => { + const fetchData = async () => { + try { + const fetchedTables = await getTables(shopId || propsShopId); + setTables(fetchedTables); + } catch (error) { + console.error("Error fetching tables:", error); + } + }; + + fetchData(); + }, [shopId || propsShopId]); + + const calculateTotalPrice = (detailedTransactions) => { + return detailedTransactions.reduce((total, dt) => { + return total + dt.qty * dt.Item.price; + }, 0); }; + const handleConfirm = async (transactionId) => { + if (isPaymentLoading) return; + setIsPaymentLoading(true); + try { + const c = await confirmTransaction(transactionId); + if (c) { + setTransaction({ ...transaction, confirmed: c.confirmed }); + } + } catch (error) { + console.error("Error processing payment:", error); + } finally { + setIsPaymentLoading(false); + } + }; + + const handleDecline = async (transactionId) => { + if (isPaymentLoading) return; + setIsPaymentLoading(true); + try { + const c = await declineTransaction(transactionId); + // if (c) { + // // Update the confirmed status locally + // setTransactions((prevTransactions) => + // prevTransactions.map((transaction) => + // transaction.transactionId === transactionId + // ? { ...transaction, confirmed: -1 } // Set to confirmed + // : transaction + // ) + // ); + // } + } catch (error) { + console.error("Error processing payment:", error); + } finally { + setIsPaymentLoading(false); + } + }; + + const autoResizeTextArea = (textarea) => { + if (textarea) { + textarea.style.height = "auto"; // Reset height + textarea.style.height = `${textarea.scrollHeight}px`; // Set new height + } + }; + + useEffect(() => { + if (noteRef.current) { + autoResizeTextArea(noteRef.current); + } + }, [transaction?.notes]); + return (Waiting for clerk confirmation...
+ +Transaction ID: {transaction.transactionId}
+Payment Type: {transaction.payment_type}
+