ok
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jsqr": "^1.4.0",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"react": "^18.3.1",
|
||||
"react-bootstrap": "^2.10.4",
|
||||
@@ -12528,6 +12529,11 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jsqr": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.4.0.tgz",
|
||||
"integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A=="
|
||||
},
|
||||
"node_modules/jsx-ast-utils": {
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jsqr": "^1.4.0",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"react": "^18.3.1",
|
||||
"react-bootstrap": "^2.10.4",
|
||||
|
||||
21
src/App.js
21
src/App.js
@@ -123,9 +123,15 @@ function App() {
|
||||
//for guest
|
||||
socket.on("transaction_pending", async (data) => {
|
||||
console.log("transaction notification");
|
||||
// Call `setModal` with content and parameters
|
||||
setModal("transaction_pending");
|
||||
});
|
||||
|
||||
socket.on("transaction_confirmed", async (data) => {
|
||||
console.log("transaction notification");
|
||||
setModal("transaction_confirmed", data);
|
||||
});
|
||||
|
||||
socket.on("transaction_success", async (data) => {
|
||||
console.log("transaction notification");
|
||||
setModal("transaction_success");
|
||||
@@ -207,11 +213,20 @@ function App() {
|
||||
}, [navigate]);
|
||||
|
||||
// Function to open the modal
|
||||
const setModal = (content) => {
|
||||
const setModal = (content, params = {}) => {
|
||||
setIsModalOpen(true);
|
||||
setModalContent(content);
|
||||
|
||||
// Prepare query parameters
|
||||
const queryParams = new URLSearchParams({
|
||||
modal: content,
|
||||
...params, // Spread additional parameters
|
||||
}).toString();
|
||||
// Update URL with new parameters
|
||||
navigate(`?${queryParams}`, { replace: true });
|
||||
|
||||
// Prevent scrolling when modal is open
|
||||
document.body.style.overflow = "hidden";
|
||||
navigate(`?modal=` + content, { replace: true });
|
||||
};
|
||||
|
||||
// Function to close the modal
|
||||
@@ -373,7 +388,7 @@ function App() {
|
||||
</Routes>
|
||||
</header>
|
||||
<Modal
|
||||
shopId={shopId}
|
||||
shop={shop}
|
||||
isOpen={isModalOpen}
|
||||
modalContent={modalContent}
|
||||
onClose={closeModal}
|
||||
|
||||
@@ -156,19 +156,18 @@ const shrink = keyframes`
|
||||
border-radius: 50%;
|
||||
}
|
||||
`;
|
||||
|
||||
const Rectangle = styled.div`
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
right: 15px;
|
||||
width: 200px;
|
||||
height: auto;
|
||||
max-height: 87vh; /* or another appropriate value */
|
||||
background-color: white;
|
||||
z-index: 10;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
animation: ${(props) => (props.animate === "grow" ? grow : shrink)} 0.5s
|
||||
forwards;
|
||||
overflow: hidden;
|
||||
overflow-y: auto; /* Enable vertical scrolling */
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
`;
|
||||
@@ -246,6 +245,7 @@ const Header = ({
|
||||
if (rectangleRef.current && !rectangleRef.current.contains(event.target)) {
|
||||
setAnimate("shrink");
|
||||
setTimeout(() => setShowRectangle(false), 500);
|
||||
rectangleRef.current.style.overflow = "hidden";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -304,7 +304,9 @@ const Header = ({
|
||||
<Child onClick={goToLogin}>Click to login</Child>
|
||||
)}
|
||||
{user.username !== undefined && (
|
||||
<Child onClick={() => setModal("edit_account")}>Edit</Child>
|
||||
<Child onClick={() => setModal("edit_account")}>
|
||||
Edit profile
|
||||
</Child>
|
||||
)}
|
||||
{shopId &&
|
||||
user.userId == shopOwnerId &&
|
||||
@@ -312,59 +314,77 @@ const Header = ({
|
||||
user.roleId === 1 && (
|
||||
<>
|
||||
<Child onClick={goToAdminCafes}>see your other cafes</Child>
|
||||
<Child onClick={() => setModal("edit_tables")}>
|
||||
{shopName} table maps
|
||||
</Child>
|
||||
<Child hasChildren>
|
||||
{shopName} clerks
|
||||
<Child onClick={() => setModal("craete_account_clerk")}>
|
||||
+ Add clerk
|
||||
</Child>
|
||||
{shopClerks &&
|
||||
shopClerks.map((key, index) => (
|
||||
<Child key={index}>
|
||||
{shopClerks[index].username}
|
||||
<button
|
||||
onClick={() =>
|
||||
removeConnectedGuestSides(guestSides[index][3])
|
||||
}
|
||||
>
|
||||
remove
|
||||
</button>
|
||||
</Child>
|
||||
))}
|
||||
</Child>
|
||||
<Child onClick={() => setModal("add_material")}>
|
||||
add material
|
||||
</Child>
|
||||
<Child onClick={() => setModal("update_stock")}>
|
||||
update stock
|
||||
</Child>
|
||||
<Child hasChildren>
|
||||
{shopName}
|
||||
<Child onClick={() => setModal("edit_tables")}>
|
||||
table maps
|
||||
</Child>
|
||||
<Child hasChildren>
|
||||
clerks
|
||||
<Child onClick={() => setModal("craete_account_clerk")}>
|
||||
+ Add clerk
|
||||
</Child>
|
||||
{shopClerks &&
|
||||
shopClerks.map((key, index) => (
|
||||
<Child key={index}>
|
||||
{shopClerks[index].username}
|
||||
<button
|
||||
onClick={() =>
|
||||
removeConnectedGuestSides(
|
||||
guestSides[index][3]
|
||||
)
|
||||
}
|
||||
>
|
||||
remove
|
||||
</button>
|
||||
</Child>
|
||||
))}
|
||||
</Child>
|
||||
<Child onClick={() => setModal("payment_option")}>
|
||||
payment options
|
||||
</Child>
|
||||
</Child>
|
||||
</>
|
||||
)}
|
||||
{user.username !== undefined &&
|
||||
((user.userId == shopOwnerId && user.roleId === 1) ||
|
||||
(user.cafeId == shopId && user.roleId === 2)) && (
|
||||
<Child onClick={() => setModal("update_stock")}>
|
||||
update stock
|
||||
</Child>
|
||||
)}
|
||||
{user.username !== undefined &&
|
||||
user.roleId == 2 &&
|
||||
user.cafeId == shopId && (
|
||||
user.cafeId == shopId &&
|
||||
user.roleId === 2 && (
|
||||
<Child hasChildren>
|
||||
connected guest sides
|
||||
<Child onClick={goToGuestSideLogin}>+ Add guest side</Child>
|
||||
{guestSides &&
|
||||
guestSides.map((key, index) => (
|
||||
<Child key={index}>
|
||||
guest side {index + 1}
|
||||
<button
|
||||
onClick={() =>
|
||||
removeConnectedGuestSides(guestSides[index][3])
|
||||
}
|
||||
>
|
||||
remove
|
||||
</button>
|
||||
{shopName}
|
||||
<Child onClick={() => setModal("update_stock")}>
|
||||
update stock
|
||||
</Child>
|
||||
{user.username !== undefined &&
|
||||
user.roleId == 2 &&
|
||||
user.cafeId == shopId && (
|
||||
<Child hasChildren>
|
||||
connected guest sides
|
||||
<Child onClick={goToGuestSideLogin}>
|
||||
+ Add guest side
|
||||
</Child>
|
||||
{guestSides &&
|
||||
guestSides.map((key, index) => (
|
||||
<Child key={index}>
|
||||
guest side {index + 1}
|
||||
<button
|
||||
onClick={() =>
|
||||
removeConnectedGuestSides(
|
||||
guestSides[index][3]
|
||||
)
|
||||
}
|
||||
>
|
||||
remove
|
||||
</button>
|
||||
</Child>
|
||||
))}
|
||||
</Child>
|
||||
))}
|
||||
)}
|
||||
</Child>
|
||||
)}
|
||||
{user.username !== undefined && (
|
||||
|
||||
@@ -58,12 +58,22 @@ export default function ItemType({
|
||||
className={styles["item-type-image"]}
|
||||
/>
|
||||
{blank && (
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className={styles["item-type-image-input"]}
|
||||
onChange={handleImageChange}
|
||||
/>
|
||||
<div className={styles["item-type-image-container"]}>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className={styles["item-type-image-input"]}
|
||||
onChange={handleImageChange}
|
||||
id="image-input"
|
||||
/>
|
||||
Click to add image
|
||||
<label
|
||||
htmlFor="image-input"
|
||||
className={styles["item-type-image-text"]}
|
||||
>
|
||||
Click to add image
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
|
||||
@@ -41,16 +41,24 @@
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.item-type-image-input {
|
||||
.item-type-image-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.item-type-image-input {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.item-type-create {
|
||||
position: absolute;
|
||||
top: 80%; /* Position below the input */
|
||||
top: 76%; /* Position below the input */
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
margin-top: 10px; /* Space between input and button */
|
||||
|
||||
@@ -19,12 +19,7 @@ const ItemTypeLister = ({ shopId, shopOwnerId, user, itemTypes }) => {
|
||||
<div className="item-type-lister">
|
||||
<div className="item-type-list">
|
||||
{itemTypes && itemTypes.length > 1 && (
|
||||
<ItemType
|
||||
name={"All"}
|
||||
imageUrl={
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAY1BMVEX///8BAQEEBAT09PT8/PzFxcWGhobu7u7R0dHLy8uoqKhTU1N7e3tXV1fIyMguLi7d3d08PDyenp41NTWBgYF1dXVHR0dMTEwTExPj4+O6urrp6emWlpasrKy2trZeXl4aGhrSJRZxAAADF0lEQVR4nO3Zi3aiMBCAYQIR71VbtXft+z/lJoFigAkNbYHdPf/X3VPqGeMkwdxIEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0dGBVaSOe5vuV/4giqR1mXOPXL6M1X4r9Cn5l+kqCx3R2Nq7igjWPYoeWjbPkpg09LdiJ/dyPK3P22O+iIid5ZvtebvJZxGxi/y4Pa9Px5efJvh99i56fVM3q+4RJFv5seHeseVqP/btdYDsY5hE7nZVGqn5/7jvqOHiUASVDsE+NyXsH5UfvLub4qtoP/Hd5lEkYn+Zf+Fb9d7lXAa73/fB2KUryytZvQ9Qgy9pm3TqNbW7WAai72udojqruKwq5sdOMJ4ub51SXNnrp2cxdqH8YHcd7PHnp7J+foebphu9inMl+pAyyQ5i7KE13Nj3ftQ6sDIfvkoNef22U59fmoswKqzaCTurRqz942ILaZZs/j6OVrPS805o6dS8dG3Uz65fAhVUqrG6MROFvkpdaF7ZjT1nvAj5Fm2/b1VxpsSszc+s2d96r+rf2Fv02FP/SeoSl9ildZfmUtLulbxV7kW6+Z3TOBWrVBVqWbdiN3LKxqYVuxbj3CeNUS2PON45D80+zLbBGm6z5lDzEIg0Hzdm9ZIkPHioXTN0/hiMPbfmgF0wlj78be5TxVz+l+/hSZ7vVXMstXuh7rFU14IvwaYbeyztmg/rd6mdD8UZLm3Nhzo8H6rR50N5TWN+rllz2dZjTZPYNU0qr2nkNf2AOtalPleBjnVpy0Uaw8TFwdCkvUVq9xaC6L2F9SG3xuh7C11uVG91c20fuT9UX+wPG7vlNLy1HtDnHr/KojORnnv81LtVXbmT7PHdOU119FL8Crd0/3Mav+RJzmnks7bwAbx/1pb+C2dthZ+cl4aO9l0prfPSCQ/2izPvzQBn3pupz7wL3vOFqEczsc8tkuSveW5RinqcJF/Lwf7TxsmfPcWmoIWrUGjt+eF3Uvolfdq3Pg71ehsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACASfwBIesXINu4PFgAAAAASUVORK5CYII="
|
||||
}
|
||||
/>
|
||||
<ItemType name={"All"} imageUrl={"uploads/1718732420960.png"} />
|
||||
)}
|
||||
{itemTypes &&
|
||||
itemTypes.map(
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import React from "react";
|
||||
import styles from "./Modal.module.css";
|
||||
import TablesPage from "./TablesPage.js";
|
||||
import PaymentOptions from "./PaymentOptions.js";
|
||||
import TableMaps from "../components/TableMaps";
|
||||
import Transactions from "../pages/Transactions";
|
||||
import Transaction_pending from "../pages/Transaction_pending";
|
||||
import Transaction_confirmed from "../pages/Transaction_confirmed";
|
||||
import Transaction_success from "../pages/Transaction_success";
|
||||
import Transaction_failed from "../pages/Transaction_failed";
|
||||
import MaterialList from "../pages/MaterialList.js";
|
||||
import MaterialMutationsPage from "../pages/MaterialMutationsPage.js";
|
||||
|
||||
const Modal = ({ shopId, isOpen, onClose, modalContent }) => {
|
||||
const Modal = ({ shop, isOpen, onClose, modalContent }) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
// Function to handle clicks on the overlay
|
||||
@@ -30,16 +32,24 @@ const Modal = ({ shopId, isOpen, onClose, modalContent }) => {
|
||||
<button onClick={onClose} className={styles.closeButton}>
|
||||
×
|
||||
</button>
|
||||
{modalContent === "edit_tables" && <TablesPage shopId={shopId} />}
|
||||
{modalContent === "edit_tables" && <TablesPage shop={shop} />}
|
||||
{modalContent === "new_transaction" && (
|
||||
<Transactions propsShopId={shopId} />
|
||||
<Transactions propsShopId={shop.cafeId} />
|
||||
)}{" "}
|
||||
{modalContent === "transaction_pending" && <Transaction_pending />}
|
||||
{modalContent === "transaction_confirmed" && (
|
||||
<Transaction_confirmed paymentUrl={shop.qrPayment} />
|
||||
)}
|
||||
{modalContent === "transaction_success" && <Transaction_success />}
|
||||
{modalContent === "transaction_failed" && <Transaction_failed />}
|
||||
{modalContent === "add_material" && <MaterialList cafeId={shopId} />}
|
||||
{modalContent === "payment_option" && (
|
||||
<PaymentOptions paymentUrl={shop.qrPayment} shopId={shop.cafeId} />
|
||||
)}
|
||||
{modalContent === "add_material" && (
|
||||
<MaterialList cafeId={shop.cafeId} />
|
||||
)}
|
||||
{modalContent === "update_stock" && (
|
||||
<MaterialMutationsPage cafeId={shopId} />
|
||||
<MaterialMutationsPage cafeId={shop.cafeId} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
165
src/components/PaymentOptions.js
Normal file
165
src/components/PaymentOptions.js
Normal file
@@ -0,0 +1,165 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import jsQR from "jsqr"; // Import jsQR library
|
||||
import { getImageUrl } from "../helpers/itemHelper";
|
||||
import { saveCafeDetails } from "../helpers/cafeHelpers"; // Import the helper function
|
||||
|
||||
const SetPaymentQr = ({
|
||||
isConfigure,
|
||||
tableNo,
|
||||
qrCodeUrl,
|
||||
paymentUrl,
|
||||
initialQrPosition,
|
||||
initialQrSize,
|
||||
handleQrSave,
|
||||
shopId, // Pass cafeId as a prop to identify which cafe to update
|
||||
}) => {
|
||||
const [qrPosition, setQrPosition] = useState(initialQrPosition);
|
||||
const [qrSize, setQrSize] = useState(initialQrSize);
|
||||
const [qrPayment, setQrPayment] = useState(getImageUrl(paymentUrl));
|
||||
const [qrCodeDetected, setQrCodeDetected] = useState(false);
|
||||
const qrPaymentInputRef = useRef(null);
|
||||
const overlayTextRef = useRef(null);
|
||||
const qrCodeContainerRef = useRef(null);
|
||||
|
||||
// Use useEffect to detect QR code after qrPayment updates
|
||||
useEffect(() => {
|
||||
if (qrPayment) {
|
||||
detectQRCodeFromContainer();
|
||||
}
|
||||
}, [qrPayment]);
|
||||
|
||||
const handleFileChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const newqrPayment = URL.createObjectURL(file); // Create a temporary URL for display
|
||||
setQrPayment(newqrPayment);
|
||||
}
|
||||
};
|
||||
|
||||
const detectQRCodeFromContainer = () => {
|
||||
const container = qrCodeContainerRef.current;
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
|
||||
// Create an image element to load the background image
|
||||
const img = new Image();
|
||||
img.crossOrigin = "Anonymous"; // Handle CORS if needed
|
||||
img.onload = () => {
|
||||
// Set canvas size
|
||||
canvas.width = container.offsetWidth;
|
||||
canvas.height = container.offsetHeight;
|
||||
|
||||
// Draw image on canvas
|
||||
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);
|
||||
|
||||
if (qrCode) {
|
||||
setQrCodeDetected(true);
|
||||
console.log("QR Code detected:", qrCode.data);
|
||||
} else {
|
||||
setQrCodeDetected(false);
|
||||
console.log("No QR Code detected");
|
||||
}
|
||||
};
|
||||
img.src = qrPayment; // Load the image URL
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
const qrPaymentFile = qrPaymentInputRef.current.files[0]; // Get the selected file for qrPayment
|
||||
|
||||
// Prepare the details object
|
||||
const details = {
|
||||
qrPosition,
|
||||
qrSize,
|
||||
qrPaymentFile, // Include qrPayment file
|
||||
};
|
||||
|
||||
// Call saveCafeDetails function with the updated details object
|
||||
saveCafeDetails(shopId, details)
|
||||
.then((response) => {
|
||||
console.log("Cafe details saved:", response);
|
||||
// handleQrSave(qrPosition, qrSize, qrPayment);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error saving cafe details:", error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
id="qr-code-container"
|
||||
ref={qrCodeContainerRef}
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "300px",
|
||||
height: "300px",
|
||||
background: `center center / contain no-repeat url(${qrPayment})`,
|
||||
backgroundSize: "contain",
|
||||
overflow: "hidden",
|
||||
border: "1px solid #ddd",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={overlayTextRef}
|
||||
style={styles.overlayText}
|
||||
onClick={() => qrPaymentInputRef.current.click()}
|
||||
>
|
||||
Click To Change Image
|
||||
</div>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
ref={qrPaymentInputRef}
|
||||
style={{ display: "none" }}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
{qrCodeDetected ? <p>QR Code Detected</p> : <p>No QR Code Detected</p>}
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
style={{
|
||||
padding: "10px 20px",
|
||||
fontSize: "16px",
|
||||
backgroundColor: "#28a745",
|
||||
color: "#fff",
|
||||
border: "none",
|
||||
borderRadius: "4px",
|
||||
cursor: "pointer",
|
||||
transition: "background-color 0.3s",
|
||||
}}
|
||||
onMouseOver={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "#218838")
|
||||
}
|
||||
onMouseOut={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "#28a745")
|
||||
}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = {
|
||||
overlayText: {
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||
color: "#fff",
|
||||
padding: "10px",
|
||||
borderRadius: "5px",
|
||||
cursor: "pointer",
|
||||
},
|
||||
// Other styles omitted for brevity
|
||||
};
|
||||
|
||||
export default SetPaymentQr;
|
||||
@@ -1,5 +1,7 @@
|
||||
import React, { useState, useRef } from "react";
|
||||
import html2canvas from "html2canvas";
|
||||
import { getImageUrl } from "../helpers/itemHelper";
|
||||
import { saveCafeDetails } from "../helpers/cafeHelpers"; // Import the helper function
|
||||
|
||||
const QRCodeWithBackground = ({
|
||||
isConfigure,
|
||||
@@ -9,11 +11,12 @@ const QRCodeWithBackground = ({
|
||||
initialQrPosition,
|
||||
initialQrSize,
|
||||
handleQrSave,
|
||||
shopId, // Pass cafeId as a prop to identify which cafe to update
|
||||
}) => {
|
||||
const [qrPosition, setQrPosition] = useState(initialQrPosition);
|
||||
const [qrSize, setQrSize] = useState(initialQrSize);
|
||||
const [bgImage, setBgImage] = useState(backgroundUrl);
|
||||
const fileInputRef = useRef(null);
|
||||
const [bgImage, setBgImage] = useState(getImageUrl(backgroundUrl)); // URL or File
|
||||
const qrBackgroundInputRef = useRef(null);
|
||||
const overlayTextRef = useRef(null);
|
||||
|
||||
const handlePositionChange = (e) => {
|
||||
@@ -31,13 +34,30 @@ const QRCodeWithBackground = ({
|
||||
const handleFileChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const newBgImage = URL.createObjectURL(file);
|
||||
const newBgImage = URL.createObjectURL(file); // Create a temporary URL for display
|
||||
setBgImage(newBgImage);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
handleQrSave(qrPosition, qrSize, bgImage);
|
||||
const qrBackgroundFile = qrBackgroundInputRef.current.files[0]; // Get the selected file for qrBackground
|
||||
|
||||
// Prepare the details object
|
||||
const details = {
|
||||
qrPosition,
|
||||
qrSize,
|
||||
qrBackgroundFile, // Include qrBackground file
|
||||
};
|
||||
|
||||
// Call saveCafeDetails function with the updated details object
|
||||
saveCafeDetails(shopId, details)
|
||||
.then((response) => {
|
||||
console.log("Cafe details saved:", response);
|
||||
handleQrSave(qrPosition, qrSize, bgImage);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error saving cafe details:", error);
|
||||
});
|
||||
};
|
||||
|
||||
const printQRCode = () => {
|
||||
@@ -118,7 +138,7 @@ const QRCodeWithBackground = ({
|
||||
position: "relative",
|
||||
width: "300px",
|
||||
height: "300px",
|
||||
background: `url(${bgImage}) no-repeat center center`,
|
||||
background: `center center / contain no-repeat url(${bgImage})`,
|
||||
backgroundSize: "contain",
|
||||
overflow: "hidden",
|
||||
border: "1px solid #ddd",
|
||||
@@ -141,7 +161,7 @@ const QRCodeWithBackground = ({
|
||||
<div
|
||||
ref={overlayTextRef}
|
||||
style={styles.overlayText}
|
||||
onClick={() => fileInputRef.current.click()}
|
||||
onClick={() => qrBackgroundInputRef.current.click()}
|
||||
>
|
||||
Click To Change Image
|
||||
</div>
|
||||
@@ -150,7 +170,7 @@ const QRCodeWithBackground = ({
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
ref={fileInputRef}
|
||||
ref={qrBackgroundInputRef}
|
||||
style={{ display: "none" }}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
@@ -276,16 +296,15 @@ const QRCodeWithBackground = ({
|
||||
borderRadius: "4px",
|
||||
cursor: "pointer",
|
||||
transition: "background-color 0.3s",
|
||||
marginLeft: "10px",
|
||||
}}
|
||||
onMouseOver={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "#138496")
|
||||
(e.currentTarget.style.backgroundColor = "#117a8b")
|
||||
}
|
||||
onMouseOut={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "#17a2b8")
|
||||
}
|
||||
>
|
||||
Save Image
|
||||
Save QR Code
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -293,54 +312,41 @@ const QRCodeWithBackground = ({
|
||||
);
|
||||
};
|
||||
|
||||
// Styles for the configuration labels and inputs
|
||||
const styles = {
|
||||
label: {
|
||||
display: "block",
|
||||
fontSize: "16px",
|
||||
fontWeight: "bold",
|
||||
marginBottom: "5px",
|
||||
overlayText: {
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||
color: "#fff",
|
||||
padding: "10px",
|
||||
borderRadius: "5px",
|
||||
cursor: "pointer",
|
||||
},
|
||||
sliderContainer: {
|
||||
marginBottom: "20px",
|
||||
},
|
||||
label: {
|
||||
display: "block",
|
||||
marginBottom: "10px",
|
||||
},
|
||||
sliderWrapper: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
marginTop: "5px",
|
||||
},
|
||||
input: {
|
||||
flex: "1",
|
||||
margin: "0 10px",
|
||||
},
|
||||
value: {
|
||||
fontSize: "16px",
|
||||
minWidth: "50px",
|
||||
textAlign: "right",
|
||||
},
|
||||
labelStart: {
|
||||
fontSize: "14px",
|
||||
marginRight: "10px",
|
||||
},
|
||||
labelEnd: {
|
||||
fontSize: "14px",
|
||||
},
|
||||
fileInput: {
|
||||
marginLeft: "10px",
|
||||
},
|
||||
overlayText: {
|
||||
position: "absolute",
|
||||
fontSize: "70px",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
||||
top: "0",
|
||||
bottom: "0",
|
||||
color: "white",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
cursor: "pointer", // Indicates it's clickable
|
||||
zIndex: 10, // Ensure it appears above other elements
|
||||
value: {
|
||||
marginLeft: "10px",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import QRCodeWithBackground from "./QR"; // Adjust path as needed
|
||||
|
||||
const TableList = ({ shopUrl, tables, onSelectTable, selectedTable }) => {
|
||||
const [initialPos, setInitialPos] = useState({ left: 50, top: 50 });
|
||||
const [initialSize, setInitialSize] = useState(20);
|
||||
const [bgImageUrl, setBgImageUrl] = useState(
|
||||
"https://example.com/your-background-image.png"
|
||||
);
|
||||
const TableList = ({ shop, tables, onSelectTable, selectedTable }) => {
|
||||
const [initialPos, setInitialPos] = useState({
|
||||
left: shop.xposition,
|
||||
top: shop.yposition,
|
||||
});
|
||||
const [initialSize, setInitialSize] = useState(shop.scale);
|
||||
const [bgImageUrl, setBgImageUrl] = useState(shop.qrBackground);
|
||||
const shopUrl = window.location.hostname + "/" + shop.cafeId;
|
||||
|
||||
const generateQRCodeUrl = (tableCode) =>
|
||||
`https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(
|
||||
@@ -51,6 +52,7 @@ const TableList = ({ shopUrl, tables, onSelectTable, selectedTable }) => {
|
||||
<div style={{ marginBottom: "10px" }}>configure</div>
|
||||
{-1 == selectedTable?.tableId && (
|
||||
<QRCodeWithBackground
|
||||
shopId={shop.cafeId}
|
||||
isConfigure={true}
|
||||
handleQrSave={handleQrSave}
|
||||
setInitialPos={setInitialPos}
|
||||
@@ -62,41 +64,43 @@ const TableList = ({ shopUrl, tables, onSelectTable, selectedTable }) => {
|
||||
/>
|
||||
)}
|
||||
</li>
|
||||
{tables
|
||||
.filter((table) => table.tableNo !== 0)
|
||||
.map((table) => (
|
||||
<li
|
||||
key={table.tableId}
|
||||
style={{
|
||||
backgroundColor:
|
||||
table.tableId === selectedTable?.tableId
|
||||
? "lightblue"
|
||||
: "white",
|
||||
marginBottom: "10px",
|
||||
padding: "10px",
|
||||
borderRadius: "4px",
|
||||
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => onSelectTable(table)}
|
||||
>
|
||||
<div style={{ marginBottom: "10px" }}>
|
||||
Table {table.tableNo}
|
||||
</div>
|
||||
{table.tableId === selectedTable?.tableId && (
|
||||
<>
|
||||
<QRCodeWithBackground
|
||||
tableNo={table.tableNo}
|
||||
qrCodeUrl={generateQRCodeUrl(table.tableCode)}
|
||||
backgroundUrl={bgImageUrl}
|
||||
initialQrPosition={initialPos}
|
||||
initialQrSize={initialSize}
|
||||
/>
|
||||
<h5>{shopUrl + "/" + table.tableCode}</h5>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
{tables &&
|
||||
tables
|
||||
.filter((table) => table.tableNo !== 0)
|
||||
.map((table) => (
|
||||
<li
|
||||
key={table.tableId}
|
||||
style={{
|
||||
backgroundColor:
|
||||
table.tableId === selectedTable?.tableId
|
||||
? "lightblue"
|
||||
: "white",
|
||||
marginBottom: "10px",
|
||||
padding: "10px",
|
||||
borderRadius: "4px",
|
||||
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => onSelectTable(table)}
|
||||
>
|
||||
<div style={{ marginBottom: "10px" }}>
|
||||
Table {table.tableNo}
|
||||
</div>
|
||||
{table.tableId === selectedTable?.tableId && (
|
||||
<>
|
||||
<QRCodeWithBackground
|
||||
tableNo={table.tableNo}
|
||||
qrCodeUrl={generateQRCodeUrl(table.tableCode)}
|
||||
backgroundUrl={bgImageUrl}
|
||||
initialQrPosition={initialPos}
|
||||
initialQrSize={initialSize}
|
||||
cafeId={shop.cafeId}
|
||||
/>
|
||||
<h5>{shopUrl + "/" + table.tableCode}</h5>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getTables, updateTable, createTable } from "../helpers/tableHelper";
|
||||
import TableCanvas from "./TableCanvas";
|
||||
import TableList from "./TableList";
|
||||
|
||||
const TablesPage = ({ shopId }) => {
|
||||
const TablesPage = ({ shop }) => {
|
||||
const [tables, setTables] = useState([]);
|
||||
const [selectedTable, setSelectedTable] = useState(null);
|
||||
const [newTable, setNewTable] = useState(null);
|
||||
@@ -13,7 +13,8 @@ const TablesPage = ({ shopId }) => {
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const fetchedTables = await getTables(shopId);
|
||||
console.log(shop);
|
||||
const fetchedTables = await getTables(shop.cafeId);
|
||||
setTables(fetchedTables);
|
||||
setOriginalTables(fetchedTables);
|
||||
} catch (error) {
|
||||
@@ -22,7 +23,7 @@ const TablesPage = ({ shopId }) => {
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [shopId]);
|
||||
}, [shop.cafeId]);
|
||||
|
||||
const handleAddTable = () => {
|
||||
const nextId = Math.random().toString(36).substr(2, 11);
|
||||
@@ -131,7 +132,7 @@ const TablesPage = ({ shopId }) => {
|
||||
const handleSave = async () => {
|
||||
if (newTable) {
|
||||
try {
|
||||
const createdTable = await createTable(shopId, {
|
||||
const createdTable = await createTable(shop.cafeId, {
|
||||
...newTable,
|
||||
tableNo,
|
||||
});
|
||||
@@ -144,7 +145,7 @@ const TablesPage = ({ shopId }) => {
|
||||
}
|
||||
} else if (selectedTable) {
|
||||
try {
|
||||
const updatedTable = await updateTable(shopId, {
|
||||
const updatedTable = await updateTable(shop.cafeId, {
|
||||
...selectedTable,
|
||||
tableNo,
|
||||
});
|
||||
@@ -202,7 +203,7 @@ const TablesPage = ({ shopId }) => {
|
||||
tableNo={tableNo}
|
||||
/> */}
|
||||
<TableList
|
||||
shopUrl={window.location.hostname + "/" + shopId}
|
||||
shop={shop}
|
||||
tables={tables}
|
||||
onSelectTable={handleSelect}
|
||||
selectedTable={selectedTable}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 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;
|
||||
|
||||
@@ -18,7 +18,7 @@ export async function getOwnedCafes(userId) {
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch cart details");
|
||||
throw new Error("Failed to fetch cafes");
|
||||
}
|
||||
|
||||
const cafes = await response.json();
|
||||
@@ -30,48 +30,85 @@ export async function getOwnedCafes(userId) {
|
||||
|
||||
export async function createCafe(userId) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/cafe/get-cafe-by-ownerId/` + userId,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${getAuthToken()}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
const response = await fetch(`${API_BASE_URL}/cafe/create-cafe`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${getAuthToken()}`,
|
||||
},
|
||||
body: JSON.stringify({ ownerId: userId }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch cart details");
|
||||
throw new Error("Failed to create cafe");
|
||||
}
|
||||
|
||||
const cafes = await response.json();
|
||||
return cafes;
|
||||
const cafe = await response.json();
|
||||
return cafe;
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateCafe(userId) {
|
||||
export async function updateCafe(cafeId, cafeDetails) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/cafe/get-cafe-by-ownerId/` + userId,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${getAuthToken()}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
const response = await fetch(`${API_BASE_URL}/cafe/update-cafe/${cafeId}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${getAuthToken()}`,
|
||||
},
|
||||
body: JSON.stringify(cafeDetails),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch cart details");
|
||||
throw new Error("Failed to update cafe");
|
||||
}
|
||||
|
||||
const cafes = await response.json();
|
||||
return cafes;
|
||||
const updatedCafe = await response.json();
|
||||
return updatedCafe;
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// helpers/cafeHelpers.js
|
||||
export async function saveCafeDetails(cafeId, details) {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
|
||||
// Append qrBackground file if it exists
|
||||
if (details.qrBackgroundFile) {
|
||||
formData.append("qrBackground", details.qrBackgroundFile);
|
||||
}
|
||||
|
||||
// Append qrPayment file if it exists
|
||||
if (details.qrPaymentFile) {
|
||||
formData.append("qrPayment", details.qrPaymentFile);
|
||||
}
|
||||
|
||||
// Append other form fields
|
||||
if (details.qrPosition) {
|
||||
formData.append("xposition", details.qrPosition.left);
|
||||
formData.append("yposition", details.qrPosition.top);
|
||||
}
|
||||
if (details.qrSize) formData.append("scale", details.qrSize);
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/cafe/set-cafe/${cafeId}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
Authorization: `Bearer ${getAuthToken()}`,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to save cafe details");
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error("Error saving cafe details:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,55 @@ export async function declineTransaction(transactionId) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleClaimHasPaid(transactionId) {
|
||||
try {
|
||||
const token = getLocalStorage("auth");
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/transaction/payment-claimed/${transactionId}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTransaction(transactionId) {
|
||||
try {
|
||||
const token = getLocalStorage("auth");
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/transaction/get-transaction/${transactionId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const transaction = await response.json();
|
||||
return transaction;
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTransactions(shopId, demand) {
|
||||
try {
|
||||
const token = getLocalStorage("auth");
|
||||
|
||||
190
src/pages/Transaction_confirmed.js
Normal file
190
src/pages/Transaction_confirmed.js
Normal file
@@ -0,0 +1,190 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { ColorRing } from "react-loader-spinner";
|
||||
import jsQR from "jsqr";
|
||||
import QRCode from "qrcode.react";
|
||||
import styles from "./Transactions.module.css";
|
||||
import { getImageUrl } from "../helpers/itemHelper";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import {
|
||||
handleClaimHasPaid,
|
||||
getTransaction,
|
||||
} from "../helpers/transactionHelpers";
|
||||
import html2canvas from "html2canvas";
|
||||
|
||||
export default function Transaction_pending({ paymentUrl }) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [qrData, setQrData] = useState(null);
|
||||
const [transaction, setTransaction] = useState(null);
|
||||
const qrCodeRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const transactionId = searchParams.get("transactionId") || "";
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const fetchedTransaction = await getTransaction(transactionId);
|
||||
setTransaction(fetchedTransaction);
|
||||
console.log(fetchedTransaction);
|
||||
} catch (error) {
|
||||
console.error("Error fetching transaction:", error);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
const detectQRCode = async () => {
|
||||
if (paymentUrl) {
|
||||
const img = new Image();
|
||||
img.crossOrigin = "Anonymous"; // Handle CORS if needed
|
||||
img.src = getImageUrl(paymentUrl);
|
||||
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
|
||||
// Draw image on canvas
|
||||
context.drawImage(img, 0, 0, img.width, img.height);
|
||||
|
||||
// Get image data
|
||||
const imageData = context.getImageData(
|
||||
0,
|
||||
0,
|
||||
canvas.width,
|
||||
canvas.height
|
||||
);
|
||||
const qrCode = jsQR(imageData.data, canvas.width, canvas.height);
|
||||
|
||||
if (qrCode) {
|
||||
setQrData(qrCode.data); // Set the QR data
|
||||
console.log(qrCode.data);
|
||||
} else {
|
||||
console.log("No QR Code detected");
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
detectQRCode();
|
||||
}, [paymentUrl]);
|
||||
|
||||
const calculateTotalPrice = (detailedTransactions) => {
|
||||
if (!Array.isArray(detailedTransactions)) return 0;
|
||||
|
||||
return detailedTransactions.reduce((total, dt) => {
|
||||
if (
|
||||
dt.Item &&
|
||||
typeof dt.Item.price === "number" &&
|
||||
typeof dt.qty === "number"
|
||||
) {
|
||||
return total + dt.Item.price * dt.qty;
|
||||
}
|
||||
return total;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const downloadQRCode = async () => {
|
||||
if (qrCodeRef.current) {
|
||||
try {
|
||||
const canvas = await html2canvas(qrCodeRef.current);
|
||||
const link = document.createElement("a");
|
||||
link.href = canvas.toDataURL("image/png");
|
||||
link.download = "qr-code.png";
|
||||
link.click();
|
||||
} catch (error) {
|
||||
console.error("Error downloading QR Code:", error);
|
||||
}
|
||||
} else {
|
||||
console.log("QR Code element not found.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.Transactions}>
|
||||
<div style={{ marginTop: "30px" }}></div>
|
||||
<h2 className={styles["Transactions-title"]}>Transaction Confirmed</h2>
|
||||
<div style={{ marginTop: "30px" }}></div>
|
||||
<div className={styles.TransactionListContainer}>
|
||||
<div style={{ marginTop: "30px", textAlign: "center" }}>
|
||||
{qrData ? (
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
<div ref={qrCodeRef}>
|
||||
<QRCode value={qrData} size={256} /> {/* Generate QR code */}
|
||||
</div>
|
||||
<p>Generated QR Code from Data</p>
|
||||
<button
|
||||
onClick={downloadQRCode}
|
||||
style={{
|
||||
marginTop: "20px",
|
||||
padding: "10px 20px",
|
||||
fontSize: "16px",
|
||||
backgroundColor: "#007bff",
|
||||
color: "#fff",
|
||||
border: "none",
|
||||
borderRadius: "4px",
|
||||
cursor: "pointer",
|
||||
transition: "background-color 0.3s",
|
||||
}}
|
||||
onMouseOver={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "#0056b3")
|
||||
}
|
||||
onMouseOut={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "#007bff")
|
||||
}
|
||||
>
|
||||
Download QR Code
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
<ColorRing
|
||||
visible={true}
|
||||
height="80"
|
||||
width="80"
|
||||
ariaLabel="blocks-loading"
|
||||
wrapperStyle={{}}
|
||||
wrapperClass="blocks-wrapper"
|
||||
colors={["#4fa94d", "#f7c34c", "#ffa53c", "#e34f53", "#d23a8d"]}
|
||||
/>
|
||||
<p>Loading QR Code Data...</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{transaction && transaction.DetailedTransactions ? (
|
||||
<div
|
||||
className={styles.TotalContainer}
|
||||
style={{ marginBottom: "20px" }}
|
||||
>
|
||||
<span>Total:</span>
|
||||
<span>
|
||||
Rp{" "}
|
||||
{calculateTotalPrice(
|
||||
transaction.DetailedTransactions
|
||||
).toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
<ColorRing
|
||||
visible={true}
|
||||
height="80"
|
||||
width="80"
|
||||
ariaLabel="blocks-loading"
|
||||
wrapperStyle={{}}
|
||||
wrapperClass="blocks-wrapper"
|
||||
colors={["#4fa94d", "#f7c34c", "#ffa53c", "#e34f53", "#d23a8d"]}
|
||||
/>
|
||||
<p>Loading Transaction Data...</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button onClick={handleClaimHasPaid} className={styles.PayButton}>
|
||||
I've already paid
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export default function Transaction_pending() {
|
||||
<h2>transaction failed</h2>
|
||||
<img
|
||||
className={styles.expression}
|
||||
src="https://i.imgur.com/5j3yIw6.png"
|
||||
src="https://i.imgur.com/B6k9exa.png"
|
||||
alt="Failed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -16,8 +16,19 @@ export default function Transaction_pending() {
|
||||
<div className={styles.Transactions}>
|
||||
<div className={containerStyle}>
|
||||
<div style={{ marginTop: "30px", textAlign: "center" }}>
|
||||
<h2>waiting for confirmation</h2>
|
||||
<ColorRing height="50" width="50" color="white" />
|
||||
<h2>Transation Pending</h2>
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
<ColorRing
|
||||
visible={true}
|
||||
height="80"
|
||||
width="80"
|
||||
ariaLabel="blocks-loading"
|
||||
wrapperStyle={{}}
|
||||
wrapperClass="blocks-wrapper"
|
||||
colors={["#4fa94d", "#f7c34c", "#ffa53c", "#e34f53", "#d23a8d"]}
|
||||
/>
|
||||
<p>Waiting for clerk confirmation...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -98,7 +98,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
|
||||
<div style={{ marginTop: "30px" }}></div>
|
||||
<h2 className={styles["Transactions-title"]}>Transactions</h2>
|
||||
<div style={{ marginTop: "30px" }}></div>
|
||||
<TableCanvas tables={tables} selectedTable={selectedTable} />
|
||||
{/* <TableCanvas tables={tables} selectedTable={selectedTable} /> */}
|
||||
<div className={styles.TransactionListContainer}>
|
||||
{transactions &&
|
||||
transactions.map((transaction) => (
|
||||
@@ -153,9 +153,11 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<h5 onClick={() => handleDecline(transaction.transactionId)}>
|
||||
decline
|
||||
</h5>
|
||||
{transaction.confirmed == 0 && (
|
||||
<h5 onClick={() => handleDecline(transaction.transactionId)}>
|
||||
decline
|
||||
</h5>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
|
||||
.TransactionListContainer {
|
||||
overflow-y: auto; /* Enables vertical scrolling */
|
||||
max-height: 600px; /* Adjust based on your design */
|
||||
}
|
||||
|
||||
.TotalContainer {
|
||||
|
||||
Reference in New Issue
Block a user