This commit is contained in:
client perkafean
2024-09-30 09:44:24 +00:00
parent d84e47bba9
commit 088b63a4a2
12 changed files with 352 additions and 65 deletions

View File

@@ -58,6 +58,7 @@ function App() {
const [shopItems, setShopItems] = useState([]); const [shopItems, setShopItems] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [modalContent, setModalContent] = useState(null); const [modalContent, setModalContent] = useState(null);
const [nextModalContent, setNextModalContent] = useState(null);
useEffect(() => { useEffect(() => {
const calculateTotalsFromLocalStorage = () => { const calculateTotalsFromLocalStorage = () => {
@@ -115,9 +116,9 @@ function App() {
items: cafe.items.filter((item) => items: cafe.items.filter((item) =>
filteredData.some((filtered) => filteredData.some((filtered) =>
filtered.itemList.some( filtered.itemList.some(
(i) => i.itemId === item.itemId && i.availability, (i) => i.itemId === item.itemId && i.availability
), )
), )
), ),
}; };
} }
@@ -221,13 +222,11 @@ function App() {
}); });
socket.on("checkUserTokenRes", async (data) => { socket.on("checkUserTokenRes", async (data) => {
console.log(data)
if (data.status !== 200) { if (data.status !== 200) {
removeLocalStorage("auth"); removeLocalStorage("auth");
setDeviceType("guestDevice"); setDeviceType("guestDevice");
} else { } else {
setUser(data.data.user); setUser(data.data.user);
console.log('setting user')
if ( if (
data.data.user.password == "unsetunsetunset" && data.data.user.password == "unsetunsetunset" &&
localStorage.getItem("settings") localStorage.getItem("settings")
@@ -290,7 +289,10 @@ function App() {
// Function to open the modal // Function to open the modal
const setModal = (content, params = {}) => { const setModal = (content, params = {}) => {
// Prepare query parameters if (modalContent) {
setNextModalContent(content);
return;
} // Prepare query parameters
const queryParams = new URLSearchParams(location.search); const queryParams = new URLSearchParams(location.search);
// Update the modal and any additional params // Update the modal and any additional params
@@ -312,8 +314,9 @@ function App() {
// Function to close the modal // Function to close the modal
const closeModal = (closeTheseContent = []) => { const closeModal = (closeTheseContent = []) => {
if ( if (
closeTheseContent.length === 0 || Array.isArray(closeTheseContent) &&
closeTheseContent.includes(modalContent) (closeTheseContent.length === 0 ||
closeTheseContent.includes(modalContent))
) { ) {
setIsModalOpen(false); setIsModalOpen(false);
document.body.style.overflow = "auto"; document.body.style.overflow = "auto";
@@ -322,9 +325,13 @@ function App() {
// Remove the 'modal' parameter // Remove the 'modal' parameter
queryParams.delete("modal"); queryParams.delete("modal");
queryParams.delete("transactionId");
// Update the URL without the 'modal' parameter // Update the URL without the 'modal' parameter
navigate({ search: queryParams.toString() }, { replace: true }); navigate({ search: queryParams.toString() }, { replace: true });
if (nextModalContent) setModal(nextModalContent);
setNextModalContent("");
} }
}; };
@@ -366,11 +373,9 @@ function App() {
} }
}; };
const handleLoad = async () => { const handleLoad = async () => {
while (modalContent !== "transaction_pending" || modalContent !== "transaction_confirmed") {
if (user != null && (user.roleId < 3 || user.roleId > 2)) { if (user != null && (user.roleId < 3 || user.roleId > 2)) {
await askNotificationPermission(); await askNotificationPermission();
} }
}
}; };
handleLoad(); handleLoad();
if ("serviceWorker" in navigator) { if ("serviceWorker" in navigator) {

View File

@@ -7,7 +7,7 @@
.button { .button {
position: relative; position: relative;
z-index: 99; /* Make sure the button is above the replica */ z-index: 201; /* Make sure the button is above the replica */
font-family: "Poppins", sans-serif; font-family: "Poppins", sans-serif;
font-weight: 500; font-weight: 500;
@@ -47,4 +47,91 @@
z-index: 200; z-index: 200;
border-radius: 0px; border-radius: 0px;
background-color: white; background-color: white;
pointer-events: none; /* Allow interactions */
}
.bussinessName {
position: absolute;
top: 50%;
left: 50%;
color: rgba(0, 0, 0, 0);
transform: translate(-50%, -50%);
z-index: 98; /* Behind the button */
transition: all 0.5s ease-in-out;
font-size: 3vw;
text-align: center;
}
.bussinessName.active {
top: -700%;
position: absolute;
color: rgb(0, 0, 0);
width: 60vw;
right: 50%;
z-index: 201; /* Behind the button */
font-size: 3vw;
text-align: center;
}
.bussinessQR {
position: absolute;
height: 25vh;
width: 25vh;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 98; /* Behind the button */
transition: all 0.5s ease-in-out;
}
.bussinessQR.active {
position: absolute;
top: -250%;
transform: translate(-50%, -80%);
transition: all 0.5s ease-in-out;
z-index: 201; /* Behind the button */
}
.price {
position: absolute;
top: 50%;
left: 50%;
color: rgba(0, 0, 0, 0);
transform: translate(-50%, -50%);
z-index: 98; /* Behind the button */
transition: all 0.5s ease-in-out;
font-size: 3vw;
text-align: center;
}
.price.active {
position: absolute;
color: rgb(0, 0, 0);
transform: translate(-50%, -150%);
z-index: 201; /* Behind the button */
font-size: 3vw;
text-align: center;
}
.ClaimButton {
visibility: hidden;
position: absolute;
}
.ClaimButton.active {
visibility: visible;
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 70%; /* Adjusted for better readability */
padding: 12px 24px; /* Added padding for a better look */
border-radius: 50px;
background-color: rgba(88, 55, 50, 1);
color: white;
border: none;
margin: 0 auto;
cursor: pointer;
display: block; /* Centering the button */
text-align: center;
z-index: 201;
} }

View File

@@ -1,22 +1,153 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import "./ButtonWithReplica.css"; import "./ButtonWithReplica.css";
import styles from "../pages/Transactions.module.css";
import { QRCodeSVG } from "qrcode.react";
import API_BASE_URL from "../config.js";
import jsqr from "jsqr";
const ButtonWithReplica = ({ children }) => { const ButtonWithReplica = ({
children,
price,
paymentUrl,
handleClick,
Open,
isPaymentOpen,
}) => {
const [isActive, setIsActive] = useState(false); const [isActive, setIsActive] = useState(false);
const [fgColor, setFgColor] = useState("transparent");
const [QRValue, setQRValue] = useState("");
const [qrisComponent, setQrisComponent] = useState({
merchantName: "",
nmid: "",
});
useEffect(() => {
const decodeQRCodeFromUrl = async (imageUrl) => {
const img = new Image();
img.crossOrigin = "Anonymous"; // Handle CORS if needed
img.src = API_BASE_URL + "/" + imageUrl;
const handleClick = () => { return new Promise((resolve, reject) => {
img.onload = () => {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0);
const imageData = context.getImageData(
0,
0,
canvas.width,
canvas.height
);
const code = jsqr(imageData.data, canvas.width, canvas.height);
if (code) {
resolve(code.data); // This is your QRValue
} else {
reject("No QR code found.");
}
};
img.onerror = (error) => {
reject("Failed to load image: " + error);
};
});
};
const fetchQRCodeValue = async () => {
if (paymentUrl) {
try {
console.log(paymentUrl);
const qrv = await decodeQRCodeFromUrl(paymentUrl);
setQRValue(qrv);
} catch (error) {
console.error(error);
}
}
};
fetchQRCodeValue();
}, [paymentUrl]); // Run effect when paymentUrl changes
useEffect(() => {
function extractMerchantName(qrisCode) {
const tagPrefix = "ID59";
const startIndex = qrisCode.indexOf(tagPrefix);
if (startIndex !== -1) {
const lengthIndex = startIndex + tagPrefix.length;
const nameLength = parseInt(qrisCode.substr(lengthIndex, 2), 10); // Length of the name
const merchantNameStart = lengthIndex + 2; // Move past the length characters
const merchantNameEnd = merchantNameStart + nameLength; // Calculate the end index
const merchantName = qrisCode.substr(merchantNameStart, nameLength);
return merchantName;
}
return null; // Return null if the tag is not found
}
function extractNMID(qrisCode) {
const startTag = "WWW0215";
const endTag = "0303";
const startIndex = qrisCode.indexOf(startTag);
if (startIndex !== -1) {
const nmStartIndex = startIndex + startTag.length; // Start after WWW0215
const endIndex = qrisCode.indexOf(endTag, nmStartIndex); // Find the next 0303
if (endIndex !== -1) {
const nmid = qrisCode.substring(nmStartIndex, endIndex);
return nmid; // This will include the ID prefix
}
}
return null; // Return null if NMID is not found
}
const parsedMerchantName = extractMerchantName(QRValue);
const parsedNMID = extractNMID(QRValue);
setQrisComponent({ merchantName: parsedMerchantName, nmid: parsedNMID });
console.log("Parsed Merchant Name:", parsedMerchantName);
console.log("Parsed NMID:", parsedNMID);
}, [QRValue]);
useEffect(() => {
setIsActive(isPaymentOpen);
if (isPaymentOpen == false) setFgColor("transparent"); // Change color to black on click
}, [isPaymentOpen]);
const handleOpen = () => {
setIsActive(true); setIsActive(true);
setTimeout(() => { setFgColor("black"); // Change color to black on click
// setIsActive(false); console.log(qrisComponent.nmid); // Log the QR value
}, 10000); // Duration of the animation Open();
}; };
return ( return (
<div className="container"> <div className="container">
<button className="button" onClick={handleClick}> <button
className="button"
onClick={() => (isPaymentOpen ? handleClick() : handleOpen())}
>
{children} {children}
</button> </button>
<div className={`replica ${isActive ? "active" : ""}`}></div> <div className={`replica ${isActive ? "active" : ""}`}></div>
<QRCodeSVG
className={`bussinessQR ${isActive ? "active" : ""}`}
bgColor={"transparent"}
fgColor={fgColor}
value={QRValue}
/>
<div className={`bussinessName ${isActive ? "active" : ""}`}>
<h2>{qrisComponent.merchantName}</h2>
{qrisComponent.nmid != "" && <h4>NMID : {qrisComponent.nmid}</h4>}
</div>
<div className={`price ${isActive ? "active" : ""}`}>
<h1>{price}</h1>
</div>
</div> </div>
); );
}; };

View File

@@ -29,11 +29,10 @@ const Modal = ({ shop, isOpen, onClose, modalContent }) => {
// Prevent click event from propagating to the overlay // Prevent click event from propagating to the overlay
event.stopPropagation(); event.stopPropagation();
}; };
console.log(shop.qrPayment);
return ( return (
<div onClick={handleOverlayClick} className={styles.modalOverlay}> <div onClick={handleOverlayClick} className={styles.modalOverlay}>
<div className={styles.modalContent} onClick={handleContentClick}> <div className={styles.modalContent} onClick={handleContentClick}>
<button onClick={onClose} className={styles.closeButton}> <button onClick={() => onClose()} className={styles.closeButton}>
&times; &times;
</button> </button>
{modalContent === "req_notification" && <NotificationBlocked />} {modalContent === "req_notification" && <NotificationBlocked />}

View File

@@ -24,7 +24,7 @@
/* Center the background image */ /* Center the background image */
filter: blur(1.5px); filter: blur(1.5px);
-webkit-filter: blur(1.5px); -webkit-filter: blur(1.5px);
border-radius: 15px 15px 0 0; border-radius: 23px 23px 0 0;
background-color: rgb(95 121 89); background-color: rgb(95 121 89);
/* Rounded corners at the top */ /* Rounded corners at the top */
text-align: right; text-align: right;
@@ -106,7 +106,7 @@
right: 0; right: 0;
background-color: rgb(108, 255, 128); background-color: rgb(108, 255, 128);
/* background-color: rgb(218 163 99); */ /* background-color: rgb(218 163 99); */
border-radius: 0 0 15px 15px; border-radius: 0 0 23px 23px;
/* Rounded corners at the bottom */ /* Rounded corners at the bottom */
cursor: pointer; cursor: pointer;
text-align: center; text-align: center;

View File

@@ -1,5 +1,5 @@
// src/config.js // src/config.js
const API_BASE_URL = "https://sswsts-5000.csb.app"; // Replace with your actual backend URL const API_BASE_URL = "https://2jtmkn-5000.csb.app"; // Replace with your actual backend URL
export default API_BASE_URL; export default API_BASE_URL;

View File

@@ -13,7 +13,7 @@ export async function confirmTransaction(transactionId) {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}, }
); );
if (!response.ok) { if (!response.ok) {
return false; return false;
@@ -37,7 +37,7 @@ export async function declineTransaction(transactionId) {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}, }
); );
if (!response.ok) { if (!response.ok) {
@@ -61,7 +61,7 @@ export async function cancelTransaction(transactionId) {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}, }
); );
console.log(response); console.log(response);
@@ -86,14 +86,14 @@ export async function handleClaimHasPaid(transactionId) {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}, }
); );
if (!response.ok) { if (!response.ok) {
return false; return false;
} }
return true; return await response.json();
} catch (error) { } catch (error) {
console.error("Error:", error); console.error("Error:", error);
} }
@@ -111,7 +111,7 @@ export async function handleConfirmHasPaid(transactionId) {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}, }
); );
if (!response.ok) { if (!response.ok) {
@@ -135,7 +135,7 @@ export async function getTransaction(transactionId) {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}, }
); );
if (!response.ok) { if (!response.ok) {
@@ -160,7 +160,7 @@ export async function getTransactions(shopId, demand) {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}, }
); );
if (!response.ok) { if (!response.ok) {
@@ -179,7 +179,7 @@ export const handlePaymentFromClerk = async (
user_email, user_email,
payment_type, payment_type,
serving_type, serving_type,
tableNo, tableNo
) => { ) => {
try { try {
const token = getLocalStorage("auth"); const token = getLocalStorage("auth");
@@ -208,7 +208,7 @@ export const handlePaymentFromClerk = async (
tableNo, tableNo,
transactions: structuredItems, transactions: structuredItems,
}), }),
}, }
); );
if (response.ok) { if (response.ok) {
@@ -233,7 +233,7 @@ export const handlePaymentFromGuestSide = async (
user_email, user_email,
payment_type, payment_type,
serving_type, serving_type,
tableNo, tableNo
) => { ) => {
try { try {
const token = getLocalStorage("authGuestSide"); const token = getLocalStorage("authGuestSide");
@@ -262,7 +262,7 @@ export const handlePaymentFromGuestSide = async (
tableNo, tableNo,
transactions: structuredItems, transactions: structuredItems,
}), }),
}, }
); );
const res = await response.json(); const res = await response.json();
console.log(res); console.log(res);
@@ -289,7 +289,7 @@ export const handlePaymentFromGuestDevice = async (
serving_type, serving_type,
tableNo, tableNo,
notes, notes,
socketId, socketId
) => { ) => {
try { try {
const token = getLocalStorage("auth"); const token = getLocalStorage("auth");
@@ -319,7 +319,7 @@ export const handlePaymentFromGuestDevice = async (
notes: notes, notes: notes,
socketId, socketId,
}), }),
}, }
); );
if (response.ok) { if (response.ok) {
@@ -364,7 +364,7 @@ const getHeaders = (method = "GET") => {
export const getFavourite = async (cafeId) => { export const getFavourite = async (cafeId) => {
const response = await fetch( const response = await fetch(
`${API_BASE_URL}/transaction/get-favourite/${cafeId}`, `${API_BASE_URL}/transaction/get-favourite/${cafeId}`,
getHeaders(), getHeaders()
); );
return response.json(); return response.json();
}; };
@@ -377,7 +377,7 @@ export const getAnalytics = async (cafeId, filter) => {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}, }
); );
return response.json(); return response.json();
}; };
@@ -385,7 +385,7 @@ export const getAnalytics = async (cafeId, filter) => {
export const getIncome = async (cafeId) => { export const getIncome = async (cafeId) => {
const response = await fetch( const response = await fetch(
`${API_BASE_URL}/transaction/get-income/${cafeId}`, `${API_BASE_URL}/transaction/get-income/${cafeId}`,
getHeaders(), getHeaders()
); );
return response.json(); return response.json();
}; };

View File

@@ -106,6 +106,12 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
setSelectedTable(transaction.Table || { tableId: 0 }) setSelectedTable(transaction.Table || { tableId: 0 })
} }
> >
{transaction.paymentClaimed && transaction.confirmed < 2 && (
<div className={styles.RibbonBanner}>
<img src={"https://i.imgur.com/yt6osgL.png"}></img>
<h1>payment claimed</h1>
</div>
)}
<h2 className={styles["Transactions-detail"]}> <h2 className={styles["Transactions-detail"]}>
Transaction ID: {transaction.transactionId} Transaction ID: {transaction.transactionId}
</h2> </h2>
@@ -127,7 +133,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
transaction.Table ? transaction.Table.tableNo : "N/A" 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

@@ -169,7 +169,11 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
<button <button
className={styles.PayButton} className={styles.PayButton}
onClick={() => handleConfirm(transaction.transactionId)} onClick={() => handleConfirm(transaction.transactionId)}
disabled={isPaymentLoading} // Disable button if confirmed (1) or declined (-1) or loading disabled={
transaction.confirmed === -1 ||
transaction.confirmed === 3 ||
isPaymentLoading
} // Disable button if confirmed (1) or declined (-1) or
> >
{isPaymentLoading ? ( {isPaymentLoading ? (
<ColorRing height="50" width="50" color="white" /> <ColorRing height="50" width="50" color="white" />

View File

@@ -14,7 +14,12 @@ 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 }) { export default function Transactions({
propsShopId,
sendParam,
deviceType,
paymentUrl,
}) {
const { shopId, tableId } = useParams(); const { shopId, tableId } = useParams();
if (sendParam) sendParam({ shopId, tableId }); if (sendParam) sendParam({ shopId, tableId });
@@ -24,6 +29,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const [transaction, setTransaction] = useState(null); const [transaction, setTransaction] = useState(null);
const noteRef = useRef(null); const noteRef = useRef(null);
const [isPaymentOpen, setIsPaymentOpen] = useState(false);
useEffect(() => { useEffect(() => {
const transactionId = searchParams.get("transactionId") || ""; const transactionId = searchParams.get("transactionId") || "";
@@ -63,9 +69,14 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
if (isPaymentLoading) return; if (isPaymentLoading) return;
setIsPaymentLoading(true); setIsPaymentLoading(true);
try { try {
const c = await confirmTransaction(transactionId); const c = await handleClaimHasPaid(transactionId);
if (c) { if (c) {
setTransaction({ ...transaction, confirmed: c.confirmed }); setTransaction({
...transaction,
confirmed: c.confirmed,
paymentClaimed: true,
});
console.log(transaction);
} }
} catch (error) { } catch (error) {
console.error("Error processing payment:", error); console.error("Error processing payment:", error);
@@ -112,7 +123,14 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
onClick={() => onClick={() =>
setSelectedTable(transaction.Table || { tableId: 0 }) setSelectedTable(transaction.Table || { tableId: 0 })
} }
style={{ overflow: isPaymentOpen ? "hidden" : "" }}
> >
{transaction.paymentClaimed && transaction.confirmed < 2 && (
<div className={styles.RibbonBanner}>
<img src={"https://i.imgur.com/yt6osgL.png"}></img>
<h1>payment claimed</h1>
</div>
)}
<h2 className={styles["Transactions-detail"]}> <h2 className={styles["Transactions-detail"]}>
Transaction ID: {transaction.transactionId} Transaction ID: {transaction.transactionId}
</h2> </h2>
@@ -134,14 +152,24 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
transaction.Table ? transaction.Table.tableNo : "N/A" transaction.Table ? transaction.Table.tableNo : "N/A"
}`} }`}
</h2> </h2>
{transaction.notes != "" && ( {(transaction.notes !== "" || isPaymentOpen) && (
<> <>
<div className={styles.NoteContainer}> <div
className={styles.NoteContainer}
style={{
visibility: transaction.notes == "" ? "hidden" : "visible",
}}
>
<span>Note :</span> <span>Note :</span>
<span></span> <span></span>
</div> </div>
<div className={styles.NoteContainer}> <div
className={styles.NoteContainer}
style={{
visibility: transaction.notes == "" ? "hidden" : "visible",
}}
>
<textarea <textarea
className={styles.NoteInput} className={styles.NoteInput}
value={transaction.notes} value={transaction.notes}
@@ -159,29 +187,36 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
</div> </div>
<div className={styles.PaymentContainer}> <div className={styles.PaymentContainer}>
<ButtonWithReplica <ButtonWithReplica
paymentUrl={paymentUrl}
price={
"Rp" + calculateTotalPrice(transaction.DetailedTransactions)
}
disabled={isPaymentLoading} disabled={isPaymentLoading}
onClick={() => handleConfirm(transaction.transactionId)} isPaymentLoading={isPaymentLoading}
handleClick={() => handleConfirm(transaction.transactionId)}
Open={() => setIsPaymentOpen(true)}
isPaymentOpen={isPaymentOpen}
> >
{isPaymentLoading ? ( {isPaymentLoading ? (
<ColorRing height="50" width="50" color="white" /> <ColorRing height="50" width="50" color="white" />
) : transaction.confirmed === 1 ? ( ) : isPaymentOpen ? (
"Show payment" // Display "Confirm has paid" if the transaction is confirmed (1) "Claim has paid" // Display "Confirm has paid" if the transaction is confirmed (1)
) : transaction.confirmed === -1 ? (
"Declined" // Display "Declined" if the transaction is declined (-1)
) : transaction.confirmed === 2 ? (
"Confirm item has ready" // Display "Item ready" if the transaction is ready (2)
) : transaction.confirmed === 3 ? (
"Transaction success" // Display "Item ready" if the transaction is ready (2)
) : ( ) : (
"Confirm availability" // Display "Confirm availability" if the transaction is not confirmed (0) "Show payment" // Display "Confirm availability" if the transaction is not confirmed (0)
)} )}
</ButtonWithReplica> </ButtonWithReplica>
</div> </div>
<h5 <h5
className={styles.DeclineButton} className={`${styles.DeclineButton} ${
onClick={() => handleDecline(transaction.transactionId)} isPaymentOpen ? styles.active : ""
}`}
onClick={() =>
isPaymentOpen
? setIsPaymentOpen(false)
: handleDecline(transaction.transactionId)
}
> >
cancel {isPaymentOpen ? "back" : "cancel"}
</h5> </h5>
</div> </div>
)} )}

View File

@@ -9,7 +9,6 @@ import {
} from "../helpers/transactionHelpers"; } from "../helpers/transactionHelpers";
import { getTables } from "../helpers/tableHelper"; import { getTables } from "../helpers/tableHelper";
import TableCanvas from "../components/TableCanvas"; import TableCanvas from "../components/TableCanvas";
import ButtonWithReplica from "../components/ButtonWithReplica";
export default function Transactions({ propsShopId, sendParam, deviceType }) { export default function Transactions({ propsShopId, sendParam, deviceType }) {
const { shopId, tableId } = useParams(); const { shopId, tableId } = useParams();
@@ -110,7 +109,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
setSelectedTable(transaction.Table || { tableId: 0 }) setSelectedTable(transaction.Table || { tableId: 0 })
} }
> >
{transaction.paymentClaimed && ( {transaction.paymentClaimed && transaction.confirmed < 2 && (
<div className={styles.RibbonBanner}> <div className={styles.RibbonBanner}>
<img src={"https://i.imgur.com/yt6osgL.png"}></img> <img src={"https://i.imgur.com/yt6osgL.png"}></img>
<h1>payment claimed</h1> <h1>payment claimed</h1>
@@ -144,11 +143,14 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
</span> </span>
</div> </div>
<div className={styles.TotalContainer}> <div className={styles.TotalContainer}>
<ButtonWithReplica />
<button <button
className={styles.PayButton} className={styles.PayButton}
onClick={() => handleConfirm(transaction.transactionId)} onClick={() => handleConfirm(transaction.transactionId)}
disabled={transaction.confirmed !== 0 || isPaymentLoading} // Disable button if confirmed (1) or declined (-1) or loading disabled={
transaction.confirmed === -1 ||
transaction.confirmed === 3 ||
isPaymentLoading
} // Disable button if confirmed (1) or declined (-1) or loading
> >
{isPaymentLoading ? ( {isPaymentLoading ? (
<ColorRing height="50" width="50" color="white" /> <ColorRing height="50" width="50" color="white" />

View File

@@ -98,6 +98,24 @@
text-align: center; /* Center the text within the button */ text-align: center; /* Center the text within the button */
margin-bottom: 23px; /* Space at the bottom to match the PayButton */ margin-bottom: 23px; /* Space at the bottom to match the PayButton */
} }
.DeclineButton.active {
position: relative;
z-index: 201;
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 20px;
padding: 12px 24px; /* Add some padding for spacing */
color: rgba(88, 55, 50, 1);
background-color: transparent; /* No background */
border: none; /* No border */
margin: 0 auto; /* Center horizontally */
cursor: pointer;
display: block; /* Center the text horizontally */
text-align: center; /* Center the text within the button */
margin-bottom: 23px; /* Space at the bottom to match the PayButton */
}
.RoundedRectangle { .RoundedRectangle {
position: relative; position: relative;
border-radius: 20px; border-radius: 20px;