- {/* Shop Icon */}
-
+
+
+
+ {/* Shop Icon */}
+
- {/* Search Icon */}
-
+ {/* Search Icon */}
+
- {/* Cart Icon */}
-
- {cartItemsLength != "0" && (
-
{cartItemsLength}
- )}
-
-
+ {/* Cart Icon */}
+
+ {cartItemsLength !== 0 && (
+
{cartItemsLength}
+ )}
+
+
- {/* Profile Icon */}
-
-
+ {/* Profile Icon */}
+
-
- {/* Add more SVG elements as needed */}
+
-
+
+ {/* Rounded Rectangle with "Scan Meja" and QR Icon */}
+ {shopId && (
+
+
+ {tableId ? `Diantar ke meja ${tableId}` : `Scan Meja\u00A0`}
+
+ {!tableId && (
+

+ )}
+ {tableId && isStretched && (
+
+ )}
+
+ )}
);
}
diff --git a/src/components/Footer.module.css b/src/components/Footer.module.css
index c155faa..ea62a43 100644
--- a/src/components/Footer.module.css
+++ b/src/components/Footer.module.css
@@ -45,10 +45,52 @@
/* Just making it pretty */
background: #38a9e4;
color: white;
- font-family:
- Helvetica,
- Arial Black,
- sans;
+ font-family: Helvetica, Arial Black, sans;
font-size: 20px;
text-align: center;
}
+.scanMeja {
+ position: fixed;
+ bottom: 90px;
+ left: 50%;
+ transform: translateX(-50%);
+ background-color: rgb(143, 135, 135);
+ color: #fff;
+ width: 147px;
+ height: 40px;
+ border-radius: 25px;
+ padding: 0px 10px;
+ font-size: 16px;
+ text-align: center;
+ font-weight: 600;
+ color: black;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: height 0.3s ease;
+}
+
+.scanMeja.stretched {
+ height: 70px;
+ flex-direction: column;
+ padding: 10px;
+}
+
+.qrIcon {
+ top: 2px;
+ position: relative;
+ width: 24px;
+ height: 24px;
+ background-color: rgb(143, 135, 135);
+}
+
+.hapusMejaBtn {
+ margin-top: 10px;
+ background-color: #d9534f;
+ color: white;
+ border: none;
+ border-radius: 5px;
+ padding: 8px 16px;
+ font-size: 14px;
+ cursor: pointer;
+}
diff --git a/src/helpers/materialMutationHelpers.js b/src/helpers/materialMutationHelpers.js
index 4b89565..58dde76 100644
--- a/src/helpers/materialMutationHelpers.js
+++ b/src/helpers/materialMutationHelpers.js
@@ -63,3 +63,19 @@ export const getMaterialMutationById = async (mutationId) => {
throw error;
}
};
+
+// Get all material mutations by materialId
+export const getMaterialMutationsByMaterialId = async (materialId) => {
+ try {
+ const response = await fetch(
+ `${API_BASE_URL}/mutation/get-material-mutations-by-material/${materialId}`,
+ getHeaders()
+ );
+ if (!response.ok)
+ throw new Error("Failed to retrieve material mutations by material ID.");
+ return await response.json();
+ } catch (error) {
+ console.error(error);
+ throw error;
+ }
+};
diff --git a/src/helpers/navigationHelpers.js b/src/helpers/navigationHelpers.js
index f7eb2dd..f154bb4 100644
--- a/src/helpers/navigationHelpers.js
+++ b/src/helpers/navigationHelpers.js
@@ -25,7 +25,21 @@ export const useNavigationHelpers = (shopId, tableId) => {
// Perform the navigation
navigate(url);
};
+ const goToScan = () => {
+ // Construct the base URL for the shop
+ let url = `/scan`;
+ // Perform the navigation
+ navigate(url);
+ };
+
+ const goToNonTable = () => {
+ // Construct the base URL for the shop
+ let url = `/${shopId}`;
+
+ // Perform the navigation
+ navigate(url);
+ };
const goToShop = () => {
// Construct the base URL for the shop
let url = `/${shopId}`;
@@ -103,5 +117,7 @@ export const useNavigationHelpers = (shopId, tableId) => {
goToTransactions,
goToGuestSideLogin,
goToAdminCafes,
+ goToScan,
+ goToNonTable,
};
};
diff --git a/src/pages/MaterialList.js b/src/pages/MaterialList.js
index 08f7442..47008de 100644
--- a/src/pages/MaterialList.js
+++ b/src/pages/MaterialList.js
@@ -4,7 +4,7 @@ import {
getMaterials,
createMaterial,
deleteMaterial,
-} from "../helpers/materialHelpers"; // Update import
+} from "../helpers/materialHelpers";
const MaterialList = ({ cafeId }) => {
const [materials, setMaterials] = useState([]);
@@ -14,7 +14,7 @@ const MaterialList = ({ cafeId }) => {
const [deleting, setDeleting] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
- const [showForm, setShowForm] = useState(false); // For form visibility
+ const [showForm, setShowForm] = useState(false);
useEffect(() => {
const fetchMaterials = async () => {
@@ -22,7 +22,7 @@ const MaterialList = ({ cafeId }) => {
try {
const data = await getMaterials(cafeId);
setMaterials(data);
- setError(null); // Clear any previous error
+ setError(null);
} catch (error) {
console.error("Error fetching materials:", error);
setError("Failed to fetch materials.");
@@ -41,6 +41,7 @@ const MaterialList = ({ cafeId }) => {
const formData = new FormData();
formData.append("name", newMaterialName);
formData.append("unit", newMaterialUnit);
+ console.log(newMaterialImage);
if (newMaterialImage) {
formData.append("image", newMaterialImage);
}
@@ -50,10 +51,10 @@ const MaterialList = ({ cafeId }) => {
setNewMaterialName("");
setNewMaterialUnit("kilogram");
setNewMaterialImage(null);
- setShowForm(false); // Hide the form after successful creation
+ setShowForm(false);
const data = await getMaterials(cafeId);
setMaterials(data);
- setError(null); // Clear any previous error
+ setError(null);
} catch (error) {
console.error("Error creating material:", error);
setError("Failed to create material.");
@@ -69,7 +70,7 @@ const MaterialList = ({ cafeId }) => {
setMaterials(
materials.filter((material) => material.materialId !== materialId)
);
- setError(null); // Clear any previous error
+ setError(null);
} catch (error) {
console.error("Error deleting material:", error);
setError("Failed to delete material.");
@@ -80,7 +81,7 @@ const MaterialList = ({ cafeId }) => {
return (
-
Materials List
+
Materials List
{/* Display error message if any */}
{error &&
{error}
}
@@ -126,9 +127,13 @@ const MaterialList = ({ cafeId }) => {
onChange={(e) => setNewMaterialUnit(e.target.value)}
style={styles.input}
>
+
+
+
+
@@ -142,7 +147,7 @@ const MaterialList = ({ cafeId }) => {
style={styles.input}
/>
-
);
@@ -182,22 +190,24 @@ const MaterialList = ({ cafeId }) => {
const styles = {
container: {
padding: "20px",
- maxWidth: "600px",
+ maxWidth: "800px",
margin: "0 auto",
- backgroundColor: "#f9f9f9",
- borderRadius: "8px",
- boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
+ },
+ heading: {
+ textAlign: "center",
},
toggleButton: {
- marginBottom: "20px",
- padding: "10px 15px",
+ display: "block",
+ width: "100%",
+ padding: "10px",
+ borderRadius: "5px",
border: "none",
- borderRadius: "4px",
backgroundColor: "#007bff",
- color: "#fff",
- cursor: "pointer",
+ color: "white",
fontSize: "16px",
- transition: "background-color 0.3s",
+ cursor: "pointer",
+ marginBottom: "20px",
+ transition: "background-color 0.3s ease",
},
formContainer: {
transition: "height 0.5s ease-in-out",
@@ -207,56 +217,77 @@ const styles = {
marginBottom: "20px",
},
formGroup: {
- marginBottom: "10px",
+ marginBottom: "15px",
},
label: {
display: "block",
marginBottom: "5px",
- fontWeight: "bold",
+ fontWeight: "600",
},
input: {
width: "100%",
- padding: "8px",
+ padding: "10px",
border: "1px solid #ddd",
- borderRadius: "4px",
+ borderRadius: "8px",
boxSizing: "border-box",
},
- button: {
- padding: "10px 15px",
+ submitButton: {
+ padding: "12px 20px",
border: "none",
- borderRadius: "4px",
+ borderRadius: "8px",
backgroundColor: "#28a745",
color: "#fff",
cursor: "pointer",
fontSize: "16px",
+ transition: "background-color 0.3s, transform 0.3s",
+ boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)",
},
deleteButton: {
marginLeft: "10px",
- padding: "5px 10px",
+ padding: "8px 15px",
border: "none",
- borderRadius: "4px",
+ borderRadius: "8px",
backgroundColor: "#dc3545",
color: "#fff",
cursor: "pointer",
fontSize: "14px",
+ transition: "background-color 0.3s, transform 0.3s",
+ boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)",
},
- list: {
- listStyleType: "none",
- padding: "0",
- margin: "0",
- },
- listItem: {
- padding: "10px",
- borderBottom: "1px solid #ddd",
+ materialList: {
display: "flex",
+ flexWrap: "wrap",
+ gap: "15px",
+ justifyContent: "center",
+ maxHeight: "500px", // Adjust the height as needed
+ overflowY: "auto", // Makes the container scrollable vertically
+ },
+ materialCard: {
+ flex: "1 1 200px",
+ padding: "15px",
+ borderRadius: "8px",
+ border: "1px solid #ddd",
+ backgroundColor: "#fff",
+ boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)",
+ display: "flex",
+ flexDirection: "column",
alignItems: "center",
- justifyContent: "space-between",
+ textAlign: "center",
+ },
+ cardContent: {
+ marginBottom: "10px",
+ },
+ cardTitle: {
+ fontSize: "18px",
+ fontWeight: "600",
+ marginBottom: "5px",
},
image: {
- marginLeft: "10px",
- height: "50px",
- width: "50px",
+ width: "80px",
+ height: "80px",
objectFit: "cover",
+ borderRadius: "8px",
+ marginBottom: "10px",
},
error: {
color: "#dc3545",
diff --git a/src/pages/MaterialMutationsPage.js b/src/pages/MaterialMutationsPage.js
index 02db5cd..d486a11 100644
--- a/src/pages/MaterialMutationsPage.js
+++ b/src/pages/MaterialMutationsPage.js
@@ -1,4 +1,3 @@
-// src/pages/MaterialMutationPage.js
import React, { useEffect, useState } from "react";
import { getMaterials } from "../helpers/materialHelpers";
import {
@@ -6,9 +5,145 @@ import {
getMaterialMutations,
} from "../helpers/materialMutationHelpers";
+// Keyframes for grow animation
+const growKeyframes = `
+ @keyframes grow {
+ 0% {
+ width: 60px;
+ height: 60px;
+ border-top-left-radius: 50%;
+ border-bottom-left-radius: 50%;
+ }
+ 100% {
+ width: 100%;
+ height: auto;
+ border-top-left-radius: 20px;
+ border-bottom-left-radius: 20px;
+ }
+ }
+`;
+
+// Styles
+const styles = {
+ container: {
+ padding: "20px",
+ maxWidth: "800px",
+ margin: "0 auto",
+ },
+ heading: {
+ textAlign: "center",
+ },
+ button: {
+ display: "block",
+ width: "100%",
+ padding: "10px",
+ borderRadius: "5px",
+ border: "none",
+ backgroundColor: "#007bff",
+ color: "white",
+ fontSize: "16px",
+ cursor: "pointer",
+ marginBottom: "20px",
+ transition: "background-color 0.3s ease",
+ },
+ buttonHover: {
+ backgroundColor: "#0056b3",
+ },
+ expandingContainer: (expanded) => ({
+ overflow: "hidden",
+ transition: "all 0.3s ease",
+ animation: expanded ? "grow 0.5s forwards" : "none",
+ marginBottom: "20px",
+ }),
+ materialSelection: {
+ padding: "10px",
+ },
+ materialList: {
+ display: "flex",
+ flexDirection: "row",
+ overflowX: "auto", // Enable horizontal scrolling
+ overflowY: "hidden", // Prevent vertical scrolling
+ whiteSpace: "nowrap", // Prevent wrapping of items
+ gap: "10px", // Space between items
+ padding: "10px 0", // Padding for better appearance
+ },
+
+ materialCard: (selected) => ({
+ flex: "0 0 auto", // Prevent growing or shrinking
+ minWidth: "100px", // Minimum width of the card
+ maxWidth: "150px", // Maximum width of the card
+ padding: "15px",
+ borderRadius: "10px",
+ border: "1px solid #ddd",
+ backgroundColor: selected ? "#007bff" : "#f9f9f9",
+ color: selected ? "white" : "black",
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
+ cursor: "pointer",
+ transition: "background-color 0.3s ease",
+ textAlign: "center", // Center text horizontally
+ display: "flex", // Flexbox for vertical centering
+ alignItems: "center", // Center text vertically
+ justifyContent: "center", // Center text horizontally
+ }),
+ mutationDetails: {
+ padding: "10px",
+ },
+ detailInput: {
+ marginBottom: "15px",
+ },
+ detailLabel: {
+ display: "block",
+ marginBottom: "5px",
+ },
+ detailInputField: {
+ width: "100%",
+ padding: "10px",
+ borderRadius: "5px",
+ border: "1px solid #ddd",
+ },
+ btnSubmit: {
+ width: "100%",
+ padding: "10px",
+ borderRadius: "5px",
+ border: "none",
+ backgroundColor: "#28a745",
+ color: "white",
+ fontSize: "16px",
+ cursor: "pointer",
+ transition: "background-color 0.3s ease",
+ },
+ btnSubmitHover: {
+ backgroundColor: "#218838",
+ },
+ message: {
+ textAlign: "center",
+ fontSize: "16px",
+ },
+ successMessage: {
+ color: "#28a745",
+ },
+ errorMessage: {
+ color: "#dc3545",
+ },
+ mutationList: {
+ display: "flex",
+ flexDirection: "column",
+ gap: "10px",
+ maxHeight: "400px", // Set the desired height
+ overflowY: "auto", // Enable vertical scrolling
+ },
+ mutationCard: {
+ padding: "15px",
+ borderRadius: "10px",
+ border: "1px solid #ddd",
+ backgroundColor: "#f9f9f9",
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
+ },
+};
+
const MaterialMutationPage = ({ cafeId }) => {
const [materials, setMaterials] = useState([]);
- const [materialMutations, setMaterialMutations] = useState([]);
+ const [mutations, setMutations] = useState([]);
const [selectedMaterialId, setSelectedMaterialId] = useState("");
const [oldStock, setOldStock] = useState("");
const [newStock, setNewStock] = useState("");
@@ -16,8 +151,8 @@ const MaterialMutationPage = ({ cafeId }) => {
const [reason, setReason] = useState("");
const [successMessage, setSuccessMessage] = useState("");
const [error, setError] = useState("");
+ const [expanded, setExpanded] = useState(false);
- // Fetch materials when the component mounts
useEffect(() => {
const fetchMaterials = async () => {
try {
@@ -28,31 +163,30 @@ const MaterialMutationPage = ({ cafeId }) => {
}
};
- fetchMaterials();
- }, [cafeId]);
-
- // Fetch material mutations when the component mounts
- useEffect(() => {
- const fetchMaterialMutations = async () => {
+ const fetchMutations = async () => {
try {
const data = await getMaterialMutations(cafeId);
- setMaterialMutations(data);
+ setMutations(data);
} catch (err) {
setError(err.message);
}
};
- fetchMaterialMutations();
+ fetchMaterials();
+ fetchMutations();
}, [cafeId]);
- // Handle form submission
- const handleSubmit = async (e) => {
- e.preventDefault();
- if (!selectedMaterialId) {
- setError("Please select a material.");
- return;
- }
+ const handleToggle = () => setExpanded(!expanded);
+ const handleCancel = () => {
+ setSelectedMaterialId(null);
+ handleToggle();
+ };
+ const handleMaterialSelect = (materialId) => {
+ setSelectedMaterialId(materialId);
+ };
+
+ const handleSubmit = async () => {
try {
const data = { oldStock, newStock, changeDate, reason };
await createMaterialMutation(selectedMaterialId, data);
@@ -62,118 +196,140 @@ const MaterialMutationPage = ({ cafeId }) => {
setChangeDate("");
setReason("");
setSelectedMaterialId("");
-
- // Refresh material mutations list after creation
+ // Refresh the mutations list
const updatedMutations = await getMaterialMutations(cafeId);
- setMaterialMutations(updatedMutations);
+ setMutations(updatedMutations);
} catch (err) {
setError(err.message);
}
};
+ // Filtered mutations based on selected material
+ const filteredMutations = selectedMaterialId
+ ? mutations.filter((mutation) => mutation.materialId === selectedMaterialId)
+ : mutations;
+
return (
-
-
Material Mutations
+
+
Material Mutations
-
Create Material Mutation
-
);
};
diff --git a/src/pages/ScanMeja.js b/src/pages/ScanMeja.js
new file mode 100644
index 0000000..8262004
--- /dev/null
+++ b/src/pages/ScanMeja.js
@@ -0,0 +1,84 @@
+import React, { useState, useEffect } from "react";
+import { useNavigate } from "react-router-dom";
+import { QrReader } from "react-qr-reader"; // Import QrReader as named import
+import styles from "./GuestSideLogin.module.css"; // Import module CSS file for styles
+
+import { getLocalStorage } from "../helpers/localStorageHelpers";
+
+const GuestSideLogin = ({ socket }) => {
+ const navigate = useNavigate();
+ const [qrCode, setQRCode] = useState(""); // State to store QR code
+
+ socket.on("qrCode_readSuccess", (response) => {
+ const { shopId } = response;
+ console.log("qr has been read");
+ navigate("/" + shopId);
+ });
+
+ const setLoginGuestSide = () => {
+ const token = getLocalStorage("auth");
+ socket.emit("read_qrCode", { qrCode, token });
+ };
+
+ // Function to handle QR code scan
+ const handleScan = (data) => {
+ if (data) {
+ setQRCode(data.text); // Set scanned QR code to state
+ setLoginGuestSide(); // Send QR code to backend
+ }
+ };
+
+ // Function to handle QR scan error
+ const handleError = (err) => {
+ console.error(err);
+ };
+
+ // Function to handle manual input
+ const handleManualInput = (e) => {
+ setQRCode(e.target.value);
+ };
+
+ useEffect(() => {
+ if (qrCode.length === 11) {
+ const timer = setTimeout(() => {
+ setLoginGuestSide();
+ }, 1000); // Delay of 1 second (1000 milliseconds)
+
+ return () => clearTimeout(timer); // Cleanup the timer if qrCode changes before the delay completes
+ }
+ }, [qrCode]);
+
+ return (
+
+
+
+ {/* Manual input form */}
+
+
+
+ );
+};
+
+export default GuestSideLogin;