add stocking page
This commit is contained in:
29
src/App.js
29
src/App.js
@@ -23,6 +23,8 @@ import GuestSideLogin from "./pages/GuestSideLogin";
|
||||
import GuestSide from "./pages/GuestSide";
|
||||
import { getItemTypesWithItems } from "./helpers/itemHelper.js";
|
||||
|
||||
import MaterialList from "./pages/MaterialList";
|
||||
|
||||
import {
|
||||
getConnectedGuestSides,
|
||||
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) => {
|
||||
console.log("transaction notification");
|
||||
setModal("new_transaction");
|
||||
@@ -136,7 +149,7 @@ function App() {
|
||||
} else {
|
||||
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
|
||||
// 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);
|
||||
@@ -209,7 +222,9 @@ function App() {
|
||||
<Routes>
|
||||
<Route
|
||||
path="/"
|
||||
element={<Dashboard user={user} setModal={setModal} />}
|
||||
element={
|
||||
<Dashboard user={user} socket={socket} setModal={setModal} />
|
||||
}
|
||||
/>
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route
|
||||
@@ -219,6 +234,7 @@ function App() {
|
||||
<CafePage
|
||||
sendParam={handleSetParam}
|
||||
shopName={shop.name}
|
||||
shopOwnerId={shop.ownerId}
|
||||
shopItems={shopItems}
|
||||
shopClerks={shopClerks}
|
||||
socket={socket}
|
||||
@@ -241,7 +257,8 @@ function App() {
|
||||
path="/:shopId/:tableId?/search"
|
||||
element={
|
||||
<>
|
||||
<SearchResult
|
||||
<MaterialList
|
||||
cafeId={shopId}
|
||||
sendParam={handleSetParam}
|
||||
user={user}
|
||||
shopItems={shopItems}
|
||||
@@ -281,7 +298,11 @@ function App() {
|
||||
path="/:shopId/:tableId?/invoice"
|
||||
element={
|
||||
<>
|
||||
<Invoice sendParam={handleSetParam} deviceType={deviceType} />
|
||||
<Invoice
|
||||
sendParam={handleSetParam}
|
||||
socket={socket}
|
||||
deviceType={deviceType}
|
||||
/>
|
||||
<Footer
|
||||
shopId={shopId}
|
||||
tableId={tableId}
|
||||
|
||||
@@ -213,6 +213,7 @@ const Header = ({
|
||||
HeaderText,
|
||||
shopId,
|
||||
shopName,
|
||||
shopOwnerId,
|
||||
shopClerks,
|
||||
tableId,
|
||||
showProfile,
|
||||
@@ -305,7 +306,10 @@ const Header = ({
|
||||
{user.username !== undefined && (
|
||||
<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={() => setModal("edit_tables")}>
|
||||
@@ -330,15 +334,21 @@ const Header = ({
|
||||
</Child>
|
||||
))}
|
||||
</Child>
|
||||
<Child onClick={() => setModal("add_material")}>
|
||||
add material
|
||||
</Child>
|
||||
</>
|
||||
)}
|
||||
{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")}>
|
||||
update stock
|
||||
</Child>
|
||||
)}
|
||||
{user.username !== undefined && user.roleId === 2 && (
|
||||
{user.username !== undefined &&
|
||||
user.roleId == 2 &&
|
||||
user.cafeId == shopId && (
|
||||
<Child hasChildren>
|
||||
connected guest sides
|
||||
<Child onClick={goToGuestSideLogin}>+ Add guest side</Child>
|
||||
|
||||
@@ -17,6 +17,7 @@ const ItemLister = ({
|
||||
itemTypeId,
|
||||
refreshTotal,
|
||||
shopId,
|
||||
shopOwnerId,
|
||||
user,
|
||||
typeName,
|
||||
itemList,
|
||||
@@ -27,7 +28,7 @@ const ItemLister = ({
|
||||
itemList.map((item) => ({
|
||||
...item,
|
||||
qty: getItemQtyFromCart(shopId, item.itemId),
|
||||
})),
|
||||
}))
|
||||
);
|
||||
const [isEdit, setIsEditing] = useState(false);
|
||||
const [isAddingNewItem, setIsAddingNewItem] = useState(false);
|
||||
@@ -111,17 +112,22 @@ const ItemLister = ({
|
||||
|
||||
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["title-container"]}>
|
||||
<input
|
||||
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}
|
||||
onChange={(e) => setEditedTypeName(e.target.value)}
|
||||
disabled={!isEdit}
|
||||
/>
|
||||
{user && user.roleId == 1 && (
|
||||
{user && user.roleId == 1 && user.userId == shopOwnerId && (
|
||||
<>
|
||||
<button
|
||||
className={styles["edit-typeItem-button"]}
|
||||
@@ -141,7 +147,10 @@ const ItemLister = ({
|
||||
)}
|
||||
</div>
|
||||
<div className={styles["item-list"]}>
|
||||
{user && user.roleId == 1 && isEdit && (
|
||||
{user &&
|
||||
user.roleId == 1 &&
|
||||
user.userId == shopOwnerId &&
|
||||
isEdit && (
|
||||
<>
|
||||
<button
|
||||
className={styles["add-item-button"]}
|
||||
@@ -159,7 +168,7 @@ const ItemLister = ({
|
||||
price,
|
||||
qty,
|
||||
selectedImage,
|
||||
itemTypeId,
|
||||
itemTypeId
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -183,7 +192,10 @@ const ItemLister = ({
|
||||
) : null;
|
||||
})}
|
||||
|
||||
{user && user.roleId == 1 && isEdit && (
|
||||
{user &&
|
||||
user.roleId == 1 &&
|
||||
user.userId == shopOwnerId &&
|
||||
isEdit && (
|
||||
<>
|
||||
<button
|
||||
className={styles["add-item-button"]}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import styles from './ItemType.module.css';
|
||||
import { getImageUrl } from '../helpers/itemHelper';
|
||||
import React, { useRef, useEffect, useState } from "react";
|
||||
import styles from "./ItemType.module.css";
|
||||
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 [name, setName] = useState(initialName);
|
||||
const [selectedImage, setSelectedImage] = useState(null);
|
||||
@@ -36,7 +42,7 @@ export default function ItemType({ onClick, onCreate, blank, name: initialName =
|
||||
|
||||
const handleCreate = async () => {
|
||||
if (!selectedImage) {
|
||||
console.error('No image selected');
|
||||
console.error("No image selected");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,7 +52,11 @@ export default function ItemType({ onClick, onCreate, blank, name: initialName =
|
||||
return (
|
||||
<div className={styles["item-type"]}>
|
||||
<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 && (
|
||||
<input
|
||||
type="file"
|
||||
@@ -58,7 +68,9 @@ export default function ItemType({ onClick, onCreate, blank, name: initialName =
|
||||
</div>
|
||||
<input
|
||||
ref={inputRef}
|
||||
className={`${styles["item-type-name"]} ${blank ? styles.border : styles.noborder}`}
|
||||
className={`${styles["item-type-name"]} ${
|
||||
blank ? styles.border : styles.noborder
|
||||
}`}
|
||||
value={name}
|
||||
onChange={handleNameChange}
|
||||
disabled={!blank}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
.item-type {
|
||||
width: calc(25vw - 20px);
|
||||
/* Adjust size as needed, subtracting margin */
|
||||
height: calc(39vw - 20px);
|
||||
/* Adjust size as needed */
|
||||
margin: 1px 10px -5px;
|
||||
/* Left and right margin */
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: relative; /* Ensure absolute positioning inside works */
|
||||
}
|
||||
|
||||
.item-type-rect {
|
||||
@@ -19,27 +17,21 @@
|
||||
width: 20vw;
|
||||
object-fit: cover;
|
||||
border-radius: 15px;
|
||||
/* Rounded corners */
|
||||
background-color: #fff;
|
||||
/* Background color of the item */
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
/* Optional: Shadow for better visual */
|
||||
}
|
||||
|
||||
.item-type-name {
|
||||
position: relative;
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
top: 13px;
|
||||
margin-bottom: 14px;
|
||||
margin-bottom: 10px; /* Adjust margin for spacing */
|
||||
font-size: 14px;
|
||||
/* Adjust font size as needed */
|
||||
color: #333;
|
||||
width: calc(25vw - 30px);
|
||||
/* Adjust size as needed, subtracting margin */
|
||||
text-align: center;
|
||||
background-color: transparent;
|
||||
position: relative; /* Needed for positioning the button */
|
||||
}
|
||||
|
||||
.item-type-image {
|
||||
@@ -47,7 +39,6 @@
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
border-radius: 15px;
|
||||
/* Rounded corners */
|
||||
}
|
||||
|
||||
.item-type-image-input {
|
||||
@@ -59,8 +50,12 @@
|
||||
|
||||
.item-type-create {
|
||||
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;
|
||||
text-align: center; /* Center button text */
|
||||
}
|
||||
|
||||
.border {
|
||||
|
||||
@@ -1,44 +1,64 @@
|
||||
import React, { useState } from 'react';
|
||||
import './ItemTypeLister.css';
|
||||
import ItemType from './ItemType';
|
||||
import { createItemType } from '../helpers/itemHelper.js';
|
||||
import React, { useState } from "react";
|
||||
import "./ItemTypeLister.css";
|
||||
import ItemType from "./ItemType";
|
||||
import { createItemType } from "../helpers/itemHelper.js";
|
||||
|
||||
const ItemTypeLister = ({ shopId, user, itemTypes }) => {
|
||||
const ItemTypeLister = ({ shopId, shopOwnerId, user, itemTypes }) => {
|
||||
const [isAddingNewItem, setIsAddingNewItem] = useState(false);
|
||||
|
||||
const toggleAddNewItem = () => {
|
||||
console.log("aaa")
|
||||
setIsAddingNewItem(prev => !prev);
|
||||
console.log("aaa");
|
||||
setIsAddingNewItem((prev) => !prev);
|
||||
};
|
||||
|
||||
async function handleCreate(name, selectedImage) {
|
||||
createItemType(shopId, name, selectedImage);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="item-type-lister">
|
||||
<div className="item-type-list">
|
||||
{itemTypes && itemTypes.length > 1 &&
|
||||
<ItemType name={"All"} imageUrl={"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAY1BMVEX///8BAQEEBAT09PT8/PzFxcWGhobu7u7R0dHLy8uoqKhTU1N7e3tXV1fIyMguLi7d3d08PDyenp41NTWBgYF1dXVHR0dMTEwTExPj4+O6urrp6emWlpasrKy2trZeXl4aGhrSJRZxAAADF0lEQVR4nO3Zi3aiMBCAYQIR71VbtXft+z/lJoFigAkNbYHdPf/X3VPqGeMkwdxIEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0dGBVaSOe5vuV/4giqR1mXOPXL6M1X4r9Cn5l+kqCx3R2Nq7igjWPYoeWjbPkpg09LdiJ/dyPK3P22O+iIid5ZvtebvJZxGxi/y4Pa9Px5efJvh99i56fVM3q+4RJFv5seHeseVqP/btdYDsY5hE7nZVGqn5/7jvqOHiUASVDsE+NyXsH5UfvLub4qtoP/Hd5lEkYn+Zf+Fb9d7lXAa73/fB2KUryytZvQ9Qgy9pm3TqNbW7WAai72udojqruKwq5sdOMJ4ub51SXNnrp2cxdqH8YHcd7PHnp7J+foebphu9inMl+pAyyQ5i7KE13Nj3ftQ6sDIfvkoNef22U59fmoswKqzaCTurRqz942ILaZZs/j6OVrPS805o6dS8dG3Uz65fAhVUqrG6MROFvkpdaF7ZjT1nvAj5Fm2/b1VxpsSszc+s2d96r+rf2Fv02FP/SeoSl9ildZfmUtLulbxV7kW6+Z3TOBWrVBVqWbdiN3LKxqYVuxbj3CeNUS2PON45D80+zLbBGm6z5lDzEIg0Hzdm9ZIkPHioXTN0/hiMPbfmgF0wlj78be5TxVz+l+/hSZ7vVXMstXuh7rFU14IvwaYbeyztmg/rd6mdD8UZLm3Nhzo8H6rR50N5TWN+rllz2dZjTZPYNU0qr2nkNf2AOtalPleBjnVpy0Uaw8TFwdCkvUVq9xaC6L2F9SG3xuh7C11uVG91c20fuT9UX+wPG7vlNLy1HtDnHr/KojORnnv81LtVXbmT7PHdOU119FL8Crd0/3Mav+RJzmnks7bwAbx/1pb+C2dthZ+cl4aO9l0prfPSCQ/2izPvzQBn3pupz7wL3vOFqEczsc8tkuSveW5RinqcJF/Lwf7TxsmfPcWmoIWrUGjt+eF3Uvolfdq3Pg71ehsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACASfwBIesXINu4PFgAAAAASUVORK5CYII="} />
|
||||
{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="
|
||||
}
|
||||
{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
|
||||
key={itemType.itemTypeId}
|
||||
name={itemType.name}
|
||||
imageUrl={itemType.image}
|
||||
/>
|
||||
)
|
||||
))}
|
||||
{user && user.roleId == 1 && isAddingNewItem &&
|
||||
)}
|
||||
{user &&
|
||||
user.roleId == 1 &&
|
||||
user.userId == shopOwnerId &&
|
||||
isAddingNewItem && (
|
||||
<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"
|
||||
}
|
||||
{user && user.roleId == 1 &&
|
||||
<ItemType onClick = {toggleAddNewItem} name={"create"} imageUrl={"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQnd07OYAm1f7T6JzziFU7U8X1_IL3bADiVrg&usqp=CAU"} />
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default ItemTypeLister;
|
||||
|
||||
@@ -3,6 +3,10 @@ import styles from "./Modal.module.css";
|
||||
import TablesPage from "./TablesPage.js";
|
||||
import TableMaps from "../components/TableMaps";
|
||||
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 }) => {
|
||||
if (!isOpen) return null;
|
||||
@@ -28,6 +32,12 @@ const Modal = ({ shopId, isOpen, onClose, modalContent }) => {
|
||||
{modalContent === "edit_tables" && <TablesPage shopId={shopId} />}
|
||||
{modalContent === "new_transaction" && (
|
||||
<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>
|
||||
|
||||
@@ -13,13 +13,12 @@
|
||||
|
||||
.modalContent {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
width: 80%;
|
||||
width: 90%;
|
||||
height: 80%;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
overflow: auto; /* Add this line to enable scrolling */
|
||||
overflow: hidden; /* Add this line to enable scrolling */
|
||||
}
|
||||
|
||||
.closeButton {
|
||||
|
||||
@@ -4,7 +4,7 @@ const TableCanvas = ({
|
||||
tables,
|
||||
selectedTable,
|
||||
newTable,
|
||||
setTableNo,
|
||||
isAdmin,
|
||||
handleSelectTable,
|
||||
handleAddTable,
|
||||
moveTable,
|
||||
@@ -91,11 +91,12 @@ const TableCanvas = ({
|
||||
context.fillStyle = "white";
|
||||
context.textAlign = "center";
|
||||
context.textBaseline = "middle";
|
||||
console.log(selectedTable);
|
||||
context.fillText(
|
||||
table.tableId === (selectedTable?.tableId || newTable?.tableId)
|
||||
? tableNo === 0
|
||||
? "clerk"
|
||||
: tableNo
|
||||
: tableNo || (isAdmin ? "" : table.tableNo)
|
||||
: table.tableNo === 0
|
||||
? "clerk"
|
||||
: table.tableNo,
|
||||
@@ -106,7 +107,7 @@ const TableCanvas = ({
|
||||
}, [tables, canvasSize, newTable, selectedTable, tableNo]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} style={{ width: "100%", height: "100%" }}>
|
||||
<div ref={containerRef} style={{ height: "20%" }}>
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
style={{
|
||||
@@ -116,7 +117,7 @@ const TableCanvas = ({
|
||||
}}
|
||||
onClick={handleSelectTable}
|
||||
/>
|
||||
<div>
|
||||
<div style={{ visibility: isAdmin ? "" : "hidden" }}>
|
||||
{!selectedTable && !newTable && (
|
||||
<button
|
||||
onClick={handleAddTable}
|
||||
@@ -127,11 +128,11 @@ const TableCanvas = ({
|
||||
backgroundColor: "#007bff",
|
||||
color: "#fff",
|
||||
border: "none",
|
||||
borderRadius: "4px",
|
||||
fontSize: "16px",
|
||||
cursor: "pointer",
|
||||
marginBottom: "10px",
|
||||
transition: "background-color 0.3s ease",
|
||||
visibility: isAdmin ? "" : "hidden",
|
||||
}}
|
||||
>
|
||||
Add Table
|
||||
@@ -143,6 +144,7 @@ const TableCanvas = ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
display: isAdmin ? "visible" : "hidden",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -175,9 +175,18 @@ const TablesPage = ({ shopId }) => {
|
||||
|
||||
return (
|
||||
<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
|
||||
isAdmin={true}
|
||||
tables={tables}
|
||||
selectedTable={selectedTable}
|
||||
newTable={newTable}
|
||||
|
||||
72
src/helpers/materialHelpers.js
Normal file
72
src/helpers/materialHelpers.js
Normal 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();
|
||||
};
|
||||
65
src/helpers/materialMutationHelpers.js
Normal file
65
src/helpers/materialMutationHelpers.js
Normal 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;
|
||||
}
|
||||
};
|
||||
@@ -2,6 +2,29 @@ import API_BASE_URL from "../config.js";
|
||||
import { getLocalStorage, updateLocalStorage } from "./localStorageHelpers";
|
||||
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) {
|
||||
try {
|
||||
const token = getLocalStorage("auth");
|
||||
@@ -140,7 +163,8 @@ export const handlePaymentFromGuestDevice = async (
|
||||
shopId,
|
||||
payment_type,
|
||||
serving_type,
|
||||
tableNo
|
||||
tableNo,
|
||||
socketId
|
||||
) => {
|
||||
try {
|
||||
const token = getLocalStorage("auth");
|
||||
@@ -167,6 +191,7 @@ export const handlePaymentFromGuestDevice = async (
|
||||
serving_type,
|
||||
tableNo,
|
||||
transactions: structuredItems,
|
||||
socketId,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
function CafePage({
|
||||
sendParam,
|
||||
shopName,
|
||||
shopOwnerId,
|
||||
shopItems,
|
||||
shopClerks,
|
||||
socket,
|
||||
@@ -100,6 +101,7 @@ function CafePage({
|
||||
isLogout={handleLogout}
|
||||
shopId={shopId}
|
||||
shopName={shopName}
|
||||
shopOwnerId={shopOwnerId}
|
||||
shopClerks={shopClerks}
|
||||
tableId={tableId}
|
||||
user={user}
|
||||
@@ -110,7 +112,12 @@ function CafePage({
|
||||
<div style={{ marginTop: "5px" }}></div>
|
||||
<SearchInput shopId={shopId} />
|
||||
<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>
|
||||
<h2 className="title">Music Req.</h2>
|
||||
<MusicPlayer
|
||||
@@ -123,6 +130,7 @@ function CafePage({
|
||||
{shopItems.map((itemType) => (
|
||||
<ItemLister
|
||||
shopId={shopId}
|
||||
shopOwnerId={shopOwnerId}
|
||||
user={user}
|
||||
key={itemType.itemTypeId}
|
||||
itemTypeId={itemType.itemTypeId}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
handlePaymentFromGuestDevice,
|
||||
} from "../helpers/transactionHelpers";
|
||||
|
||||
export default function Invoice({ sendParam, deviceType }) {
|
||||
export default function Invoice({ sendParam, deviceType, socket }) {
|
||||
const { shopId, tableId } = useParams();
|
||||
sendParam({ shopId, tableId });
|
||||
|
||||
@@ -71,11 +71,13 @@ export default function Invoice({ sendParam, deviceType }) {
|
||||
tableNumber
|
||||
);
|
||||
} else if (deviceType == "guestDevice") {
|
||||
const socketId = socket.id;
|
||||
const pay = await handlePaymentFromGuestDevice(
|
||||
shopId,
|
||||
isCash ? "cash" : "cashless",
|
||||
orderType,
|
||||
tableNumber
|
||||
tableNumber,
|
||||
socketId
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
267
src/pages/MaterialList.js
Normal file
267
src/pages/MaterialList.js
Normal 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;
|
||||
181
src/pages/MaterialMutationsPage.js
Normal file
181
src/pages/MaterialMutationsPage.js
Normal 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;
|
||||
25
src/pages/Transaction_pending.js
Normal file
25
src/pages/Transaction_pending.js
Normal 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>
|
||||
);
|
||||
}
|
||||
25
src/pages/Transaction_success.js
Normal file
25
src/pages/Transaction_success.js
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -2,12 +2,21 @@ import React, { useEffect, useState } from "react";
|
||||
import styles from "./Transactions.module.css";
|
||||
import { useParams } from "react-router-dom";
|
||||
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 }) {
|
||||
const { shopId, tableId } = useParams();
|
||||
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 [isPaymentLoading, setIsPaymentLoading] = useState(false);
|
||||
|
||||
@@ -15,8 +24,6 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
|
||||
const fetchTransactions = async () => {
|
||||
try {
|
||||
const response = await getTransactions(shopId || propsShopId, 5);
|
||||
console.log("modallll");
|
||||
console.log(response);
|
||||
setTransactions(response);
|
||||
} catch (error) {
|
||||
console.error("Error fetching transactions:", error);
|
||||
@@ -24,7 +31,20 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
|
||||
};
|
||||
|
||||
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) => {
|
||||
return detailedTransactions.reduce((total, dt) => {
|
||||
@@ -32,13 +52,13 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const handlePayment = async (isCash) => {
|
||||
const handleConfirm = async (transactionId) => {
|
||||
setIsPaymentLoading(true);
|
||||
try {
|
||||
// Implement payment logic here
|
||||
console.log(`Processing ${isCash ? "cash" : "cashless"} payment`);
|
||||
// Simulate payment process
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
const c = await confirmTransaction(transactionId);
|
||||
if (c) setMessage("success");
|
||||
else setMessage("not confirmed");
|
||||
setConfirmed(true);
|
||||
} catch (error) {
|
||||
console.error("Error processing payment:", error);
|
||||
} finally {
|
||||
@@ -51,11 +71,15 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
|
||||
<div style={{ marginTop: "30px" }}></div>
|
||||
<h2 className={styles["Transactions-title"]}>Transactions</h2>
|
||||
<div style={{ marginTop: "30px" }}></div>
|
||||
<div>
|
||||
<TableCanvas tables={tables} selectedTable={selectedTable} />
|
||||
<div className={styles.TransactionListContainer}>
|
||||
{transactions.map((transaction) => (
|
||||
<div
|
||||
key={transaction.transactionId}
|
||||
className={styles.RoundedRectangle}
|
||||
onClick={() =>
|
||||
setSelectedTable(transaction.Table || { tableId: 0 })
|
||||
}
|
||||
>
|
||||
<h2 className={styles["Transactions-detail"]}>
|
||||
Transaction ID: {transaction.transactionId}
|
||||
@@ -87,12 +111,15 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
|
||||
<div className={styles.TotalContainer}>
|
||||
<button
|
||||
className={styles.PayButton}
|
||||
onClick={() => handlePayment(false)}
|
||||
onClick={() => handleConfirm(transaction.transactionId)}
|
||||
disabled={transaction.confirmed || isPaymentLoading} // Disable button if confirmed or loading
|
||||
>
|
||||
{isPaymentLoading ? (
|
||||
<ColorRing height="50" width="50" color="white" />
|
||||
) : transaction.confirmed ? (
|
||||
"Confirmed" // Display "Confirmed" if the transaction is confirmed
|
||||
) : (
|
||||
"Confirm"
|
||||
"Confirm" // Display "Confirm" otherwise
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -31,46 +31,22 @@
|
||||
margin-top: 17px;
|
||||
}
|
||||
|
||||
.PaymentOption {
|
||||
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);
|
||||
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;
|
||||
.TransactionListContainer {
|
||||
overflow-y: auto; /* Enables vertical scrolling */
|
||||
max-height: 600px; /* Adjust based on your design */
|
||||
}
|
||||
|
||||
.TotalContainer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 80vw;
|
||||
width: 100%; /* Ensures it takes up full width */
|
||||
margin: 0 auto;
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-size: 1.5em;
|
||||
padding: 10px 0;
|
||||
padding: 10px;
|
||||
box-sizing: border-box; /* Includes padding in width */
|
||||
margin-bottom: 17px;
|
||||
}
|
||||
|
||||
@@ -78,43 +54,22 @@
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-size: 32px;
|
||||
|
||||
width: 80vw;
|
||||
height: 18vw;
|
||||
font-size: 24px; /* Adjusted for better readability */
|
||||
padding: 12px 24px; /* Added padding for a better look */
|
||||
border-radius: 50px;
|
||||
background-color: rgba(88, 55, 50, 1);
|
||||
color: white;
|
||||
border: none;
|
||||
margin: 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;
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-size: 1.5em;
|
||||
padding: 10px 0;
|
||||
margin-bottom: 17px;
|
||||
cursor: pointer;
|
||||
display: block; /* Centering the button */
|
||||
margin-bottom: 23px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.RoundedRectangle {
|
||||
border-radius: 20px;
|
||||
padding-top: 5px;
|
||||
padding: 15px; /* Adjusted for better spacing */
|
||||
margin: 26px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user