diff --git a/src/App.js b/src/App.js index 825e739..2cefcc6 100644 --- a/src/App.js +++ b/src/App.js @@ -11,6 +11,7 @@ import { import socket from "./services/socketService"; import Dashboard from "./pages/Dashboard"; +import ScanMeja from "./pages/ScanMeja"; import LoginPage from "./pages/LoginPage"; import CafePage from "./pages/CafePage"; import SearchResult from "./pages/SearchResult"; @@ -23,8 +24,6 @@ import GuestSideLogin from "./pages/GuestSideLogin"; import GuestSide from "./pages/GuestSide"; import { getItemTypesWithItems } from "./helpers/itemHelper.js"; -import MaterialList from "./pages/MaterialList"; - import { getConnectedGuestSides, getClerks, @@ -227,6 +226,26 @@ function App() { } /> } /> + + + + } + /> - { + if (tableId) { + setIsStretched(true); + } else { + goToTransactions(); + } + }; + + const handleClickOutside = (event) => { + if (scanMejaRef.current && !scanMejaRef.current.contains(event.target)) { + setIsStretched(false); + } + }; + + useEffect(() => { + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); return ( -
-
- {/* 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 && ( + QR Code + )} + {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} />
- @@ -152,17 +157,20 @@ const MaterialList = ({ cafeId }) => { {loading ? (

Loading materials...

) : ( -
    +
    {materials.map((material) => ( -
  • - {material.name} - {material.unit} +
    {material.image && ( {material.name} )} +
    +

    {material.name}

    +

    {material.unit}

    +
    -
  • +
    ))} -
+
)}
); @@ -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

-
-
- -
+
+
+ )} + {expanded && !selectedMaterialId && ( +
+
+ {materials.map((material) => ( +
handleMaterialSelect(material.materialId)} + > + {material.name} +
+ ))} +
+
+ )} -
- -
+ {expanded && selectedMaterialId && ( +
+
+ + setOldStock(e.target.value)} + /> +
-
- -
+
+ + setNewStock(e.target.value)} + /> +
-
- -
+
+ + setChangeDate(e.target.value)} + /> +
-
-