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 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}
|
||||||
|
|||||||
@@ -213,6 +213,7 @@ const Header = ({
|
|||||||
HeaderText,
|
HeaderText,
|
||||||
shopId,
|
shopId,
|
||||||
shopName,
|
shopName,
|
||||||
|
shopOwnerId,
|
||||||
shopClerks,
|
shopClerks,
|
||||||
tableId,
|
tableId,
|
||||||
showProfile,
|
showProfile,
|
||||||
@@ -305,21 +306,56 @@ 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 &&
|
||||||
<Child onClick={goToAdminCafes}>see your other cafes</Child>
|
user.username !== undefined &&
|
||||||
<Child onClick={() => setModal("edit_tables")}>
|
user.roleId === 1 && (
|
||||||
{shopName} table maps
|
<>
|
||||||
</Child>
|
<Child onClick={goToAdminCafes}>see your other cafes</Child>
|
||||||
<Child hasChildren>
|
<Child onClick={() => setModal("edit_tables")}>
|
||||||
{shopName} clerks
|
{shopName} table maps
|
||||||
<Child onClick={() => setModal("craete_account_clerk")}>
|
|
||||||
+ Add clerk
|
|
||||||
</Child>
|
</Child>
|
||||||
{shopClerks &&
|
<Child hasChildren>
|
||||||
shopClerks.map((key, index) => (
|
{shopName} clerks
|
||||||
|
<Child onClick={() => setModal("craete_account_clerk")}>
|
||||||
|
+ Add clerk
|
||||||
|
</Child>
|
||||||
|
{shopClerks &&
|
||||||
|
shopClerks.map((key, index) => (
|
||||||
|
<Child key={index}>
|
||||||
|
{shopClerks[index].username}
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
removeConnectedGuestSides(guestSides[index][3])
|
||||||
|
}
|
||||||
|
>
|
||||||
|
remove
|
||||||
|
</button>
|
||||||
|
</Child>
|
||||||
|
))}
|
||||||
|
</Child>
|
||||||
|
<Child onClick={() => setModal("add_material")}>
|
||||||
|
add material
|
||||||
|
</Child>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{user.username !== undefined &&
|
||||||
|
((user.userId == shopOwnerId && user.roleId === 1) ||
|
||||||
|
(user.cafeId == shopId && user.roleId === 2)) && (
|
||||||
|
<Child onClick={() => setModal("update_stock")}>
|
||||||
|
update stock
|
||||||
|
</Child>
|
||||||
|
)}
|
||||||
|
{user.username !== undefined &&
|
||||||
|
user.roleId == 2 &&
|
||||||
|
user.cafeId == shopId && (
|
||||||
|
<Child hasChildren>
|
||||||
|
connected guest sides
|
||||||
|
<Child onClick={goToGuestSideLogin}>+ Add guest side</Child>
|
||||||
|
{guestSides &&
|
||||||
|
guestSides.map((key, index) => (
|
||||||
<Child key={index}>
|
<Child key={index}>
|
||||||
{shopClerks[index].username}
|
guest side {index + 1}
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
removeConnectedGuestSides(guestSides[index][3])
|
removeConnectedGuestSides(guestSides[index][3])
|
||||||
@@ -330,33 +366,7 @@ const Header = ({
|
|||||||
</Child>
|
</Child>
|
||||||
))}
|
))}
|
||||||
</Child>
|
</Child>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{user.username !== undefined &&
|
|
||||||
(user.roleId === 1 || user.roleId === 2) && (
|
|
||||||
<Child onClick={() => setModal("update_stock")}>
|
|
||||||
update stock
|
|
||||||
</Child>
|
|
||||||
)}
|
)}
|
||||||
{user.username !== undefined && user.roleId === 2 && (
|
|
||||||
<Child hasChildren>
|
|
||||||
connected guest sides
|
|
||||||
<Child onClick={goToGuestSideLogin}>+ Add guest side</Child>
|
|
||||||
{guestSides &&
|
|
||||||
guestSides.map((key, index) => (
|
|
||||||
<Child key={index}>
|
|
||||||
guest side {index + 1}
|
|
||||||
<button
|
|
||||||
onClick={() =>
|
|
||||||
removeConnectedGuestSides(guestSides[index][3])
|
|
||||||
}
|
|
||||||
>
|
|
||||||
remove
|
|
||||||
</button>
|
|
||||||
</Child>
|
|
||||||
))}
|
|
||||||
</Child>
|
|
||||||
)}
|
|
||||||
{user.username !== undefined && (
|
{user.username !== undefined && (
|
||||||
<Child onClick={isLogout}>Logout</Child>
|
<Child onClick={isLogout}>Logout</Child>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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,31 +147,34 @@ const ItemLister = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles["item-list"]}>
|
<div className={styles["item-list"]}>
|
||||||
{user && user.roleId == 1 && isEdit && (
|
{user &&
|
||||||
<>
|
user.roleId == 1 &&
|
||||||
<button
|
user.userId == shopOwnerId &&
|
||||||
className={styles["add-item-button"]}
|
isEdit && (
|
||||||
onClick={toggleAddNewItem}
|
<>
|
||||||
>
|
<button
|
||||||
{isAddingNewItem ? "Cancel" : "Add new Item"}
|
className={styles["add-item-button"]}
|
||||||
</button>
|
onClick={toggleAddNewItem}
|
||||||
{isAddingNewItem && (
|
>
|
||||||
<Item
|
{isAddingNewItem ? "Cancel" : "Add new Item"}
|
||||||
blank={true}
|
</button>
|
||||||
handleCreateItem={(name, price, qty, selectedImage) =>
|
{isAddingNewItem && (
|
||||||
createItem(
|
<Item
|
||||||
shopId,
|
blank={true}
|
||||||
name,
|
handleCreateItem={(name, price, qty, selectedImage) =>
|
||||||
price,
|
createItem(
|
||||||
qty,
|
shopId,
|
||||||
selectedImage,
|
name,
|
||||||
itemTypeId,
|
price,
|
||||||
)
|
qty,
|
||||||
}
|
selectedImage,
|
||||||
/>
|
itemTypeId
|
||||||
)}
|
)
|
||||||
</>
|
}
|
||||||
)}
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{items.map((item) => {
|
{items.map((item) => {
|
||||||
return !forCart || (forCart && item.qty > 0) ? (
|
return !forCart || (forCart && item.qty > 0) ? (
|
||||||
<Item
|
<Item
|
||||||
@@ -183,16 +192,19 @@ const ItemLister = ({
|
|||||||
) : null;
|
) : null;
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{user && user.roleId == 1 && isEdit && (
|
{user &&
|
||||||
<>
|
user.roleId == 1 &&
|
||||||
<button
|
user.userId == shopOwnerId &&
|
||||||
className={styles["add-item-button"]}
|
isEdit && (
|
||||||
onClick={handleRemoveType}
|
<>
|
||||||
>
|
<button
|
||||||
Remove
|
className={styles["add-item-button"]}
|
||||||
</button>
|
onClick={handleRemoveType}
|
||||||
</>
|
>
|
||||||
)}
|
Remove
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -1,72 +1,67 @@
|
|||||||
.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);
|
margin: 1px 10px -5px;
|
||||||
/* Adjust size as needed */
|
overflow: hidden;
|
||||||
margin: 1px 10px -5px;
|
text-align: center;
|
||||||
/* Left and right margin */
|
align-items: center;
|
||||||
overflow: hidden;
|
display: flex;
|
||||||
text-align: center;
|
flex-direction: column;
|
||||||
align-items: center;
|
justify-content: center;
|
||||||
display: flex;
|
position: relative; /* Ensure absolute positioning inside works */
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-type-rect {
|
.item-type-rect {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 20vw;
|
height: 20vw;
|
||||||
width: 20vw;
|
width: 20vw;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
/* Rounded corners */
|
background-color: #fff;
|
||||||
background-color: #fff;
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
/* Background color of the item */
|
|
||||||
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;
|
margin-bottom: 10px; /* Adjust margin for spacing */
|
||||||
top: 13px;
|
font-size: 14px;
|
||||||
margin-bottom: 14px;
|
color: #333;
|
||||||
font-size: 14px;
|
width: calc(25vw - 30px);
|
||||||
/* Adjust font size as needed */
|
text-align: center;
|
||||||
color: #333;
|
background-color: transparent;
|
||||||
width: calc(25vw - 30px);
|
position: relative; /* Needed for positioning the button */
|
||||||
/* Adjust size as needed, subtracting margin */
|
|
||||||
text-align: center;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-type-image {
|
.item-type-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
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 {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-type-create {
|
.item-type-create {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin-top: 130px;
|
top: 80%; /* Position below the input */
|
||||||
width: 20vw;
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
margin-top: 10px; /* Space between input and button */
|
||||||
|
width: 20vw;
|
||||||
|
text-align: center; /* Center button text */
|
||||||
}
|
}
|
||||||
|
|
||||||
.border {
|
.border {
|
||||||
border: 1px solid #000000;
|
border: 1px solid #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.noborder {
|
.noborder {
|
||||||
border: 1px solid #ffffff00;
|
border: 1px solid #ffffff00;
|
||||||
}
|
}
|
||||||
@@ -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"}
|
||||||
{itemTypes && itemTypes.map(itemType => (
|
imageUrl={
|
||||||
(user && user.roleId == 1 || itemType.itemList.length > 0) && (
|
"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 && user.userId == shopOwnerId) ||
|
||||||
|
itemType.itemList.length > 0) && (
|
||||||
|
<ItemType
|
||||||
|
key={itemType.itemTypeId}
|
||||||
|
name={itemType.name}
|
||||||
|
imageUrl={itemType.image}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
{user &&
|
||||||
|
user.roleId == 1 &&
|
||||||
|
user.userId == shopOwnerId &&
|
||||||
|
isAddingNewItem && (
|
||||||
|
<ItemType blank={true} name={"blank"} onCreate={handleCreate} />
|
||||||
|
)}
|
||||||
|
{!isAddingNewItem &&
|
||||||
|
user &&
|
||||||
|
user.roleId == 1 &&
|
||||||
|
user.userId == shopOwnerId && (
|
||||||
<ItemType
|
<ItemType
|
||||||
key={itemType.itemTypeId}
|
onClick={toggleAddNewItem}
|
||||||
name={itemType.name}
|
name={"create"}
|
||||||
imageUrl={itemType.image}
|
imageUrl={
|
||||||
|
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQnd07OYAm1f7T6JzziFU7U8X1_IL3bADiVrg&usqp=CAU"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)}
|
||||||
))}
|
|
||||||
{user && user.roleId == 1 && isAddingNewItem &&
|
|
||||||
<ItemType blank={true} name={"blank"} onCreate = {handleCreate} />
|
|
||||||
}
|
|
||||||
{user && user.roleId == 1 &&
|
|
||||||
<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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
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 { 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,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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
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 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>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user