working on material and table
This commit is contained in:
25
src/App.js
25
src/App.js
@@ -11,6 +11,7 @@ import {
|
|||||||
import socket from "./services/socketService";
|
import socket from "./services/socketService";
|
||||||
|
|
||||||
import Dashboard from "./pages/Dashboard";
|
import Dashboard from "./pages/Dashboard";
|
||||||
|
import ScanMeja from "./pages/ScanMeja";
|
||||||
import LoginPage from "./pages/LoginPage";
|
import LoginPage from "./pages/LoginPage";
|
||||||
import CafePage from "./pages/CafePage";
|
import CafePage from "./pages/CafePage";
|
||||||
import SearchResult from "./pages/SearchResult";
|
import SearchResult from "./pages/SearchResult";
|
||||||
@@ -23,8 +24,6 @@ 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,
|
||||||
@@ -227,6 +226,26 @@ function App() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
|
<Route
|
||||||
|
path="/scan"
|
||||||
|
element={
|
||||||
|
<>
|
||||||
|
<ScanMeja
|
||||||
|
sendParam={handleSetParam}
|
||||||
|
shopName={shop.name}
|
||||||
|
shopOwnerId={shop.ownerId}
|
||||||
|
shopItems={shopItems}
|
||||||
|
shopClerks={shopClerks}
|
||||||
|
socket={socket}
|
||||||
|
user={user}
|
||||||
|
guestSides={guestSides}
|
||||||
|
guestSideOfClerk={guestSideOfClerk}
|
||||||
|
removeConnectedGuestSides={rmConnectedGuestSides}
|
||||||
|
setModal={setModal} // Pass the function to open modal
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/:shopId/:tableId?"
|
path="/:shopId/:tableId?"
|
||||||
element={
|
element={
|
||||||
@@ -257,7 +276,7 @@ function App() {
|
|||||||
path="/:shopId/:tableId?/search"
|
path="/:shopId/:tableId?/search"
|
||||||
element={
|
element={
|
||||||
<>
|
<>
|
||||||
<MaterialList
|
<SearchResult
|
||||||
cafeId={shopId}
|
cafeId={shopId}
|
||||||
sendParam={handleSetParam}
|
sendParam={handleSetParam}
|
||||||
user={user}
|
user={user}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import styles from "./Footer.module.css"; // assuming you have a CSS module for Footer
|
import styles from "./Footer.module.css"; // assuming you have a CSS module for Footer
|
||||||
import { useNavigationHelpers } from "../helpers/navigationHelpers";
|
import { useNavigationHelpers } from "../helpers/navigationHelpers";
|
||||||
|
|
||||||
@@ -8,10 +8,41 @@ export default function Footer({
|
|||||||
cartItemsLength,
|
cartItemsLength,
|
||||||
selectedPage,
|
selectedPage,
|
||||||
}) {
|
}) {
|
||||||
const { goToShop, goToSearch, goToCart, goToTransactions } =
|
const {
|
||||||
useNavigationHelpers(shopId, tableId);
|
goToShop,
|
||||||
|
goToSearch,
|
||||||
|
goToCart,
|
||||||
|
goToTransactions,
|
||||||
|
goToScan,
|
||||||
|
goToNonTable,
|
||||||
|
} = useNavigationHelpers(shopId, tableId);
|
||||||
|
|
||||||
|
const [isStretched, setIsStretched] = useState(false);
|
||||||
|
const scanMejaRef = useRef(null);
|
||||||
|
|
||||||
|
const handleScanMejaClick = () => {
|
||||||
|
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 (
|
return (
|
||||||
|
<div className={styles.footerContainer}>
|
||||||
<div className={styles.item}>
|
<div className={styles.item}>
|
||||||
<div className={styles["footer-rect"]}>
|
<div className={styles["footer-rect"]}>
|
||||||
{/* Shop Icon */}
|
{/* Shop Icon */}
|
||||||
@@ -36,7 +67,7 @@ export default function Footer({
|
|||||||
|
|
||||||
{/* Cart Icon */}
|
{/* Cart Icon */}
|
||||||
<div onClick={goToCart} className={styles["footer-icon"]}>
|
<div onClick={goToCart} className={styles["footer-icon"]}>
|
||||||
{cartItemsLength != "0" && (
|
{cartItemsLength !== 0 && (
|
||||||
<div className={styles.circle}>{cartItemsLength}</div>
|
<div className={styles.circle}>{cartItemsLength}</div>
|
||||||
)}
|
)}
|
||||||
<svg
|
<svg
|
||||||
@@ -56,10 +87,36 @@ export default function Footer({
|
|||||||
<path d="M15.9842 0.166656C7.24421 0.166656 0.150879 7.25999 0.150879 16C0.150879 24.74 7.24421 31.8333 15.9842 31.8333C24.7242 31.8333 31.8175 24.74 31.8175 16C31.8175 7.25999 24.7242 0.166656 15.9842 0.166656ZM21.7 10.205C23.3942 10.205 24.7559 11.5667 24.7559 13.2608C24.7559 14.955 23.3942 16.3167 21.7 16.3167C20.0059 16.3167 18.6442 14.955 18.6442 13.2608C18.6284 11.5667 20.0059 10.205 21.7 10.205ZM12.2 7.70332C14.2584 7.70332 15.9367 9.38166 15.9367 11.44C15.9367 13.4983 14.2584 15.1767 12.2 15.1767C10.1417 15.1767 8.46338 13.4983 8.46338 11.44C8.46338 9.36582 10.1259 7.70332 12.2 7.70332ZM12.2 22.1592V28.0967C8.40005 26.9092 5.39171 23.98 4.06171 20.2433C5.72421 18.47 9.87255 17.5675 12.2 17.5675C13.0392 17.5675 14.1 17.6942 15.2084 17.9158C12.6117 19.2933 12.2 21.1142 12.2 22.1592ZM15.9842 28.6667C15.5567 28.6667 15.145 28.6508 14.7334 28.6033V22.1592C14.7334 19.9108 19.3884 18.7867 21.7 18.7867C23.3942 18.7867 26.3234 19.4042 27.78 20.6075C25.9275 25.31 21.3517 28.6667 15.9842 28.6667Z" />
|
<path d="M15.9842 0.166656C7.24421 0.166656 0.150879 7.25999 0.150879 16C0.150879 24.74 7.24421 31.8333 15.9842 31.8333C24.7242 31.8333 31.8175 24.74 31.8175 16C31.8175 7.25999 24.7242 0.166656 15.9842 0.166656ZM21.7 10.205C23.3942 10.205 24.7559 11.5667 24.7559 13.2608C24.7559 14.955 23.3942 16.3167 21.7 16.3167C20.0059 16.3167 18.6442 14.955 18.6442 13.2608C18.6284 11.5667 20.0059 10.205 21.7 10.205ZM12.2 7.70332C14.2584 7.70332 15.9367 9.38166 15.9367 11.44C15.9367 13.4983 14.2584 15.1767 12.2 15.1767C10.1417 15.1767 8.46338 13.4983 8.46338 11.44C8.46338 9.36582 10.1259 7.70332 12.2 7.70332ZM12.2 22.1592V28.0967C8.40005 26.9092 5.39171 23.98 4.06171 20.2433C5.72421 18.47 9.87255 17.5675 12.2 17.5675C13.0392 17.5675 14.1 17.6942 15.2084 17.9158C12.6117 19.2933 12.2 21.1142 12.2 22.1592ZM15.9842 28.6667C15.5567 28.6667 15.145 28.6508 14.7334 28.6033V22.1592C14.7334 19.9108 19.3884 18.7867 21.7 18.7867C23.3942 18.7867 26.3234 19.4042 27.78 20.6075C25.9275 25.31 21.3517 28.6667 15.9842 28.6667Z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Add more SVG elements as needed */}
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles["footer-bottom"]}></div>
|
<div className={styles["footer-bottom"]}></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Rounded Rectangle with "Scan Meja" and QR Icon */}
|
||||||
|
{shopId && (
|
||||||
|
<div
|
||||||
|
ref={scanMejaRef}
|
||||||
|
onClick={!tableId ? goToScan : handleScanMejaClick}
|
||||||
|
className={`${styles.scanMeja} ${
|
||||||
|
isStretched ? styles.stretched : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{tableId ? `Diantar ke meja ${tableId}` : `Scan Meja\u00A0`}
|
||||||
|
</span>
|
||||||
|
{!tableId && (
|
||||||
|
<img
|
||||||
|
src="https://static-00.iconduck.com/assets.00/qr-scan-icon-2048x2048-aeh36n7y.png"
|
||||||
|
alt="QR Code"
|
||||||
|
className={styles.qrIcon}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{tableId && isStretched && (
|
||||||
|
<button onClick={goToNonTable} className={styles.hapusMejaBtn}>
|
||||||
|
Hapus Meja
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,10 +45,52 @@
|
|||||||
/* Just making it pretty */
|
/* Just making it pretty */
|
||||||
background: #38a9e4;
|
background: #38a9e4;
|
||||||
color: white;
|
color: white;
|
||||||
font-family:
|
font-family: Helvetica, Arial Black, sans;
|
||||||
Helvetica,
|
|
||||||
Arial Black,
|
|
||||||
sans;
|
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
text-align: center;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,3 +63,19 @@ export const getMaterialMutationById = async (mutationId) => {
|
|||||||
throw error;
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -25,7 +25,21 @@ export const useNavigationHelpers = (shopId, tableId) => {
|
|||||||
// Perform the navigation
|
// Perform the navigation
|
||||||
navigate(url);
|
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 = () => {
|
const goToShop = () => {
|
||||||
// Construct the base URL for the shop
|
// Construct the base URL for the shop
|
||||||
let url = `/${shopId}`;
|
let url = `/${shopId}`;
|
||||||
@@ -103,5 +117,7 @@ export const useNavigationHelpers = (shopId, tableId) => {
|
|||||||
goToTransactions,
|
goToTransactions,
|
||||||
goToGuestSideLogin,
|
goToGuestSideLogin,
|
||||||
goToAdminCafes,
|
goToAdminCafes,
|
||||||
|
goToScan,
|
||||||
|
goToNonTable,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
getMaterials,
|
getMaterials,
|
||||||
createMaterial,
|
createMaterial,
|
||||||
deleteMaterial,
|
deleteMaterial,
|
||||||
} from "../helpers/materialHelpers"; // Update import
|
} from "../helpers/materialHelpers";
|
||||||
|
|
||||||
const MaterialList = ({ cafeId }) => {
|
const MaterialList = ({ cafeId }) => {
|
||||||
const [materials, setMaterials] = useState([]);
|
const [materials, setMaterials] = useState([]);
|
||||||
@@ -14,7 +14,7 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
const [deleting, setDeleting] = useState(null);
|
const [deleting, setDeleting] = useState(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [showForm, setShowForm] = useState(false); // For form visibility
|
const [showForm, setShowForm] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchMaterials = async () => {
|
const fetchMaterials = async () => {
|
||||||
@@ -22,7 +22,7 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
try {
|
try {
|
||||||
const data = await getMaterials(cafeId);
|
const data = await getMaterials(cafeId);
|
||||||
setMaterials(data);
|
setMaterials(data);
|
||||||
setError(null); // Clear any previous error
|
setError(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching materials:", error);
|
console.error("Error fetching materials:", error);
|
||||||
setError("Failed to fetch materials.");
|
setError("Failed to fetch materials.");
|
||||||
@@ -41,6 +41,7 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("name", newMaterialName);
|
formData.append("name", newMaterialName);
|
||||||
formData.append("unit", newMaterialUnit);
|
formData.append("unit", newMaterialUnit);
|
||||||
|
console.log(newMaterialImage);
|
||||||
if (newMaterialImage) {
|
if (newMaterialImage) {
|
||||||
formData.append("image", newMaterialImage);
|
formData.append("image", newMaterialImage);
|
||||||
}
|
}
|
||||||
@@ -50,10 +51,10 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
setNewMaterialName("");
|
setNewMaterialName("");
|
||||||
setNewMaterialUnit("kilogram");
|
setNewMaterialUnit("kilogram");
|
||||||
setNewMaterialImage(null);
|
setNewMaterialImage(null);
|
||||||
setShowForm(false); // Hide the form after successful creation
|
setShowForm(false);
|
||||||
const data = await getMaterials(cafeId);
|
const data = await getMaterials(cafeId);
|
||||||
setMaterials(data);
|
setMaterials(data);
|
||||||
setError(null); // Clear any previous error
|
setError(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating material:", error);
|
console.error("Error creating material:", error);
|
||||||
setError("Failed to create material.");
|
setError("Failed to create material.");
|
||||||
@@ -69,7 +70,7 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
setMaterials(
|
setMaterials(
|
||||||
materials.filter((material) => material.materialId !== materialId)
|
materials.filter((material) => material.materialId !== materialId)
|
||||||
);
|
);
|
||||||
setError(null); // Clear any previous error
|
setError(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting material:", error);
|
console.error("Error deleting material:", error);
|
||||||
setError("Failed to delete material.");
|
setError("Failed to delete material.");
|
||||||
@@ -80,7 +81,7 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={styles.container}>
|
<div style={styles.container}>
|
||||||
<h2>Materials List</h2>
|
<h1 style={styles.heading}>Materials List</h1>
|
||||||
|
|
||||||
{/* Display error message if any */}
|
{/* Display error message if any */}
|
||||||
{error && <p style={styles.error}>{error}</p>}
|
{error && <p style={styles.error}>{error}</p>}
|
||||||
@@ -126,9 +127,13 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
onChange={(e) => setNewMaterialUnit(e.target.value)}
|
onChange={(e) => setNewMaterialUnit(e.target.value)}
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
>
|
>
|
||||||
|
<option value="gram">Gram</option>
|
||||||
|
<option value="ons">Ons</option>
|
||||||
<option value="kilogram">Kilogram</option>
|
<option value="kilogram">Kilogram</option>
|
||||||
|
<option value="kuintal">Kuintal</option>
|
||||||
<option value="liter">Liter</option>
|
<option value="liter">Liter</option>
|
||||||
<option value="piece">Piece</option>
|
<option value="piece">Piece</option>
|
||||||
|
<option value="meter">Meter</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div style={styles.formGroup}>
|
<div style={styles.formGroup}>
|
||||||
@@ -142,7 +147,7 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
style={styles.input}
|
style={styles.input}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" style={styles.button} disabled={loading}>
|
<button type="submit" style={styles.submitButton} disabled={loading}>
|
||||||
{loading ? "Creating..." : "Create Material"}
|
{loading ? "Creating..." : "Create Material"}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -152,17 +157,20 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
{loading ? (
|
{loading ? (
|
||||||
<p>Loading materials...</p>
|
<p>Loading materials...</p>
|
||||||
) : (
|
) : (
|
||||||
<ul style={styles.list}>
|
<div style={styles.materialList}>
|
||||||
{materials.map((material) => (
|
{materials.map((material) => (
|
||||||
<li key={material.materialId} style={styles.listItem}>
|
<div key={material.materialId} style={styles.materialCard}>
|
||||||
{material.name} - {material.unit}
|
|
||||||
{material.image && (
|
{material.image && (
|
||||||
<img
|
<img
|
||||||
src={`${API_BASE_URL}/uploads/${material.image}`}
|
src={`${API_BASE_URL}/${material.image}`}
|
||||||
alt={material.name}
|
alt={material.name}
|
||||||
style={styles.image}
|
style={styles.image}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<div style={styles.cardContent}>
|
||||||
|
<h3 style={styles.cardTitle}>{material.name}</h3>
|
||||||
|
<p>{material.unit}</p>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDeleteMaterial(material.materialId)}
|
onClick={() => handleDeleteMaterial(material.materialId)}
|
||||||
disabled={deleting === material.materialId || loading}
|
disabled={deleting === material.materialId || loading}
|
||||||
@@ -170,9 +178,9 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
>
|
>
|
||||||
{deleting === material.materialId ? "Deleting..." : "Delete"}
|
{deleting === material.materialId ? "Deleting..." : "Delete"}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</div>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -182,22 +190,24 @@ const MaterialList = ({ cafeId }) => {
|
|||||||
const styles = {
|
const styles = {
|
||||||
container: {
|
container: {
|
||||||
padding: "20px",
|
padding: "20px",
|
||||||
maxWidth: "600px",
|
maxWidth: "800px",
|
||||||
margin: "0 auto",
|
margin: "0 auto",
|
||||||
backgroundColor: "#f9f9f9",
|
},
|
||||||
borderRadius: "8px",
|
heading: {
|
||||||
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
textAlign: "center",
|
||||||
},
|
},
|
||||||
toggleButton: {
|
toggleButton: {
|
||||||
marginBottom: "20px",
|
display: "block",
|
||||||
padding: "10px 15px",
|
width: "100%",
|
||||||
|
padding: "10px",
|
||||||
|
borderRadius: "5px",
|
||||||
border: "none",
|
border: "none",
|
||||||
borderRadius: "4px",
|
|
||||||
backgroundColor: "#007bff",
|
backgroundColor: "#007bff",
|
||||||
color: "#fff",
|
color: "white",
|
||||||
cursor: "pointer",
|
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
transition: "background-color 0.3s",
|
cursor: "pointer",
|
||||||
|
marginBottom: "20px",
|
||||||
|
transition: "background-color 0.3s ease",
|
||||||
},
|
},
|
||||||
formContainer: {
|
formContainer: {
|
||||||
transition: "height 0.5s ease-in-out",
|
transition: "height 0.5s ease-in-out",
|
||||||
@@ -207,56 +217,77 @@ const styles = {
|
|||||||
marginBottom: "20px",
|
marginBottom: "20px",
|
||||||
},
|
},
|
||||||
formGroup: {
|
formGroup: {
|
||||||
marginBottom: "10px",
|
marginBottom: "15px",
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
display: "block",
|
display: "block",
|
||||||
marginBottom: "5px",
|
marginBottom: "5px",
|
||||||
fontWeight: "bold",
|
fontWeight: "600",
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
padding: "8px",
|
padding: "10px",
|
||||||
border: "1px solid #ddd",
|
border: "1px solid #ddd",
|
||||||
borderRadius: "4px",
|
borderRadius: "8px",
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
},
|
},
|
||||||
button: {
|
submitButton: {
|
||||||
padding: "10px 15px",
|
padding: "12px 20px",
|
||||||
border: "none",
|
border: "none",
|
||||||
borderRadius: "4px",
|
borderRadius: "8px",
|
||||||
backgroundColor: "#28a745",
|
backgroundColor: "#28a745",
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
|
transition: "background-color 0.3s, transform 0.3s",
|
||||||
|
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)",
|
||||||
},
|
},
|
||||||
deleteButton: {
|
deleteButton: {
|
||||||
marginLeft: "10px",
|
marginLeft: "10px",
|
||||||
padding: "5px 10px",
|
padding: "8px 15px",
|
||||||
border: "none",
|
border: "none",
|
||||||
borderRadius: "4px",
|
borderRadius: "8px",
|
||||||
backgroundColor: "#dc3545",
|
backgroundColor: "#dc3545",
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
fontSize: "14px",
|
fontSize: "14px",
|
||||||
|
transition: "background-color 0.3s, transform 0.3s",
|
||||||
|
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)",
|
||||||
},
|
},
|
||||||
list: {
|
materialList: {
|
||||||
listStyleType: "none",
|
|
||||||
padding: "0",
|
|
||||||
margin: "0",
|
|
||||||
},
|
|
||||||
listItem: {
|
|
||||||
padding: "10px",
|
|
||||||
borderBottom: "1px solid #ddd",
|
|
||||||
display: "flex",
|
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",
|
alignItems: "center",
|
||||||
justifyContent: "space-between",
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
cardContent: {
|
||||||
|
marginBottom: "10px",
|
||||||
|
},
|
||||||
|
cardTitle: {
|
||||||
|
fontSize: "18px",
|
||||||
|
fontWeight: "600",
|
||||||
|
marginBottom: "5px",
|
||||||
},
|
},
|
||||||
image: {
|
image: {
|
||||||
marginLeft: "10px",
|
width: "80px",
|
||||||
height: "50px",
|
height: "80px",
|
||||||
width: "50px",
|
|
||||||
objectFit: "cover",
|
objectFit: "cover",
|
||||||
|
borderRadius: "8px",
|
||||||
|
marginBottom: "10px",
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
color: "#dc3545",
|
color: "#dc3545",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// src/pages/MaterialMutationPage.js
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { getMaterials } from "../helpers/materialHelpers";
|
import { getMaterials } from "../helpers/materialHelpers";
|
||||||
import {
|
import {
|
||||||
@@ -6,9 +5,145 @@ import {
|
|||||||
getMaterialMutations,
|
getMaterialMutations,
|
||||||
} from "../helpers/materialMutationHelpers";
|
} 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 MaterialMutationPage = ({ cafeId }) => {
|
||||||
const [materials, setMaterials] = useState([]);
|
const [materials, setMaterials] = useState([]);
|
||||||
const [materialMutations, setMaterialMutations] = useState([]);
|
const [mutations, setMutations] = useState([]);
|
||||||
const [selectedMaterialId, setSelectedMaterialId] = useState("");
|
const [selectedMaterialId, setSelectedMaterialId] = useState("");
|
||||||
const [oldStock, setOldStock] = useState("");
|
const [oldStock, setOldStock] = useState("");
|
||||||
const [newStock, setNewStock] = useState("");
|
const [newStock, setNewStock] = useState("");
|
||||||
@@ -16,8 +151,8 @@ const MaterialMutationPage = ({ cafeId }) => {
|
|||||||
const [reason, setReason] = useState("");
|
const [reason, setReason] = useState("");
|
||||||
const [successMessage, setSuccessMessage] = useState("");
|
const [successMessage, setSuccessMessage] = useState("");
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
// Fetch materials when the component mounts
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchMaterials = async () => {
|
const fetchMaterials = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -28,31 +163,30 @@ const MaterialMutationPage = ({ cafeId }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchMaterials();
|
const fetchMutations = async () => {
|
||||||
}, [cafeId]);
|
|
||||||
|
|
||||||
// Fetch material mutations when the component mounts
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchMaterialMutations = async () => {
|
|
||||||
try {
|
try {
|
||||||
const data = await getMaterialMutations(cafeId);
|
const data = await getMaterialMutations(cafeId);
|
||||||
setMaterialMutations(data);
|
setMutations(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err.message);
|
setError(err.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchMaterialMutations();
|
fetchMaterials();
|
||||||
|
fetchMutations();
|
||||||
}, [cafeId]);
|
}, [cafeId]);
|
||||||
|
|
||||||
// Handle form submission
|
const handleToggle = () => setExpanded(!expanded);
|
||||||
const handleSubmit = async (e) => {
|
const handleCancel = () => {
|
||||||
e.preventDefault();
|
setSelectedMaterialId(null);
|
||||||
if (!selectedMaterialId) {
|
handleToggle();
|
||||||
setError("Please select a material.");
|
};
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const handleMaterialSelect = (materialId) => {
|
||||||
|
setSelectedMaterialId(materialId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
const data = { oldStock, newStock, changeDate, reason };
|
const data = { oldStock, newStock, changeDate, reason };
|
||||||
await createMaterialMutation(selectedMaterialId, data);
|
await createMaterialMutation(selectedMaterialId, data);
|
||||||
@@ -62,119 +196,141 @@ const MaterialMutationPage = ({ cafeId }) => {
|
|||||||
setChangeDate("");
|
setChangeDate("");
|
||||||
setReason("");
|
setReason("");
|
||||||
setSelectedMaterialId("");
|
setSelectedMaterialId("");
|
||||||
|
// Refresh the mutations list
|
||||||
// Refresh material mutations list after creation
|
|
||||||
const updatedMutations = await getMaterialMutations(cafeId);
|
const updatedMutations = await getMaterialMutations(cafeId);
|
||||||
setMaterialMutations(updatedMutations);
|
setMutations(updatedMutations);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err.message);
|
setError(err.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Filtered mutations based on selected material
|
||||||
|
const filteredMutations = selectedMaterialId
|
||||||
|
? mutations.filter((mutation) => mutation.materialId === selectedMaterialId)
|
||||||
|
: mutations;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div style={styles.container}>
|
||||||
<h1>Material Mutations</h1>
|
<h1 style={styles.heading}>Material Mutations</h1>
|
||||||
|
|
||||||
<h2>Create Material Mutation</h2>
|
<button style={styles.button} onClick={handleCancel}>
|
||||||
<form onSubmit={handleSubmit}>
|
{expanded ? "Cancel" : "Create Material Mutation"}
|
||||||
<div>
|
</button>
|
||||||
<label>
|
|
||||||
Select Material:
|
<div style={styles.expandingContainer(expanded)}>
|
||||||
<select
|
{!expanded && (
|
||||||
value={selectedMaterialId}
|
<div style={styles.materialSelection}>
|
||||||
onChange={(e) => setSelectedMaterialId(e.target.value)}
|
<div style={styles.materialList}>
|
||||||
required
|
<div
|
||||||
|
style={styles.materialCard(selectedMaterialId === "")}
|
||||||
|
onClick={() => handleMaterialSelect("")}
|
||||||
>
|
>
|
||||||
<option value="">Select a material</option>
|
All
|
||||||
{materials.map((material) => (
|
|
||||||
<option key={material.materialId} value={material.materialId}>
|
|
||||||
{material.name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
{materials.map((material) => (
|
||||||
|
<div
|
||||||
|
key={material.materialId}
|
||||||
|
style={styles.materialCard(
|
||||||
|
selectedMaterialId === material.materialId
|
||||||
|
)}
|
||||||
|
onClick={() => handleMaterialSelect(material.materialId)}
|
||||||
|
>
|
||||||
|
{material.name}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{expanded && !selectedMaterialId && (
|
||||||
|
<div style={styles.materialSelection}>
|
||||||
|
<div style={styles.materialList}>
|
||||||
|
{materials.map((material) => (
|
||||||
|
<div
|
||||||
|
key={material.materialId}
|
||||||
|
style={styles.materialCard(
|
||||||
|
selectedMaterialId === material.materialId
|
||||||
|
)}
|
||||||
|
onClick={() => handleMaterialSelect(material.materialId)}
|
||||||
|
>
|
||||||
|
{material.name}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div>
|
{expanded && selectedMaterialId && (
|
||||||
<label>
|
<div style={styles.mutationDetails}>
|
||||||
Old Stock:
|
<div style={styles.detailInput}>
|
||||||
|
<label style={styles.detailLabel}>Old Stock:</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
|
style={styles.detailInputField}
|
||||||
value={oldStock}
|
value={oldStock}
|
||||||
onChange={(e) => setOldStock(e.target.value)}
|
onChange={(e) => setOldStock(e.target.value)}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div style={styles.detailInput}>
|
||||||
<label>
|
<label style={styles.detailLabel}>New Stock:</label>
|
||||||
New Stock:
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
|
style={styles.detailInputField}
|
||||||
value={newStock}
|
value={newStock}
|
||||||
onChange={(e) => setNewStock(e.target.value)}
|
onChange={(e) => setNewStock(e.target.value)}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div style={styles.detailInput}>
|
||||||
<label>
|
<label style={styles.detailLabel}>Change Date:</label>
|
||||||
Change Date:
|
|
||||||
<input
|
<input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
|
style={styles.detailInputField}
|
||||||
value={changeDate}
|
value={changeDate}
|
||||||
onChange={(e) => setChangeDate(e.target.value)}
|
onChange={(e) => setChangeDate(e.target.value)}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div style={styles.detailInput}>
|
||||||
<label>
|
<label style={styles.detailLabel}>Reason:</label>
|
||||||
Reason:
|
|
||||||
<textarea
|
<textarea
|
||||||
|
style={styles.detailInputField}
|
||||||
value={reason}
|
value={reason}
|
||||||
onChange={(e) => setReason(e.target.value)}
|
onChange={(e) => setReason(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit">Create Mutation</button>
|
<button style={styles.btnSubmit} onClick={handleSubmit}>
|
||||||
</form>
|
Create Mutation
|
||||||
|
</button>
|
||||||
{successMessage && <p>{successMessage}</p>}
|
</div>
|
||||||
{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>
|
</div>
|
||||||
|
|
||||||
|
{successMessage && (
|
||||||
|
<p style={{ ...styles.message, ...styles.successMessage }}>
|
||||||
|
{successMessage}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{error && (
|
||||||
|
<p style={{ ...styles.message, ...styles.errorMessage }}>
|
||||||
|
Error: {error}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div style={styles.mutationList}>
|
||||||
|
{filteredMutations.map((mutation) => (
|
||||||
|
<div key={mutation.mutationId} style={styles.mutationCard}>
|
||||||
|
<h3>
|
||||||
|
{mutation.Material.name}-{mutation.reason}
|
||||||
|
</h3>
|
||||||
|
<p>Old Stock: {mutation.oldStock}</p>
|
||||||
|
<p>New Stock: {mutation.newStock}</p>
|
||||||
|
<p>Change Date: {new Date(mutation.changeDate).toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
84
src/pages/ScanMeja.js
Normal file
84
src/pages/ScanMeja.js
Normal file
@@ -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 (
|
||||||
|
<div className={styles.qrisReaderContainer}>
|
||||||
|
<div className={styles.qrScannerContainer}>
|
||||||
|
<QrReader
|
||||||
|
constraints={{ facingMode: "environment" }}
|
||||||
|
delay={500}
|
||||||
|
onResult={handleScan}
|
||||||
|
onError={handleError}
|
||||||
|
videoId="video"
|
||||||
|
className={styles.qrReader} // Apply the class
|
||||||
|
videoContainerStyle={{
|
||||||
|
width: "100vw",
|
||||||
|
height: "100vh",
|
||||||
|
paddingTop: "0px",
|
||||||
|
}}
|
||||||
|
videoStyle={{ width: "100%", height: "100%" }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className={styles.focusSquare}></div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.inputContainer}>
|
||||||
|
{/* Manual input form */}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={qrCode}
|
||||||
|
onChange={handleManualInput}
|
||||||
|
placeholder="Enter QRIS Code"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GuestSideLogin;
|
||||||
Reference in New Issue
Block a user