add stocking page

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

View File

@@ -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}

View File

@@ -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
View File

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

View File

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

View File

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

View File

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

View File

@@ -2,12 +2,21 @@ import React, { useEffect, useState } from "react";
import styles from "./Transactions.module.css";
import { 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>

View File

@@ -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;
}