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/react": "^13.4.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
|
"jsqr": "^1.4.0",
|
||||||
"qrcode.react": "^3.1.0",
|
"qrcode.react": "^3.1.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-bootstrap": "^2.10.4",
|
"react-bootstrap": "^2.10.4",
|
||||||
@@ -12528,6 +12529,11 @@
|
|||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/jsx-ast-utils": {
|
||||||
"version": "3.3.5",
|
"version": "3.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
"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/react": "^13.4.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
|
"jsqr": "^1.4.0",
|
||||||
"qrcode.react": "^3.1.0",
|
"qrcode.react": "^3.1.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-bootstrap": "^2.10.4",
|
"react-bootstrap": "^2.10.4",
|
||||||
|
|||||||
21
src/App.js
21
src/App.js
@@ -123,9 +123,15 @@ function App() {
|
|||||||
//for guest
|
//for guest
|
||||||
socket.on("transaction_pending", async (data) => {
|
socket.on("transaction_pending", async (data) => {
|
||||||
console.log("transaction notification");
|
console.log("transaction notification");
|
||||||
|
// Call `setModal` with content and parameters
|
||||||
setModal("transaction_pending");
|
setModal("transaction_pending");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("transaction_confirmed", async (data) => {
|
||||||
|
console.log("transaction notification");
|
||||||
|
setModal("transaction_confirmed", data);
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("transaction_success", async (data) => {
|
socket.on("transaction_success", async (data) => {
|
||||||
console.log("transaction notification");
|
console.log("transaction notification");
|
||||||
setModal("transaction_success");
|
setModal("transaction_success");
|
||||||
@@ -207,11 +213,20 @@ function App() {
|
|||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
// Function to open the modal
|
// Function to open the modal
|
||||||
const setModal = (content) => {
|
const setModal = (content, params = {}) => {
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
setModalContent(content);
|
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";
|
document.body.style.overflow = "hidden";
|
||||||
navigate(`?modal=` + content, { replace: true });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to close the modal
|
// Function to close the modal
|
||||||
@@ -373,7 +388,7 @@ function App() {
|
|||||||
</Routes>
|
</Routes>
|
||||||
</header>
|
</header>
|
||||||
<Modal
|
<Modal
|
||||||
shopId={shopId}
|
shop={shop}
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
modalContent={modalContent}
|
modalContent={modalContent}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
|
|||||||
@@ -156,19 +156,18 @@ const shrink = keyframes`
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Rectangle = styled.div`
|
const Rectangle = styled.div`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 45px;
|
top: 45px;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: auto;
|
max-height: 87vh; /* or another appropriate value */
|
||||||
background-color: white;
|
background-color: white;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
animation: ${(props) => (props.animate === "grow" ? grow : shrink)} 0.5s
|
animation: ${(props) => (props.animate === "grow" ? grow : shrink)} 0.5s
|
||||||
forwards;
|
forwards;
|
||||||
overflow: hidden;
|
overflow-y: auto; /* Enable vertical scrolling */
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
`;
|
`;
|
||||||
@@ -246,6 +245,7 @@ const Header = ({
|
|||||||
if (rectangleRef.current && !rectangleRef.current.contains(event.target)) {
|
if (rectangleRef.current && !rectangleRef.current.contains(event.target)) {
|
||||||
setAnimate("shrink");
|
setAnimate("shrink");
|
||||||
setTimeout(() => setShowRectangle(false), 500);
|
setTimeout(() => setShowRectangle(false), 500);
|
||||||
|
rectangleRef.current.style.overflow = "hidden";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -304,7 +304,9 @@ const Header = ({
|
|||||||
<Child onClick={goToLogin}>Click to login</Child>
|
<Child onClick={goToLogin}>Click to login</Child>
|
||||||
)}
|
)}
|
||||||
{user.username !== undefined && (
|
{user.username !== undefined && (
|
||||||
<Child onClick={() => setModal("edit_account")}>Edit</Child>
|
<Child onClick={() => setModal("edit_account")}>
|
||||||
|
Edit profile
|
||||||
|
</Child>
|
||||||
)}
|
)}
|
||||||
{shopId &&
|
{shopId &&
|
||||||
user.userId == shopOwnerId &&
|
user.userId == shopOwnerId &&
|
||||||
@@ -312,11 +314,19 @@ const Header = ({
|
|||||||
user.roleId === 1 && (
|
user.roleId === 1 && (
|
||||||
<>
|
<>
|
||||||
<Child onClick={goToAdminCafes}>see your other cafes</Child>
|
<Child onClick={goToAdminCafes}>see your other cafes</Child>
|
||||||
<Child onClick={() => setModal("edit_tables")}>
|
<Child onClick={() => setModal("add_material")}>
|
||||||
{shopName} table maps
|
add material
|
||||||
|
</Child>
|
||||||
|
<Child onClick={() => setModal("update_stock")}>
|
||||||
|
update stock
|
||||||
</Child>
|
</Child>
|
||||||
<Child hasChildren>
|
<Child hasChildren>
|
||||||
{shopName} clerks
|
{shopName}
|
||||||
|
<Child onClick={() => setModal("edit_tables")}>
|
||||||
|
table maps
|
||||||
|
</Child>
|
||||||
|
<Child hasChildren>
|
||||||
|
clerks
|
||||||
<Child onClick={() => setModal("craete_account_clerk")}>
|
<Child onClick={() => setModal("craete_account_clerk")}>
|
||||||
+ Add clerk
|
+ Add clerk
|
||||||
</Child>
|
</Child>
|
||||||
@@ -326,7 +336,9 @@ const Header = ({
|
|||||||
{shopClerks[index].username}
|
{shopClerks[index].username}
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
removeConnectedGuestSides(guestSides[index][3])
|
removeConnectedGuestSides(
|
||||||
|
guestSides[index][3]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
remove
|
remove
|
||||||
@@ -334,31 +346,37 @@ const Header = ({
|
|||||||
</Child>
|
</Child>
|
||||||
))}
|
))}
|
||||||
</Child>
|
</Child>
|
||||||
<Child onClick={() => setModal("add_material")}>
|
<Child onClick={() => setModal("payment_option")}>
|
||||||
add material
|
payment options
|
||||||
|
</Child>
|
||||||
</Child>
|
</Child>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{user.username !== undefined &&
|
{user.username !== undefined &&
|
||||||
((user.userId == shopOwnerId && user.roleId === 1) ||
|
user.cafeId == shopId &&
|
||||||
(user.cafeId == shopId && user.roleId === 2)) && (
|
user.roleId === 2 && (
|
||||||
|
<Child hasChildren>
|
||||||
|
{shopName}
|
||||||
<Child onClick={() => setModal("update_stock")}>
|
<Child onClick={() => setModal("update_stock")}>
|
||||||
update stock
|
update stock
|
||||||
</Child>
|
</Child>
|
||||||
)}
|
|
||||||
{user.username !== undefined &&
|
{user.username !== undefined &&
|
||||||
user.roleId == 2 &&
|
user.roleId == 2 &&
|
||||||
user.cafeId == shopId && (
|
user.cafeId == shopId && (
|
||||||
<Child hasChildren>
|
<Child hasChildren>
|
||||||
connected guest sides
|
connected guest sides
|
||||||
<Child onClick={goToGuestSideLogin}>+ Add guest side</Child>
|
<Child onClick={goToGuestSideLogin}>
|
||||||
|
+ Add guest side
|
||||||
|
</Child>
|
||||||
{guestSides &&
|
{guestSides &&
|
||||||
guestSides.map((key, index) => (
|
guestSides.map((key, index) => (
|
||||||
<Child key={index}>
|
<Child key={index}>
|
||||||
guest side {index + 1}
|
guest side {index + 1}
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
removeConnectedGuestSides(guestSides[index][3])
|
removeConnectedGuestSides(
|
||||||
|
guestSides[index][3]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
remove
|
remove
|
||||||
@@ -367,6 +385,8 @@ const Header = ({
|
|||||||
))}
|
))}
|
||||||
</Child>
|
</Child>
|
||||||
)}
|
)}
|
||||||
|
</Child>
|
||||||
|
)}
|
||||||
{user.username !== undefined && (
|
{user.username !== undefined && (
|
||||||
<Child onClick={isLogout}>Logout</Child>
|
<Child onClick={isLogout}>Logout</Child>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -58,12 +58,22 @@ export default function ItemType({
|
|||||||
className={styles["item-type-image"]}
|
className={styles["item-type-image"]}
|
||||||
/>
|
/>
|
||||||
{blank && (
|
{blank && (
|
||||||
|
<div className={styles["item-type-image-container"]}>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
className={styles["item-type-image-input"]}
|
className={styles["item-type-image-input"]}
|
||||||
onChange={handleImageChange}
|
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>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -41,16 +41,24 @@
|
|||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-type-image-input {
|
.item-type-image-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.item-type-image-input {
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-type-create {
|
.item-type-create {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 80%; /* Position below the input */
|
top: 76%; /* Position below the input */
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
margin-top: 10px; /* Space between input and button */
|
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-lister">
|
||||||
<div className="item-type-list">
|
<div className="item-type-list">
|
||||||
{itemTypes && itemTypes.length > 1 && (
|
{itemTypes && itemTypes.length > 1 && (
|
||||||
<ItemType
|
<ItemType name={"All"} imageUrl={"uploads/1718732420960.png"} />
|
||||||
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="
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{itemTypes &&
|
{itemTypes &&
|
||||||
itemTypes.map(
|
itemTypes.map(
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import styles from "./Modal.module.css";
|
import styles from "./Modal.module.css";
|
||||||
import TablesPage from "./TablesPage.js";
|
import TablesPage from "./TablesPage.js";
|
||||||
|
import PaymentOptions from "./PaymentOptions.js";
|
||||||
import TableMaps from "../components/TableMaps";
|
import TableMaps from "../components/TableMaps";
|
||||||
import Transactions from "../pages/Transactions";
|
import Transactions from "../pages/Transactions";
|
||||||
import Transaction_pending from "../pages/Transaction_pending";
|
import Transaction_pending from "../pages/Transaction_pending";
|
||||||
|
import Transaction_confirmed from "../pages/Transaction_confirmed";
|
||||||
import Transaction_success from "../pages/Transaction_success";
|
import Transaction_success from "../pages/Transaction_success";
|
||||||
import Transaction_failed from "../pages/Transaction_failed";
|
import Transaction_failed from "../pages/Transaction_failed";
|
||||||
import MaterialList from "../pages/MaterialList.js";
|
import MaterialList from "../pages/MaterialList.js";
|
||||||
import MaterialMutationsPage from "../pages/MaterialMutationsPage.js";
|
import MaterialMutationsPage from "../pages/MaterialMutationsPage.js";
|
||||||
|
|
||||||
const Modal = ({ shopId, isOpen, onClose, modalContent }) => {
|
const Modal = ({ shop, isOpen, onClose, modalContent }) => {
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
// Function to handle clicks on the overlay
|
// Function to handle clicks on the overlay
|
||||||
@@ -30,16 +32,24 @@ const Modal = ({ shopId, isOpen, onClose, modalContent }) => {
|
|||||||
<button onClick={onClose} className={styles.closeButton}>
|
<button onClick={onClose} className={styles.closeButton}>
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
{modalContent === "edit_tables" && <TablesPage shopId={shopId} />}
|
{modalContent === "edit_tables" && <TablesPage shop={shop} />}
|
||||||
{modalContent === "new_transaction" && (
|
{modalContent === "new_transaction" && (
|
||||||
<Transactions propsShopId={shopId} />
|
<Transactions propsShopId={shop.cafeId} />
|
||||||
)}{" "}
|
)}{" "}
|
||||||
{modalContent === "transaction_pending" && <Transaction_pending />}
|
{modalContent === "transaction_pending" && <Transaction_pending />}
|
||||||
|
{modalContent === "transaction_confirmed" && (
|
||||||
|
<Transaction_confirmed paymentUrl={shop.qrPayment} />
|
||||||
|
)}
|
||||||
{modalContent === "transaction_success" && <Transaction_success />}
|
{modalContent === "transaction_success" && <Transaction_success />}
|
||||||
{modalContent === "transaction_failed" && <Transaction_failed />}
|
{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" && (
|
{modalContent === "update_stock" && (
|
||||||
<MaterialMutationsPage cafeId={shopId} />
|
<MaterialMutationsPage cafeId={shop.cafeId} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</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 React, { useState, useRef } from "react";
|
||||||
import html2canvas from "html2canvas";
|
import html2canvas from "html2canvas";
|
||||||
|
import { getImageUrl } from "../helpers/itemHelper";
|
||||||
|
import { saveCafeDetails } from "../helpers/cafeHelpers"; // Import the helper function
|
||||||
|
|
||||||
const QRCodeWithBackground = ({
|
const QRCodeWithBackground = ({
|
||||||
isConfigure,
|
isConfigure,
|
||||||
@@ -9,11 +11,12 @@ const QRCodeWithBackground = ({
|
|||||||
initialQrPosition,
|
initialQrPosition,
|
||||||
initialQrSize,
|
initialQrSize,
|
||||||
handleQrSave,
|
handleQrSave,
|
||||||
|
shopId, // Pass cafeId as a prop to identify which cafe to update
|
||||||
}) => {
|
}) => {
|
||||||
const [qrPosition, setQrPosition] = useState(initialQrPosition);
|
const [qrPosition, setQrPosition] = useState(initialQrPosition);
|
||||||
const [qrSize, setQrSize] = useState(initialQrSize);
|
const [qrSize, setQrSize] = useState(initialQrSize);
|
||||||
const [bgImage, setBgImage] = useState(backgroundUrl);
|
const [bgImage, setBgImage] = useState(getImageUrl(backgroundUrl)); // URL or File
|
||||||
const fileInputRef = useRef(null);
|
const qrBackgroundInputRef = useRef(null);
|
||||||
const overlayTextRef = useRef(null);
|
const overlayTextRef = useRef(null);
|
||||||
|
|
||||||
const handlePositionChange = (e) => {
|
const handlePositionChange = (e) => {
|
||||||
@@ -31,13 +34,30 @@ const QRCodeWithBackground = ({
|
|||||||
const handleFileChange = (e) => {
|
const handleFileChange = (e) => {
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
const newBgImage = URL.createObjectURL(file);
|
const newBgImage = URL.createObjectURL(file); // Create a temporary URL for display
|
||||||
setBgImage(newBgImage);
|
setBgImage(newBgImage);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
|
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);
|
handleQrSave(qrPosition, qrSize, bgImage);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error saving cafe details:", error);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const printQRCode = () => {
|
const printQRCode = () => {
|
||||||
@@ -118,7 +138,7 @@ const QRCodeWithBackground = ({
|
|||||||
position: "relative",
|
position: "relative",
|
||||||
width: "300px",
|
width: "300px",
|
||||||
height: "300px",
|
height: "300px",
|
||||||
background: `url(${bgImage}) no-repeat center center`,
|
background: `center center / contain no-repeat url(${bgImage})`,
|
||||||
backgroundSize: "contain",
|
backgroundSize: "contain",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
border: "1px solid #ddd",
|
border: "1px solid #ddd",
|
||||||
@@ -141,7 +161,7 @@ const QRCodeWithBackground = ({
|
|||||||
<div
|
<div
|
||||||
ref={overlayTextRef}
|
ref={overlayTextRef}
|
||||||
style={styles.overlayText}
|
style={styles.overlayText}
|
||||||
onClick={() => fileInputRef.current.click()}
|
onClick={() => qrBackgroundInputRef.current.click()}
|
||||||
>
|
>
|
||||||
Click To Change Image
|
Click To Change Image
|
||||||
</div>
|
</div>
|
||||||
@@ -150,7 +170,7 @@ const QRCodeWithBackground = ({
|
|||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
ref={fileInputRef}
|
ref={qrBackgroundInputRef}
|
||||||
style={{ display: "none" }}
|
style={{ display: "none" }}
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
@@ -276,16 +296,15 @@ const QRCodeWithBackground = ({
|
|||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
transition: "background-color 0.3s",
|
transition: "background-color 0.3s",
|
||||||
marginLeft: "10px",
|
|
||||||
}}
|
}}
|
||||||
onMouseOver={(e) =>
|
onMouseOver={(e) =>
|
||||||
(e.currentTarget.style.backgroundColor = "#138496")
|
(e.currentTarget.style.backgroundColor = "#117a8b")
|
||||||
}
|
}
|
||||||
onMouseOut={(e) =>
|
onMouseOut={(e) =>
|
||||||
(e.currentTarget.style.backgroundColor = "#17a2b8")
|
(e.currentTarget.style.backgroundColor = "#17a2b8")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Save Image
|
Save QR Code
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -293,54 +312,41 @@ const QRCodeWithBackground = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Styles for the configuration labels and inputs
|
|
||||||
const styles = {
|
const styles = {
|
||||||
label: {
|
overlayText: {
|
||||||
display: "block",
|
position: "absolute",
|
||||||
fontSize: "16px",
|
top: "50%",
|
||||||
fontWeight: "bold",
|
left: "50%",
|
||||||
marginBottom: "5px",
|
transform: "translate(-50%, -50%)",
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||||
|
color: "#fff",
|
||||||
|
padding: "10px",
|
||||||
|
borderRadius: "5px",
|
||||||
|
cursor: "pointer",
|
||||||
},
|
},
|
||||||
sliderContainer: {
|
sliderContainer: {
|
||||||
marginBottom: "20px",
|
marginBottom: "20px",
|
||||||
},
|
},
|
||||||
|
label: {
|
||||||
|
display: "block",
|
||||||
|
marginBottom: "10px",
|
||||||
|
},
|
||||||
sliderWrapper: {
|
sliderWrapper: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "10px",
|
|
||||||
marginTop: "5px",
|
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
flex: "1",
|
flex: "1",
|
||||||
margin: "0 10px",
|
margin: "0 10px",
|
||||||
},
|
},
|
||||||
value: {
|
|
||||||
fontSize: "16px",
|
|
||||||
minWidth: "50px",
|
|
||||||
textAlign: "right",
|
|
||||||
},
|
|
||||||
labelStart: {
|
labelStart: {
|
||||||
fontSize: "14px",
|
marginRight: "10px",
|
||||||
},
|
},
|
||||||
labelEnd: {
|
labelEnd: {
|
||||||
fontSize: "14px",
|
|
||||||
},
|
|
||||||
fileInput: {
|
|
||||||
marginLeft: "10px",
|
marginLeft: "10px",
|
||||||
},
|
},
|
||||||
overlayText: {
|
value: {
|
||||||
position: "absolute",
|
marginLeft: "10px",
|
||||||
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
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import QRCodeWithBackground from "./QR"; // Adjust path as needed
|
import QRCodeWithBackground from "./QR"; // Adjust path as needed
|
||||||
|
const TableList = ({ shop, tables, onSelectTable, selectedTable }) => {
|
||||||
const TableList = ({ shopUrl, tables, onSelectTable, selectedTable }) => {
|
const [initialPos, setInitialPos] = useState({
|
||||||
const [initialPos, setInitialPos] = useState({ left: 50, top: 50 });
|
left: shop.xposition,
|
||||||
const [initialSize, setInitialSize] = useState(20);
|
top: shop.yposition,
|
||||||
const [bgImageUrl, setBgImageUrl] = useState(
|
});
|
||||||
"https://example.com/your-background-image.png"
|
const [initialSize, setInitialSize] = useState(shop.scale);
|
||||||
);
|
const [bgImageUrl, setBgImageUrl] = useState(shop.qrBackground);
|
||||||
|
const shopUrl = window.location.hostname + "/" + shop.cafeId;
|
||||||
|
|
||||||
const generateQRCodeUrl = (tableCode) =>
|
const generateQRCodeUrl = (tableCode) =>
|
||||||
`https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(
|
`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>
|
<div style={{ marginBottom: "10px" }}>configure</div>
|
||||||
{-1 == selectedTable?.tableId && (
|
{-1 == selectedTable?.tableId && (
|
||||||
<QRCodeWithBackground
|
<QRCodeWithBackground
|
||||||
|
shopId={shop.cafeId}
|
||||||
isConfigure={true}
|
isConfigure={true}
|
||||||
handleQrSave={handleQrSave}
|
handleQrSave={handleQrSave}
|
||||||
setInitialPos={setInitialPos}
|
setInitialPos={setInitialPos}
|
||||||
@@ -62,7 +64,8 @@ const TableList = ({ shopUrl, tables, onSelectTable, selectedTable }) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
{tables
|
{tables &&
|
||||||
|
tables
|
||||||
.filter((table) => table.tableNo !== 0)
|
.filter((table) => table.tableNo !== 0)
|
||||||
.map((table) => (
|
.map((table) => (
|
||||||
<li
|
<li
|
||||||
@@ -91,6 +94,7 @@ const TableList = ({ shopUrl, tables, onSelectTable, selectedTable }) => {
|
|||||||
backgroundUrl={bgImageUrl}
|
backgroundUrl={bgImageUrl}
|
||||||
initialQrPosition={initialPos}
|
initialQrPosition={initialPos}
|
||||||
initialQrSize={initialSize}
|
initialQrSize={initialSize}
|
||||||
|
cafeId={shop.cafeId}
|
||||||
/>
|
/>
|
||||||
<h5>{shopUrl + "/" + table.tableCode}</h5>
|
<h5>{shopUrl + "/" + table.tableCode}</h5>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { getTables, updateTable, createTable } from "../helpers/tableHelper";
|
|||||||
import TableCanvas from "./TableCanvas";
|
import TableCanvas from "./TableCanvas";
|
||||||
import TableList from "./TableList";
|
import TableList from "./TableList";
|
||||||
|
|
||||||
const TablesPage = ({ shopId }) => {
|
const TablesPage = ({ shop }) => {
|
||||||
const [tables, setTables] = useState([]);
|
const [tables, setTables] = useState([]);
|
||||||
const [selectedTable, setSelectedTable] = useState(null);
|
const [selectedTable, setSelectedTable] = useState(null);
|
||||||
const [newTable, setNewTable] = useState(null);
|
const [newTable, setNewTable] = useState(null);
|
||||||
@@ -13,7 +13,8 @@ const TablesPage = ({ shopId }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
const fetchedTables = await getTables(shopId);
|
console.log(shop);
|
||||||
|
const fetchedTables = await getTables(shop.cafeId);
|
||||||
setTables(fetchedTables);
|
setTables(fetchedTables);
|
||||||
setOriginalTables(fetchedTables);
|
setOriginalTables(fetchedTables);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -22,7 +23,7 @@ const TablesPage = ({ shopId }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [shopId]);
|
}, [shop.cafeId]);
|
||||||
|
|
||||||
const handleAddTable = () => {
|
const handleAddTable = () => {
|
||||||
const nextId = Math.random().toString(36).substr(2, 11);
|
const nextId = Math.random().toString(36).substr(2, 11);
|
||||||
@@ -131,7 +132,7 @@ const TablesPage = ({ shopId }) => {
|
|||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
if (newTable) {
|
if (newTable) {
|
||||||
try {
|
try {
|
||||||
const createdTable = await createTable(shopId, {
|
const createdTable = await createTable(shop.cafeId, {
|
||||||
...newTable,
|
...newTable,
|
||||||
tableNo,
|
tableNo,
|
||||||
});
|
});
|
||||||
@@ -144,7 +145,7 @@ const TablesPage = ({ shopId }) => {
|
|||||||
}
|
}
|
||||||
} else if (selectedTable) {
|
} else if (selectedTable) {
|
||||||
try {
|
try {
|
||||||
const updatedTable = await updateTable(shopId, {
|
const updatedTable = await updateTable(shop.cafeId, {
|
||||||
...selectedTable,
|
...selectedTable,
|
||||||
tableNo,
|
tableNo,
|
||||||
});
|
});
|
||||||
@@ -202,7 +203,7 @@ const TablesPage = ({ shopId }) => {
|
|||||||
tableNo={tableNo}
|
tableNo={tableNo}
|
||||||
/> */}
|
/> */}
|
||||||
<TableList
|
<TableList
|
||||||
shopUrl={window.location.hostname + "/" + shopId}
|
shop={shop}
|
||||||
tables={tables}
|
tables={tables}
|
||||||
onSelectTable={handleSelect}
|
onSelectTable={handleSelect}
|
||||||
selectedTable={selectedTable}
|
selectedTable={selectedTable}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export async function getOwnedCafes(userId) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Failed to fetch cart details");
|
throw new Error("Failed to fetch cafes");
|
||||||
}
|
}
|
||||||
|
|
||||||
const cafes = await response.json();
|
const cafes = await response.json();
|
||||||
@@ -30,48 +30,85 @@ export async function getOwnedCafes(userId) {
|
|||||||
|
|
||||||
export async function createCafe(userId) {
|
export async function createCafe(userId) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(`${API_BASE_URL}/cafe/create-cafe`, {
|
||||||
`${API_BASE_URL}/cafe/get-cafe-by-ownerId/` + userId,
|
|
||||||
{
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${getAuthToken()}`,
|
Authorization: `Bearer ${getAuthToken()}`,
|
||||||
},
|
},
|
||||||
}
|
body: JSON.stringify({ ownerId: userId }),
|
||||||
);
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Failed to fetch cart details");
|
throw new Error("Failed to create cafe");
|
||||||
}
|
}
|
||||||
|
|
||||||
const cafes = await response.json();
|
const cafe = await response.json();
|
||||||
return cafes;
|
return cafe;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error:", error);
|
console.error("Error:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateCafe(userId) {
|
export async function updateCafe(cafeId, cafeDetails) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(`${API_BASE_URL}/cafe/update-cafe/${cafeId}`, {
|
||||||
`${API_BASE_URL}/cafe/get-cafe-by-ownerId/` + userId,
|
method: "PUT",
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${getAuthToken()}`,
|
Authorization: `Bearer ${getAuthToken()}`,
|
||||||
},
|
},
|
||||||
}
|
body: JSON.stringify(cafeDetails),
|
||||||
);
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Failed to fetch cart details");
|
throw new Error("Failed to update cafe");
|
||||||
}
|
}
|
||||||
|
|
||||||
const cafes = await response.json();
|
const updatedCafe = await response.json();
|
||||||
return cafes;
|
return updatedCafe;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error:", 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) {
|
export async function getTransactions(shopId, demand) {
|
||||||
try {
|
try {
|
||||||
const token = getLocalStorage("auth");
|
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>
|
<h2>transaction failed</h2>
|
||||||
<img
|
<img
|
||||||
className={styles.expression}
|
className={styles.expression}
|
||||||
src="https://i.imgur.com/5j3yIw6.png"
|
src="https://i.imgur.com/B6k9exa.png"
|
||||||
alt="Failed"
|
alt="Failed"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,8 +16,19 @@ export default function Transaction_pending() {
|
|||||||
<div className={styles.Transactions}>
|
<div className={styles.Transactions}>
|
||||||
<div className={containerStyle}>
|
<div className={containerStyle}>
|
||||||
<div style={{ marginTop: "30px", textAlign: "center" }}>
|
<div style={{ marginTop: "30px", textAlign: "center" }}>
|
||||||
<h2>waiting for confirmation</h2>
|
<h2>Transation Pending</h2>
|
||||||
<ColorRing height="50" width="50" color="white" />
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
|
|||||||
<div style={{ marginTop: "30px" }}></div>
|
<div style={{ marginTop: "30px" }}></div>
|
||||||
<h2 className={styles["Transactions-title"]}>Transactions</h2>
|
<h2 className={styles["Transactions-title"]}>Transactions</h2>
|
||||||
<div style={{ marginTop: "30px" }}></div>
|
<div style={{ marginTop: "30px" }}></div>
|
||||||
<TableCanvas tables={tables} selectedTable={selectedTable} />
|
{/* <TableCanvas tables={tables} selectedTable={selectedTable} /> */}
|
||||||
<div className={styles.TransactionListContainer}>
|
<div className={styles.TransactionListContainer}>
|
||||||
{transactions &&
|
{transactions &&
|
||||||
transactions.map((transaction) => (
|
transactions.map((transaction) => (
|
||||||
@@ -153,9 +153,11 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{transaction.confirmed == 0 && (
|
||||||
<h5 onClick={() => handleDecline(transaction.transactionId)}>
|
<h5 onClick={() => handleDecline(transaction.transactionId)}>
|
||||||
decline
|
decline
|
||||||
</h5>
|
</h5>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
|
|
||||||
.TransactionListContainer {
|
.TransactionListContainer {
|
||||||
overflow-y: auto; /* Enables vertical scrolling */
|
overflow-y: auto; /* Enables vertical scrolling */
|
||||||
max-height: 600px; /* Adjust based on your design */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.TotalContainer {
|
.TotalContainer {
|
||||||
|
|||||||
Reference in New Issue
Block a user