add stocking page

This commit is contained in:
zadit frontend
2024-08-03 07:29:36 +00:00
parent c4654ce64f
commit 5b4de33afb
21 changed files with 990 additions and 248 deletions

View File

@@ -23,6 +23,8 @@ import GuestSideLogin from "./pages/GuestSideLogin";
import GuestSide from "./pages/GuestSide"; import GuestSide from "./pages/GuestSide";
import { getItemTypesWithItems } from "./helpers/itemHelper.js"; import { getItemTypesWithItems } from "./helpers/itemHelper.js";
import MaterialList from "./pages/MaterialList";
import { import {
getConnectedGuestSides, getConnectedGuestSides,
getClerks, getClerks,
@@ -115,6 +117,17 @@ function App() {
}); });
} }
//for guest
socket.on("transaction_pending", async (data) => {
console.log("transaction notification");
setModal("transaction_pending");
});
socket.on("transaction_success", async (data) => {
console.log("transaction notification");
setModal("transaction_success");
});
//for clerk
socket.on("transaction_created", async (data) => { socket.on("transaction_created", async (data) => {
console.log("transaction notification"); console.log("transaction notification");
setModal("new_transaction"); setModal("new_transaction");
@@ -136,7 +149,7 @@ function App() {
} else { } else {
setDeviceType("guestDevice"); setDeviceType("guestDevice");
} }
if (data.data.user.roleId == 1) { if (data.data.user.roleId == 1 && user.userId == shop.ownerId) {
// shopClerks is can only be obtained by the shop owner // shopClerks is can only be obtained by the shop owner
// so every user that is admin will try to getting shopClerks, even not yet proven that this is their shop // so every user that is admin will try to getting shopClerks, even not yet proven that this is their shop
const shopClerks = await getClerks(shopId); const shopClerks = await getClerks(shopId);
@@ -209,7 +222,9 @@ function App() {
<Routes> <Routes>
<Route <Route
path="/" path="/"
element={<Dashboard user={user} setModal={setModal} />} element={
<Dashboard user={user} socket={socket} setModal={setModal} />
}
/> />
<Route path="/login" element={<LoginPage />} /> <Route path="/login" element={<LoginPage />} />
<Route <Route
@@ -219,6 +234,7 @@ function App() {
<CafePage <CafePage
sendParam={handleSetParam} sendParam={handleSetParam}
shopName={shop.name} shopName={shop.name}
shopOwnerId={shop.ownerId}
shopItems={shopItems} shopItems={shopItems}
shopClerks={shopClerks} shopClerks={shopClerks}
socket={socket} socket={socket}
@@ -241,7 +257,8 @@ function App() {
path="/:shopId/:tableId?/search" path="/:shopId/:tableId?/search"
element={ element={
<> <>
<SearchResult <MaterialList
cafeId={shopId}
sendParam={handleSetParam} sendParam={handleSetParam}
user={user} user={user}
shopItems={shopItems} shopItems={shopItems}
@@ -281,7 +298,11 @@ function App() {
path="/:shopId/:tableId?/invoice" path="/:shopId/:tableId?/invoice"
element={ element={
<> <>
<Invoice sendParam={handleSetParam} deviceType={deviceType} /> <Invoice
sendParam={handleSetParam}
socket={socket}
deviceType={deviceType}
/>
<Footer <Footer
shopId={shopId} shopId={shopId}
tableId={tableId} tableId={tableId}

View File

@@ -213,6 +213,7 @@ const Header = ({
HeaderText, HeaderText,
shopId, shopId,
shopName, shopName,
shopOwnerId,
shopClerks, shopClerks,
tableId, tableId,
showProfile, showProfile,
@@ -305,7 +306,10 @@ const Header = ({
{user.username !== undefined && ( {user.username !== undefined && (
<Child onClick={() => setModal("edit_account")}>Edit</Child> <Child onClick={() => setModal("edit_account")}>Edit</Child>
)} )}
{shopId && user.username !== undefined && user.roleId === 1 && ( {shopId &&
user.userId == shopOwnerId &&
user.username !== undefined &&
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("edit_tables")}>
@@ -330,15 +334,21 @@ const Header = ({
</Child> </Child>
))} ))}
</Child> </Child>
<Child onClick={() => setModal("add_material")}>
add material
</Child>
</> </>
)} )}
{user.username !== undefined && {user.username !== undefined &&
(user.roleId === 1 || user.roleId === 2) && ( ((user.userId == shopOwnerId && user.roleId === 1) ||
(user.cafeId == shopId && user.roleId === 2)) && (
<Child onClick={() => setModal("update_stock")}> <Child onClick={() => setModal("update_stock")}>
update stock update stock
</Child> </Child>
)} )}
{user.username !== undefined && user.roleId === 2 && ( {user.username !== undefined &&
user.roleId == 2 &&
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>

View File

@@ -17,6 +17,7 @@ const ItemLister = ({
itemTypeId, itemTypeId,
refreshTotal, refreshTotal,
shopId, shopId,
shopOwnerId,
user, user,
typeName, typeName,
itemList, itemList,
@@ -27,7 +28,7 @@ const ItemLister = ({
itemList.map((item) => ({ itemList.map((item) => ({
...item, ...item,
qty: getItemQtyFromCart(shopId, item.itemId), qty: getItemQtyFromCart(shopId, item.itemId),
})), }))
); );
const [isEdit, setIsEditing] = useState(false); const [isEdit, setIsEditing] = useState(false);
const [isAddingNewItem, setIsAddingNewItem] = useState(false); const [isAddingNewItem, setIsAddingNewItem] = useState(false);
@@ -111,17 +112,22 @@ const ItemLister = ({
return ( return (
<> <>
{(items.length > 0 || (user && user.roleId == 1)) && ( {(items.length > 0 ||
(user && user.roleId == 1 && user.userId == shopOwnerId)) && (
<div className={styles["item-lister"]}> <div className={styles["item-lister"]}>
<div className={styles["title-container"]}> <div className={styles["title-container"]}>
<input <input
ref={typeNameInputRef} ref={typeNameInputRef}
className={`${styles.title} ${user && user.roleId == 1 && isEdit ? styles.border : styles.noborder}`} className={`${styles.title} ${
user && user.roleId == 1 && user.userId == shopOwnerId && isEdit
? styles.border
: styles.noborder
}`}
value={editedTypeName} value={editedTypeName}
onChange={(e) => setEditedTypeName(e.target.value)} onChange={(e) => setEditedTypeName(e.target.value)}
disabled={!isEdit} disabled={!isEdit}
/> />
{user && user.roleId == 1 && ( {user && user.roleId == 1 && user.userId == shopOwnerId && (
<> <>
<button <button
className={styles["edit-typeItem-button"]} className={styles["edit-typeItem-button"]}
@@ -141,7 +147,10 @@ const ItemLister = ({
)} )}
</div> </div>
<div className={styles["item-list"]}> <div className={styles["item-list"]}>
{user && user.roleId == 1 && isEdit && ( {user &&
user.roleId == 1 &&
user.userId == shopOwnerId &&
isEdit && (
<> <>
<button <button
className={styles["add-item-button"]} className={styles["add-item-button"]}
@@ -159,7 +168,7 @@ const ItemLister = ({
price, price,
qty, qty,
selectedImage, selectedImage,
itemTypeId, itemTypeId
) )
} }
/> />
@@ -183,7 +192,10 @@ const ItemLister = ({
) : null; ) : null;
})} })}
{user && user.roleId == 1 && isEdit && ( {user &&
user.roleId == 1 &&
user.userId == shopOwnerId &&
isEdit && (
<> <>
<button <button
className={styles["add-item-button"]} className={styles["add-item-button"]}

View File

@@ -1,8 +1,14 @@
import React, { useRef, useEffect, useState } from 'react'; import React, { useRef, useEffect, useState } from "react";
import styles from './ItemType.module.css'; import styles from "./ItemType.module.css";
import { getImageUrl } from '../helpers/itemHelper'; import { getImageUrl } from "../helpers/itemHelper";
export default function ItemType({ onClick, onCreate, blank, name: initialName = '', imageUrl }) { export default function ItemType({
onClick,
onCreate,
blank,
name: initialName = "",
imageUrl,
}) {
const inputRef = useRef(null); const inputRef = useRef(null);
const [name, setName] = useState(initialName); const [name, setName] = useState(initialName);
const [selectedImage, setSelectedImage] = useState(null); const [selectedImage, setSelectedImage] = useState(null);
@@ -36,7 +42,7 @@ export default function ItemType({ onClick, onCreate, blank, name: initialName =
const handleCreate = async () => { const handleCreate = async () => {
if (!selectedImage) { if (!selectedImage) {
console.error('No image selected'); console.error("No image selected");
return; return;
} }
@@ -46,7 +52,11 @@ export default function ItemType({ onClick, onCreate, blank, name: initialName =
return ( return (
<div className={styles["item-type"]}> <div className={styles["item-type"]}>
<div onClick={onClick} className={styles["item-type-rect"]}> <div onClick={onClick} className={styles["item-type-rect"]}>
<img src={previewUrl} alt={name} className={styles["item-type-image"]} /> <img
src={previewUrl}
alt={name}
className={styles["item-type-image"]}
/>
{blank && ( {blank && (
<input <input
type="file" type="file"
@@ -58,7 +68,9 @@ export default function ItemType({ onClick, onCreate, blank, name: initialName =
</div> </div>
<input <input
ref={inputRef} ref={inputRef}
className={`${styles["item-type-name"]} ${blank ? styles.border : styles.noborder}`} className={`${styles["item-type-name"]} ${
blank ? styles.border : styles.noborder
}`}
value={name} value={name}
onChange={handleNameChange} onChange={handleNameChange}
disabled={!blank} disabled={!blank}

View File

@@ -1,16 +1,14 @@
.item-type { .item-type {
width: calc(25vw - 20px); width: calc(25vw - 20px);
/* Adjust size as needed, subtracting margin */
height: calc(39vw - 20px); height: calc(39vw - 20px);
/* Adjust size as needed */
margin: 1px 10px -5px; margin: 1px 10px -5px;
/* Left and right margin */
overflow: hidden; overflow: hidden;
text-align: center; text-align: center;
align-items: center; align-items: center;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
position: relative; /* Ensure absolute positioning inside works */
} }
.item-type-rect { .item-type-rect {
@@ -19,27 +17,21 @@
width: 20vw; width: 20vw;
object-fit: cover; object-fit: cover;
border-radius: 15px; border-radius: 15px;
/* Rounded corners */
background-color: #fff; background-color: #fff;
/* Background color of the item */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
/* Optional: Shadow for better visual */
} }
.item-type-name { .item-type-name {
position: relative;
font-family: "Poppins", sans-serif; font-family: "Poppins", sans-serif;
font-weight: 500; font-weight: 500;
font-style: normal; font-style: normal;
top: 13px; margin-bottom: 10px; /* Adjust margin for spacing */
margin-bottom: 14px;
font-size: 14px; font-size: 14px;
/* Adjust font size as needed */
color: #333; color: #333;
width: calc(25vw - 30px); width: calc(25vw - 30px);
/* Adjust size as needed, subtracting margin */
text-align: center; text-align: center;
background-color: transparent; background-color: transparent;
position: relative; /* Needed for positioning the button */
} }
.item-type-image { .item-type-image {
@@ -47,7 +39,6 @@
height: 100%; height: 100%;
object-fit: contain; object-fit: contain;
border-radius: 15px; border-radius: 15px;
/* Rounded corners */
} }
.item-type-image-input { .item-type-image-input {
@@ -59,8 +50,12 @@
.item-type-create { .item-type-create {
position: absolute; position: absolute;
margin-top: 130px; top: 80%; /* Position below the input */
left: 50%;
transform: translateX(-50%);
margin-top: 10px; /* Space between input and button */
width: 20vw; width: 20vw;
text-align: center; /* Center button text */
} }
.border { .border {

View File

@@ -1,44 +1,64 @@
import React, { useState } from 'react'; import React, { useState } from "react";
import './ItemTypeLister.css'; import "./ItemTypeLister.css";
import ItemType from './ItemType'; import ItemType from "./ItemType";
import { createItemType } from '../helpers/itemHelper.js'; import { createItemType } from "../helpers/itemHelper.js";
const ItemTypeLister = ({ shopId, user, itemTypes }) => { const ItemTypeLister = ({ shopId, shopOwnerId, user, itemTypes }) => {
const [isAddingNewItem, setIsAddingNewItem] = useState(false); const [isAddingNewItem, setIsAddingNewItem] = useState(false);
const toggleAddNewItem = () => { const toggleAddNewItem = () => {
console.log("aaa") console.log("aaa");
setIsAddingNewItem(prev => !prev); setIsAddingNewItem((prev) => !prev);
}; };
async function handleCreate(name, selectedImage) { async function handleCreate(name, selectedImage) {
createItemType(shopId, name, selectedImage); createItemType(shopId, name, selectedImage);
}; }
return ( return (
<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 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={
"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.map(itemType => ( />
(user && user.roleId == 1 || itemType.itemList.length > 0) && ( )}
{itemTypes &&
itemTypes.map(
(itemType) =>
((user && user.roleId == 1 && user.userId == shopOwnerId) ||
itemType.itemList.length > 0) && (
<ItemType <ItemType
key={itemType.itemTypeId} key={itemType.itemTypeId}
name={itemType.name} name={itemType.name}
imageUrl={itemType.image} imageUrl={itemType.image}
/> />
) )
))} )}
{user && user.roleId == 1 && isAddingNewItem && {user &&
<ItemType blank={true} name={"blank"} onCreate = {handleCreate} /> user.roleId == 1 &&
} user.userId == shopOwnerId &&
{user && user.roleId == 1 && isAddingNewItem && (
<ItemType onClick = {toggleAddNewItem} name={"create"} imageUrl={"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQnd07OYAm1f7T6JzziFU7U8X1_IL3bADiVrg&usqp=CAU"} /> <ItemType blank={true} name={"blank"} onCreate={handleCreate} />
)}
{!isAddingNewItem &&
user &&
user.roleId == 1 &&
user.userId == shopOwnerId && (
<ItemType
onClick={toggleAddNewItem}
name={"create"}
imageUrl={
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQnd07OYAm1f7T6JzziFU7U8X1_IL3bADiVrg&usqp=CAU"
} }
/>
)}
</div> </div>
</div> </div>
); );
} };
export default ItemTypeLister; export default ItemTypeLister;

View File

@@ -3,6 +3,10 @@ import styles from "./Modal.module.css";
import TablesPage from "./TablesPage.js"; import TablesPage from "./TablesPage.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_success from "../pages/Transaction_success";
import MaterialList from "../pages/MaterialList.js";
import MaterialMutationsPage from "../pages/MaterialMutationsPage.js";
const Modal = ({ shopId, isOpen, onClose, modalContent }) => { const Modal = ({ shopId, isOpen, onClose, modalContent }) => {
if (!isOpen) return null; if (!isOpen) return null;
@@ -28,6 +32,12 @@ const Modal = ({ shopId, isOpen, onClose, modalContent }) => {
{modalContent === "edit_tables" && <TablesPage shopId={shopId} />} {modalContent === "edit_tables" && <TablesPage shopId={shopId} />}
{modalContent === "new_transaction" && ( {modalContent === "new_transaction" && (
<Transactions propsShopId={shopId} /> <Transactions propsShopId={shopId} />
)}{" "}
{modalContent === "transaction_pending" && <Transaction_pending />}
{modalContent === "transaction_success" && <Transaction_success />}
{modalContent === "add_material" && <MaterialList cafeId={shopId} />}
{modalContent === "update_stock" && (
<MaterialMutationsPage cafeId={shopId} />
)} )}
</div> </div>
</div> </div>

View File

@@ -13,13 +13,12 @@
.modalContent { .modalContent {
background: white; background: white;
padding: 20px;
border-radius: 5px; border-radius: 5px;
width: 80%; width: 90%;
height: 80%; height: 80%;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
position: relative; position: relative;
overflow: auto; /* Add this line to enable scrolling */ overflow: hidden; /* Add this line to enable scrolling */
} }
.closeButton { .closeButton {

View File

@@ -4,7 +4,7 @@ const TableCanvas = ({
tables, tables,
selectedTable, selectedTable,
newTable, newTable,
setTableNo, isAdmin,
handleSelectTable, handleSelectTable,
handleAddTable, handleAddTable,
moveTable, moveTable,
@@ -91,11 +91,12 @@ const TableCanvas = ({
context.fillStyle = "white"; context.fillStyle = "white";
context.textAlign = "center"; context.textAlign = "center";
context.textBaseline = "middle"; context.textBaseline = "middle";
console.log(selectedTable);
context.fillText( context.fillText(
table.tableId === (selectedTable?.tableId || newTable?.tableId) table.tableId === (selectedTable?.tableId || newTable?.tableId)
? tableNo === 0 ? tableNo === 0
? "clerk" ? "clerk"
: tableNo : tableNo || (isAdmin ? "" : table.tableNo)
: table.tableNo === 0 : table.tableNo === 0
? "clerk" ? "clerk"
: table.tableNo, : table.tableNo,
@@ -106,7 +107,7 @@ const TableCanvas = ({
}, [tables, canvasSize, newTable, selectedTable, tableNo]); }, [tables, canvasSize, newTable, selectedTable, tableNo]);
return ( return (
<div ref={containerRef} style={{ width: "100%", height: "100%" }}> <div ref={containerRef} style={{ height: "20%" }}>
<canvas <canvas
ref={canvasRef} ref={canvasRef}
style={{ style={{
@@ -116,7 +117,7 @@ const TableCanvas = ({
}} }}
onClick={handleSelectTable} onClick={handleSelectTable}
/> />
<div> <div style={{ visibility: isAdmin ? "" : "hidden" }}>
{!selectedTable && !newTable && ( {!selectedTable && !newTable && (
<button <button
onClick={handleAddTable} onClick={handleAddTable}
@@ -127,11 +128,11 @@ const TableCanvas = ({
backgroundColor: "#007bff", backgroundColor: "#007bff",
color: "#fff", color: "#fff",
border: "none", border: "none",
borderRadius: "4px",
fontSize: "16px", fontSize: "16px",
cursor: "pointer", cursor: "pointer",
marginBottom: "10px", marginBottom: "10px",
transition: "background-color 0.3s ease", transition: "background-color 0.3s ease",
visibility: isAdmin ? "" : "hidden",
}} }}
> >
Add Table Add Table
@@ -143,6 +144,7 @@ const TableCanvas = ({
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
display: isAdmin ? "visible" : "hidden",
}} }}
> >
<div <div

View File

@@ -175,9 +175,18 @@ const TablesPage = ({ shopId }) => {
return ( return (
<div <div
style={{ display: "flex", flexDirection: "column", alignItems: "center" }} style={{
overflowX: "hidden", // Correct property name for horizontal overflow
backgroundColor: "#e9e9e9", // Remove duplicate property
display: "flex",
flexDirection: "column",
justifyContent: "center",
fontSize: "calc(10px + 2vmin)",
color: "rgba(88, 55, 50, 1)",
}}
> >
<TableCanvas <TableCanvas
isAdmin={true}
tables={tables} tables={tables}
selectedTable={selectedTable} selectedTable={selectedTable}
newTable={newTable} newTable={newTable}

View File

@@ -0,0 +1,72 @@
import API_BASE_URL from "../config.js";
// Function to retrieve the authentication token from localStorage
function getAuthToken() {
return localStorage.getItem("auth");
}
// Helper function to get headers with authentication token
const getHeaders = (method = "GET") => {
const headers = {
Authorization: `Bearer ${getAuthToken()}`,
};
// No content-type header needed for FormData; fetch will handle it automatically
if (method !== "POST" && method !== "PUT") {
return { method, headers };
}
return {
method,
headers,
};
};
// Create a new material
export const createMaterial = async (cafeId, data) => {
console.log(cafeId);
const response = await fetch(`${API_BASE_URL}/material/create/${cafeId}`, {
...getHeaders("POST"),
body: data, // Assuming data is FormData with image
});
return response.json();
};
// Get all materials for a specific cafe
export const getMaterials = async (cafeId) => {
const response = await fetch(
`${API_BASE_URL}/material/get-materials/${cafeId}`,
getHeaders()
);
return response.json();
};
// Get a single material by its ID
export const getMaterialById = async (materialId) => {
const response = await fetch(
`${API_BASE_URL}/material/get-material/${materialId}`,
getHeaders()
);
return response.json();
};
// Update a material by its ID
export const updateMaterial = async (materialId, data) => {
const response = await fetch(
`${API_BASE_URL}/material/update-material/${materialId}`,
{
...getHeaders("PUT"),
body: data, // Assuming data is FormData with image
}
);
return response.json();
};
// Delete a material by its ID
export const deleteMaterial = async (materialId) => {
const response = await fetch(
`${API_BASE_URL}/material/delete-material/${materialId}`,
getHeaders("DELETE")
);
return response.json();
};

View File

@@ -0,0 +1,65 @@
import API_BASE_URL from "../config.js";
// Function to retrieve the authentication token from localStorage
function getAuthToken() {
return localStorage.getItem("auth");
}
// Helper function to get headers with authentication token
const getHeaders = (method = "GET", body = null) => {
const headers = {
Authorization: `Bearer ${getAuthToken()}`,
"Content-Type": "application/json",
};
return {
method,
headers,
body: body ? JSON.stringify(body) : null,
};
};
// Create a new material mutation
export const createMaterialMutation = async (materialId, data) => {
try {
const response = await fetch(
`${API_BASE_URL}/mutation/create/${materialId}`,
getHeaders("POST", data)
);
if (!response.ok) throw new Error("Failed to create material mutation.");
return await response.json();
} catch (error) {
console.error(error);
throw error;
}
};
// Get all material mutations for a specific cafe
export const getMaterialMutations = async (cafeId) => {
try {
const response = await fetch(
`${API_BASE_URL}/mutation/get-material-mutations/${cafeId}`,
getHeaders()
);
if (!response.ok) throw new Error("Failed to retrieve material mutations.");
return await response.json();
} catch (error) {
console.error(error);
throw error;
}
};
// Get a single material mutation by its ID
export const getMaterialMutationById = async (mutationId) => {
try {
const response = await fetch(
`${API_BASE_URL}/mutation/get-material-mutation/${mutationId}`,
getHeaders()
);
if (!response.ok) throw new Error("Failed to retrieve material mutation.");
return await response.json();
} catch (error) {
console.error(error);
throw error;
}
};

View File

@@ -2,6 +2,29 @@ import API_BASE_URL from "../config.js";
import { getLocalStorage, updateLocalStorage } from "./localStorageHelpers"; import { getLocalStorage, updateLocalStorage } from "./localStorageHelpers";
import { getItemsByCafeId } from "../helpers/cartHelpers.js"; import { getItemsByCafeId } from "../helpers/cartHelpers.js";
export async function confirmTransaction(transactionId) {
try {
const token = getLocalStorage("auth");
const response = await fetch(
`${API_BASE_URL}/transaction/confirm-transaction/${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 getTransactions(shopId, demand) { export async function getTransactions(shopId, demand) {
try { try {
const token = getLocalStorage("auth"); const token = getLocalStorage("auth");
@@ -140,7 +163,8 @@ export const handlePaymentFromGuestDevice = async (
shopId, shopId,
payment_type, payment_type,
serving_type, serving_type,
tableNo tableNo,
socketId
) => { ) => {
try { try {
const token = getLocalStorage("auth"); const token = getLocalStorage("auth");
@@ -167,6 +191,7 @@ export const handlePaymentFromGuestDevice = async (
serving_type, serving_type,
tableNo, tableNo,
transactions: structuredItems, transactions: structuredItems,
socketId,
}), }),
} }
); );

View File

@@ -22,6 +22,7 @@ import {
function CafePage({ function CafePage({
sendParam, sendParam,
shopName, shopName,
shopOwnerId,
shopItems, shopItems,
shopClerks, shopClerks,
socket, socket,
@@ -100,6 +101,7 @@ function CafePage({
isLogout={handleLogout} isLogout={handleLogout}
shopId={shopId} shopId={shopId}
shopName={shopName} shopName={shopName}
shopOwnerId={shopOwnerId}
shopClerks={shopClerks} shopClerks={shopClerks}
tableId={tableId} tableId={tableId}
user={user} user={user}
@@ -110,7 +112,12 @@ function CafePage({
<div style={{ marginTop: "5px" }}></div> <div style={{ marginTop: "5px" }}></div>
<SearchInput shopId={shopId} /> <SearchInput shopId={shopId} />
<div style={{ marginTop: "15px" }}></div> <div style={{ marginTop: "15px" }}></div>
<ItemTypeLister user={user} shopId={shopId} itemTypes={shopItems} /> <ItemTypeLister
user={user}
shopOwnerId={shopOwnerId}
shopId={shopId}
itemTypes={shopItems}
/>
<div style={{ marginTop: "-13px" }}></div> <div style={{ marginTop: "-13px" }}></div>
<h2 className="title">Music Req.</h2> <h2 className="title">Music Req.</h2>
<MusicPlayer <MusicPlayer
@@ -123,6 +130,7 @@ function CafePage({
{shopItems.map((itemType) => ( {shopItems.map((itemType) => (
<ItemLister <ItemLister
shopId={shopId} shopId={shopId}
shopOwnerId={shopOwnerId}
user={user} user={user}
key={itemType.itemTypeId} key={itemType.itemTypeId}
itemTypeId={itemType.itemTypeId} itemTypeId={itemType.itemTypeId}

View File

@@ -11,7 +11,7 @@ import {
handlePaymentFromGuestDevice, handlePaymentFromGuestDevice,
} from "../helpers/transactionHelpers"; } from "../helpers/transactionHelpers";
export default function Invoice({ sendParam, deviceType }) { export default function Invoice({ sendParam, deviceType, socket }) {
const { shopId, tableId } = useParams(); const { shopId, tableId } = useParams();
sendParam({ shopId, tableId }); sendParam({ shopId, tableId });
@@ -71,11 +71,13 @@ export default function Invoice({ sendParam, deviceType }) {
tableNumber tableNumber
); );
} else if (deviceType == "guestDevice") { } else if (deviceType == "guestDevice") {
const socketId = socket.id;
const pay = await handlePaymentFromGuestDevice( const pay = await handlePaymentFromGuestDevice(
shopId, shopId,
isCash ? "cash" : "cashless", isCash ? "cash" : "cashless",
orderType, orderType,
tableNumber tableNumber,
socketId
); );
} }

267
src/pages/MaterialList.js Normal file
View File

@@ -0,0 +1,267 @@
import React, { useState, useEffect } from "react";
import API_BASE_URL from "../config.js";
import {
getMaterials,
createMaterial,
deleteMaterial,
} from "../helpers/materialHelpers"; // Update import
const MaterialList = ({ cafeId }) => {
const [materials, setMaterials] = useState([]);
const [newMaterialName, setNewMaterialName] = useState("");
const [newMaterialUnit, setNewMaterialUnit] = useState("kilogram");
const [newMaterialImage, setNewMaterialImage] = useState(null);
const [deleting, setDeleting] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [showForm, setShowForm] = useState(false); // For form visibility
useEffect(() => {
const fetchMaterials = async () => {
setLoading(true);
try {
const data = await getMaterials(cafeId);
setMaterials(data);
setError(null); // Clear any previous error
} catch (error) {
console.error("Error fetching materials:", error);
setError("Failed to fetch materials.");
} finally {
setLoading(false);
}
};
fetchMaterials();
}, [cafeId]);
const handleCreateMaterial = async (e) => {
e.preventDefault();
setLoading(true);
const formData = new FormData();
formData.append("name", newMaterialName);
formData.append("unit", newMaterialUnit);
if (newMaterialImage) {
formData.append("image", newMaterialImage);
}
try {
await createMaterial(cafeId, formData);
setNewMaterialName("");
setNewMaterialUnit("kilogram");
setNewMaterialImage(null);
setShowForm(false); // Hide the form after successful creation
const data = await getMaterials(cafeId);
setMaterials(data);
setError(null); // Clear any previous error
} catch (error) {
console.error("Error creating material:", error);
setError("Failed to create material.");
} finally {
setLoading(false);
}
};
const handleDeleteMaterial = async (materialId) => {
setDeleting(materialId);
try {
await deleteMaterial(materialId);
setMaterials(
materials.filter((material) => material.materialId !== materialId)
);
setError(null); // Clear any previous error
} catch (error) {
console.error("Error deleting material:", error);
setError("Failed to delete material.");
} finally {
setDeleting(null);
}
};
return (
<div style={styles.container}>
<h2>Materials List</h2>
{/* Display error message if any */}
{error && <p style={styles.error}>{error}</p>}
{/* Button to toggle the form */}
<button
onClick={() => setShowForm(!showForm)}
style={styles.toggleButton}
disabled={loading}
>
{showForm ? "Hide Form" : "Add New Material"}
</button>
{/* Create Material Form */}
<div
style={{
...styles.formContainer,
height: showForm ? "auto" : "0",
overflow: "hidden",
}}
>
<form onSubmit={handleCreateMaterial} style={styles.form}>
<div style={styles.formGroup}>
<label htmlFor="materialName" style={styles.label}>
Name:
</label>
<input
id="materialName"
type="text"
value={newMaterialName}
onChange={(e) => setNewMaterialName(e.target.value)}
required
style={styles.input}
/>
</div>
<div style={styles.formGroup}>
<label htmlFor="materialUnit" style={styles.label}>
Unit:
</label>
<select
id="materialUnit"
value={newMaterialUnit}
onChange={(e) => setNewMaterialUnit(e.target.value)}
style={styles.input}
>
<option value="kilogram">Kilogram</option>
<option value="liter">Liter</option>
<option value="piece">Piece</option>
</select>
</div>
<div style={styles.formGroup}>
<label htmlFor="materialImage" style={styles.label}>
Image:
</label>
<input
id="materialImage"
type="file"
onChange={(e) => setNewMaterialImage(e.target.files[0])}
style={styles.input}
/>
</div>
<button type="submit" style={styles.button} disabled={loading}>
{loading ? "Creating..." : "Create Material"}
</button>
</form>
</div>
{/* Materials List */}
{loading ? (
<p>Loading materials...</p>
) : (
<ul style={styles.list}>
{materials.map((material) => (
<li key={material.materialId} style={styles.listItem}>
{material.name} - {material.unit}
{material.image && (
<img
src={`${API_BASE_URL}/uploads/${material.image}`}
alt={material.name}
style={styles.image}
/>
)}
<button
onClick={() => handleDeleteMaterial(material.materialId)}
disabled={deleting === material.materialId || loading}
style={styles.deleteButton}
>
{deleting === material.materialId ? "Deleting..." : "Delete"}
</button>
</li>
))}
</ul>
)}
</div>
);
};
// Styles
const styles = {
container: {
padding: "20px",
maxWidth: "600px",
margin: "0 auto",
backgroundColor: "#f9f9f9",
borderRadius: "8px",
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
},
toggleButton: {
marginBottom: "20px",
padding: "10px 15px",
border: "none",
borderRadius: "4px",
backgroundColor: "#007bff",
color: "#fff",
cursor: "pointer",
fontSize: "16px",
transition: "background-color 0.3s",
},
formContainer: {
transition: "height 0.5s ease-in-out",
overflow: "hidden",
},
form: {
marginBottom: "20px",
},
formGroup: {
marginBottom: "10px",
},
label: {
display: "block",
marginBottom: "5px",
fontWeight: "bold",
},
input: {
width: "100%",
padding: "8px",
border: "1px solid #ddd",
borderRadius: "4px",
boxSizing: "border-box",
},
button: {
padding: "10px 15px",
border: "none",
borderRadius: "4px",
backgroundColor: "#28a745",
color: "#fff",
cursor: "pointer",
fontSize: "16px",
},
deleteButton: {
marginLeft: "10px",
padding: "5px 10px",
border: "none",
borderRadius: "4px",
backgroundColor: "#dc3545",
color: "#fff",
cursor: "pointer",
fontSize: "14px",
},
list: {
listStyleType: "none",
padding: "0",
margin: "0",
},
listItem: {
padding: "10px",
borderBottom: "1px solid #ddd",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
},
image: {
marginLeft: "10px",
height: "50px",
width: "50px",
objectFit: "cover",
},
error: {
color: "#dc3545",
marginBottom: "15px",
},
};
export default MaterialList;

View File

@@ -0,0 +1,181 @@
// src/pages/MaterialMutationPage.js
import React, { useEffect, useState } from "react";
import { getMaterials } from "../helpers/materialHelpers";
import {
createMaterialMutation,
getMaterialMutations,
} from "../helpers/materialMutationHelpers";
const MaterialMutationPage = ({ cafeId }) => {
const [materials, setMaterials] = useState([]);
const [materialMutations, setMaterialMutations] = useState([]);
const [selectedMaterialId, setSelectedMaterialId] = useState("");
const [oldStock, setOldStock] = useState("");
const [newStock, setNewStock] = useState("");
const [changeDate, setChangeDate] = useState("");
const [reason, setReason] = useState("");
const [successMessage, setSuccessMessage] = useState("");
const [error, setError] = useState("");
// Fetch materials when the component mounts
useEffect(() => {
const fetchMaterials = async () => {
try {
const data = await getMaterials(cafeId);
setMaterials(data);
} catch (err) {
setError(err.message);
}
};
fetchMaterials();
}, [cafeId]);
// Fetch material mutations when the component mounts
useEffect(() => {
const fetchMaterialMutations = async () => {
try {
const data = await getMaterialMutations(cafeId);
setMaterialMutations(data);
} catch (err) {
setError(err.message);
}
};
fetchMaterialMutations();
}, [cafeId]);
// Handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
if (!selectedMaterialId) {
setError("Please select a material.");
return;
}
try {
const data = { oldStock, newStock, changeDate, reason };
await createMaterialMutation(selectedMaterialId, data);
setSuccessMessage("Material mutation created successfully!");
setOldStock("");
setNewStock("");
setChangeDate("");
setReason("");
setSelectedMaterialId("");
// Refresh material mutations list after creation
const updatedMutations = await getMaterialMutations(cafeId);
setMaterialMutations(updatedMutations);
} catch (err) {
setError(err.message);
}
};
return (
<div>
<h1>Material Mutations</h1>
<h2>Create Material Mutation</h2>
<form onSubmit={handleSubmit}>
<div>
<label>
Select Material:
<select
value={selectedMaterialId}
onChange={(e) => setSelectedMaterialId(e.target.value)}
required
>
<option value="">Select a material</option>
{materials.map((material) => (
<option key={material.materialId} value={material.materialId}>
{material.name}
</option>
))}
</select>
</label>
</div>
<div>
<label>
Old Stock:
<input
type="number"
value={oldStock}
onChange={(e) => setOldStock(e.target.value)}
required
/>
</label>
</div>
<div>
<label>
New Stock:
<input
type="number"
value={newStock}
onChange={(e) => setNewStock(e.target.value)}
required
/>
</label>
</div>
<div>
<label>
Change Date:
<input
type="datetime-local"
value={changeDate}
onChange={(e) => setChangeDate(e.target.value)}
required
/>
</label>
</div>
<div>
<label>
Reason:
<textarea
value={reason}
onChange={(e) => setReason(e.target.value)}
/>
</label>
</div>
<button type="submit">Create Mutation</button>
</form>
{successMessage && <p>{successMessage}</p>}
{error && <p>Error: {error}</p>}
<h2>Existing Material Mutations</h2>
{materialMutations.length > 0 ? (
<ul>
{materialMutations.map((mutation) => (
<li key={mutation.mutationId}>
<p>
<strong>Material ID:</strong> {mutation.materialId}
</p>
<p>
<strong>Old Stock:</strong> {mutation.oldStock}
</p>
<p>
<strong>New Stock:</strong> {mutation.newStock}
</p>
<p>
<strong>Change Date:</strong>{" "}
{new Date(mutation.changeDate).toLocaleString()}
</p>
<p>
<strong>Reason:</strong> {mutation.reason}
</p>
</li>
))}
</ul>
) : (
<p>No material mutations found.</p>
)}
</div>
);
};
export default MaterialMutationPage;

View File

@@ -0,0 +1,25 @@
import React from "react";
import { ColorRing } from "react-loader-spinner";
import styles from "./Transactions.module.css";
export default function Transaction_pending() {
const containerStyle = {
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100%", // This makes the container stretch to the bottom of the viewport
backgroundColor: "#000", // Optional: Set a background color if you want to see the color ring clearly
};
return (
<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" />
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,25 @@
import React from "react";
import { ColorRing } from "react-loader-spinner";
import styles from "./Transactions.module.css";
export default function Transaction_pending() {
const containerStyle = {
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100%", // This makes the container stretch to the bottom of the viewport
backgroundColor: "#000", // Optional: Set a background color if you want to see the color ring clearly
};
return (
<div className={styles.Transactions}>
<div className={containerStyle}>
<div style={{ marginTop: "30px", textAlign: "center" }}>
<h2>transaction success</h2>
<img src="https://ibb.co.com/X7CD2f6" alt="Success" />
</div>
</div>
</div>
);
}

View File

@@ -2,12 +2,21 @@ import React, { useEffect, useState } from "react";
import styles from "./Transactions.module.css"; import styles from "./Transactions.module.css";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { ColorRing } from "react-loader-spinner"; import { ColorRing } from "react-loader-spinner";
import { getTransactions } from "../helpers/transactionHelpers"; import {
getTransactions,
confirmTransaction,
} from "../helpers/transactionHelpers";
import { getTables } from "../helpers/tableHelper";
import TableCanvas from "../components/TableCanvas";
export default function Transactions({ propsShopId, sendParam, deviceType }) { export default function Transactions({ propsShopId, sendParam, deviceType }) {
const { shopId, tableId } = useParams(); const { shopId, tableId } = useParams();
if (sendParam) sendParam({ shopId, tableId }); if (sendParam) sendParam({ shopId, tableId });
const [confirmed, setConfirmed] = useState(false);
const [message, setMessage] = useState("");
const [tables, setTables] = useState([]);
const [selectedTable, setSelectedTable] = useState(null);
const [transactions, setTransactions] = useState([]); const [transactions, setTransactions] = useState([]);
const [isPaymentLoading, setIsPaymentLoading] = useState(false); const [isPaymentLoading, setIsPaymentLoading] = useState(false);
@@ -15,8 +24,6 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
const fetchTransactions = async () => { const fetchTransactions = async () => {
try { try {
const response = await getTransactions(shopId || propsShopId, 5); const response = await getTransactions(shopId || propsShopId, 5);
console.log("modallll");
console.log(response);
setTransactions(response); setTransactions(response);
} catch (error) { } catch (error) {
console.error("Error fetching transactions:", error); console.error("Error fetching transactions:", error);
@@ -24,7 +31,20 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
}; };
fetchTransactions(); fetchTransactions();
}, [shopId]); }, [shopId || propsShopId]);
useEffect(() => {
const fetchData = async () => {
try {
const fetchedTables = await getTables(shopId || propsShopId);
setTables(fetchedTables);
} catch (error) {
console.error("Error fetching tables:", error);
}
};
fetchData();
}, [shopId || propsShopId]);
const calculateTotalPrice = (detailedTransactions) => { const calculateTotalPrice = (detailedTransactions) => {
return detailedTransactions.reduce((total, dt) => { return detailedTransactions.reduce((total, dt) => {
@@ -32,13 +52,13 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
}, 0); }, 0);
}; };
const handlePayment = async (isCash) => { const handleConfirm = async (transactionId) => {
setIsPaymentLoading(true); setIsPaymentLoading(true);
try { try {
// Implement payment logic here const c = await confirmTransaction(transactionId);
console.log(`Processing ${isCash ? "cash" : "cashless"} payment`); if (c) setMessage("success");
// Simulate payment process else setMessage("not confirmed");
await new Promise((resolve) => setTimeout(resolve, 2000)); setConfirmed(true);
} catch (error) { } catch (error) {
console.error("Error processing payment:", error); console.error("Error processing payment:", error);
} finally { } finally {
@@ -51,11 +71,15 @@ 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>
<div> <TableCanvas tables={tables} selectedTable={selectedTable} />
<div className={styles.TransactionListContainer}>
{transactions.map((transaction) => ( {transactions.map((transaction) => (
<div <div
key={transaction.transactionId} key={transaction.transactionId}
className={styles.RoundedRectangle} className={styles.RoundedRectangle}
onClick={() =>
setSelectedTable(transaction.Table || { tableId: 0 })
}
> >
<h2 className={styles["Transactions-detail"]}> <h2 className={styles["Transactions-detail"]}>
Transaction ID: {transaction.transactionId} Transaction ID: {transaction.transactionId}
@@ -87,12 +111,15 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
<div className={styles.TotalContainer}> <div className={styles.TotalContainer}>
<button <button
className={styles.PayButton} className={styles.PayButton}
onClick={() => handlePayment(false)} onClick={() => handleConfirm(transaction.transactionId)}
disabled={transaction.confirmed || isPaymentLoading} // Disable button if confirmed or loading
> >
{isPaymentLoading ? ( {isPaymentLoading ? (
<ColorRing height="50" width="50" color="white" /> <ColorRing height="50" width="50" color="white" />
) : transaction.confirmed ? (
"Confirmed" // Display "Confirmed" if the transaction is confirmed
) : ( ) : (
"Confirm" "Confirm" // Display "Confirm" otherwise
)} )}
</button> </button>
</div> </div>

View File

@@ -31,46 +31,22 @@
margin-top: 17px; margin-top: 17px;
} }
.PaymentOption { .TransactionListContainer {
overflow-x: hidden; overflow-y: auto; /* Enables vertical scrolling */
background-color: white; max-height: 600px; /* Adjust based on your design */
display: flex;
flex-direction: column;
justify-content: center;
font-size: calc(10px + 2vmin);
color: rgba(88, 55, 50, 1);
border-radius: 15px 15px 0 0;
position: fixed;
bottom: 75px;
right: 0;
left: 0;
}
.PaymentOptionMargin {
z-index: -1;
overflow-x: hidden;
background-color: white;
display: flex;
flex-direction: column;
justify-content: center;
font-size: calc(10px + 2vmin);
color: rgba(88, 55, 50, 1);
position: relative;
height: 229.39px;
} }
.TotalContainer { .TotalContainer {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
width: 80vw; width: 100%; /* Ensures it takes up full width */
margin: 0 auto; margin: 0 auto;
font-family: "Poppins", sans-serif; font-family: "Poppins", sans-serif;
font-weight: 600; font-weight: 600;
font-style: normal; font-style: normal;
font-size: 1.5em; font-size: 1.5em;
padding: 10px 0; padding: 10px;
box-sizing: border-box; /* Includes padding in width */
margin-bottom: 17px; margin-bottom: 17px;
} }
@@ -78,43 +54,22 @@
font-family: "Poppins", sans-serif; font-family: "Poppins", sans-serif;
font-weight: 500; font-weight: 500;
font-style: normal; font-style: normal;
font-size: 32px; font-size: 24px; /* Adjusted for better readability */
padding: 12px 24px; /* Added padding for a better look */
width: 80vw;
height: 18vw;
border-radius: 50px; border-radius: 50px;
background-color: rgba(88, 55, 50, 1); background-color: rgba(88, 55, 50, 1);
color: white; color: white;
border: none; border: none;
margin: 0px auto;
cursor: pointer;
margin-bottom: 23px;
}
.Pay2Button {
text-align: center;
color: rgba(88, 55, 50, 1);
font-size: 1em;
margin-bottom: 25px;
cursor: pointer;
}
.Confirm {
display: flex;
justify-content: space-between;
width: 80vw;
margin: 0 auto; margin: 0 auto;
font-family: "Poppins", sans-serif; cursor: pointer;
font-weight: 600; display: block; /* Centering the button */
font-style: normal; margin-bottom: 23px;
font-size: 1.5em; text-align: center;
padding: 10px 0;
margin-bottom: 17px;
} }
.RoundedRectangle { .RoundedRectangle {
border-radius: 20px; border-radius: 20px;
padding-top: 5px; padding: 15px; /* Adjusted for better spacing */
margin: 26px; margin: 26px;
background-color: #f9f9f9; background-color: #f9f9f9;
} }