mantab
This commit is contained in:
@@ -114,7 +114,7 @@ export default function Footer({
|
||||
</span>
|
||||
{table.length == 0 && (
|
||||
<img
|
||||
src="https://static-00.iconduck.com/assets.00/qr-scan-icon-2048x2048-aeh36n7y.png"
|
||||
src="https://i.ibb.co.com/Pt2s8N8/qr-scan-icon-2048x2048-aeh36n7y.png"
|
||||
alt="QR Code"
|
||||
className={styles.qrIcon}
|
||||
/>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
.footerContainer {
|
||||
margin-top: 40px;
|
||||
}
|
||||
.footer-rect {
|
||||
height: 75px;
|
||||
background-color: #fff;
|
||||
|
||||
@@ -284,7 +284,7 @@ const Header = ({
|
||||
<Title>{HeaderText}</Title>
|
||||
<div style={{ visibility: showProfile ? "visible" : "hidden" }}>
|
||||
<ProfileImage
|
||||
src="https://static-00.iconduck.com/assets.00/profile-major-icon-1024x1024-9rtgyx30.png"
|
||||
src="https://i.ibb.co.com/fpg1v8J/profile-major-icon-1024x1024-9rtgyx30.png"
|
||||
alt="Profile"
|
||||
onClick={handleImageClick}
|
||||
animate={showRectangle && animate}
|
||||
@@ -349,6 +349,8 @@ const Header = ({
|
||||
<Child onClick={() => setModal("payment_option")}>
|
||||
payment options
|
||||
</Child>
|
||||
|
||||
<Child onClick={() => setModal("reports")}>reports</Child>
|
||||
</Child>
|
||||
</>
|
||||
)}
|
||||
@@ -385,6 +387,8 @@ const Header = ({
|
||||
))}
|
||||
</Child>
|
||||
)}
|
||||
|
||||
<Child onClick={() => setModal("reports")}>reports</Child>
|
||||
</Child>
|
||||
)}
|
||||
{user.username !== undefined && (
|
||||
|
||||
@@ -10,6 +10,7 @@ import Transaction_success from "../pages/Transaction_success";
|
||||
import Transaction_failed from "../pages/Transaction_failed";
|
||||
import MaterialList from "../pages/MaterialList.js";
|
||||
import MaterialMutationsPage from "../pages/MaterialMutationsPage.js";
|
||||
import Reports from "../pages/Reports.js";
|
||||
|
||||
const Modal = ({ shop, isOpen, onClose, modalContent }) => {
|
||||
if (!isOpen) return null;
|
||||
@@ -51,6 +52,9 @@ const Modal = ({ shop, isOpen, onClose, modalContent }) => {
|
||||
{modalContent === "update_stock" && (
|
||||
<MaterialMutationsPage cafeId={shop.cafeId} />
|
||||
)}
|
||||
{modalContent === "reports" && (
|
||||
<Reports cafeId={shop.cafeId} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -90,8 +90,8 @@
|
||||
}
|
||||
|
||||
.expandable-container.expanded {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
position: relative;
|
||||
max-height: 400px;
|
||||
/* Adjust the max-height as needed */
|
||||
@@ -101,12 +101,11 @@
|
||||
|
||||
.expand-button {
|
||||
font-size: 20px;
|
||||
padding-bottom: 10px;
|
||||
position: relative;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 25px;
|
||||
background-color: rgb(108, 255, 128);
|
||||
/* background-color: rgb(218 163 99); */
|
||||
border-radius: 0 0 15px 15px;
|
||||
/* Rounded corners at the bottom */
|
||||
cursor: pointer;
|
||||
@@ -116,7 +115,9 @@
|
||||
}
|
||||
|
||||
.expand-button h5 {
|
||||
font-weight: 500;
|
||||
margin-top: 0px;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.36);
|
||||
}
|
||||
|
||||
.expand-button:hover {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/config.js
|
||||
|
||||
const API_BASE_URL = "https://2jtmkn-5000.csb.app"; // Replace with your actual backend URL
|
||||
const API_BASE_URL = "https://8h8rx8-5000.csb.app"; // Replace with your actual backend URL
|
||||
|
||||
export default API_BASE_URL;
|
||||
|
||||
@@ -28,7 +28,7 @@ export async function getOwnedCafes(userId) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function createCafe(userId) {
|
||||
export async function createCafe(cafeName) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/cafe/create-cafe`, {
|
||||
method: "POST",
|
||||
@@ -36,7 +36,8 @@ export async function createCafe(userId) {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${getAuthToken()}`,
|
||||
},
|
||||
body: JSON.stringify({ ownerId: userId }),
|
||||
body: JSON.stringify({
|
||||
name: cafeName }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
@@ -2,21 +2,16 @@ import API_BASE_URL from "../config.js";
|
||||
|
||||
// Function to retrieve the authentication token from localStorage
|
||||
function getAuthToken() {
|
||||
if (localStorage.getItem("auth")) return localStorage.getItem("auth");
|
||||
return null;
|
||||
return localStorage.getItem("auth") || null;
|
||||
}
|
||||
|
||||
// Helper function to get headers with authentication token
|
||||
const getHeaders = (method = "GET", body = null) => {
|
||||
const headers = {
|
||||
Authorization: `Bearer ${getAuthToken()}`,
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
|
||||
const getHeaders = () => {
|
||||
return {
|
||||
method,
|
||||
headers,
|
||||
body: body ? JSON.stringify(body) : null,
|
||||
headers: {
|
||||
Authorization: `Bearer ${getAuthToken()}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -25,7 +20,14 @@ export const createMaterialMutation = async (materialId, data) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/mutation/create/${materialId}`,
|
||||
getHeaders("POST", data)
|
||||
{
|
||||
...getHeaders(),
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
newStock: data.get("newStock"),
|
||||
reason: data.get("reason"),
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (!response.ok) throw new Error("Failed to create material mutation.");
|
||||
return await response.json();
|
||||
@@ -40,7 +42,10 @@ export const getMaterialMutations = async (cafeId) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/mutation/get-material-mutations/${cafeId}`,
|
||||
getHeaders()
|
||||
{
|
||||
...getHeaders(),
|
||||
method: "GET",
|
||||
}
|
||||
);
|
||||
if (!response.ok) throw new Error("Failed to retrieve material mutations.");
|
||||
return await response.json();
|
||||
@@ -55,7 +60,10 @@ export const getMaterialMutationById = async (mutationId) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/mutation/get-material-mutation/${mutationId}`,
|
||||
getHeaders()
|
||||
{
|
||||
...getHeaders(),
|
||||
method: "GET",
|
||||
}
|
||||
);
|
||||
if (!response.ok) throw new Error("Failed to retrieve material mutation.");
|
||||
return await response.json();
|
||||
@@ -70,7 +78,10 @@ export const getMaterialMutationsByMaterialId = async (materialId) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/mutation/get-material-mutations-by-material/${materialId}`,
|
||||
getHeaders()
|
||||
{
|
||||
...getHeaders(),
|
||||
method: "GET",
|
||||
}
|
||||
);
|
||||
if (!response.ok)
|
||||
throw new Error("Failed to retrieve material mutations by material ID.");
|
||||
|
||||
@@ -288,3 +288,39 @@ export const handlePaymentFromGuestDevice = async (
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Function to retrieve the authentication token from localStorage
|
||||
function getAuthToken() {
|
||||
return localStorage.getItem("auth");
|
||||
}
|
||||
// Helper function to get headers with authentication token
|
||||
const getHeaders = (method = "GET") => {
|
||||
const headers = {
|
||||
Authorization: `Bearer ${getAuthToken()}`,
|
||||
};
|
||||
|
||||
// No content-type header needed for FormData; fetch will handle it automatically
|
||||
if (method !== "POST" && method !== "PUT") {
|
||||
return { method, headers };
|
||||
}
|
||||
|
||||
return {
|
||||
method,
|
||||
headers,
|
||||
};
|
||||
};
|
||||
export const getFavourite = async (cafeId) => {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/transaction/get-favourite/${cafeId}`,
|
||||
getHeaders()
|
||||
);
|
||||
return response.json();
|
||||
};
|
||||
|
||||
export const getAnalytics = async (cafeId) => {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/transaction/get-analytics/${cafeId}`,
|
||||
getHeaders()
|
||||
);
|
||||
return response.json();
|
||||
};
|
||||
|
||||
62
src/pages/CircularDiagram.js
Normal file
62
src/pages/CircularDiagram.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from "react";
|
||||
|
||||
const CircularDiagram = ({ segments }) => {
|
||||
const radius = 100; // Radius of the circle
|
||||
const strokeWidth = 30; // Width of each portion
|
||||
const circumference = 2 * Math.PI * (radius - strokeWidth / 2);
|
||||
|
||||
// Calculate total value to be displayed
|
||||
const totalSold = segments.reduce((sum, segment) => sum + segment.value, 0);
|
||||
|
||||
let startOffset = 0; // Initial offset for each segment
|
||||
|
||||
const svgStyles = {
|
||||
display: "block",
|
||||
margin: "0 auto",
|
||||
};
|
||||
|
||||
return (
|
||||
<svg
|
||||
width={radius * 2}
|
||||
height={radius * 2}
|
||||
viewBox={`0 0 ${radius * 2} ${radius * 2}`}
|
||||
style={svgStyles}
|
||||
>
|
||||
<circle
|
||||
cx={radius}
|
||||
cy={radius}
|
||||
r={radius - strokeWidth / 2}
|
||||
stroke="#eee"
|
||||
strokeWidth={strokeWidth}
|
||||
fill="none"
|
||||
/>
|
||||
{segments.map((segment, index) => {
|
||||
const { value, color } = segment;
|
||||
const segmentLength = (circumference * value) / totalSold;
|
||||
const strokeDashoffset = circumference - startOffset;
|
||||
|
||||
startOffset += segmentLength;
|
||||
|
||||
return (
|
||||
<circle
|
||||
key={index}
|
||||
cx={radius}
|
||||
cy={radius}
|
||||
r={radius - strokeWidth / 2}
|
||||
stroke={color}
|
||||
strokeWidth={strokeWidth}
|
||||
fill="none"
|
||||
strokeDasharray={`${segmentLength} ${
|
||||
circumference - segmentLength
|
||||
}`}
|
||||
strokeDashoffset={strokeDashoffset}
|
||||
strokeLinecap="round" // Rounds the edges of each segment
|
||||
transform={`rotate(-90 ${radius} ${radius})`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default CircularDiagram;
|
||||
@@ -113,7 +113,7 @@ const Dashboard = ({ user, setModal }) => {
|
||||
className={styles.rectangle}
|
||||
onClick={() => setIsCreating(true)}
|
||||
>
|
||||
Create Cafe
|
||||
+
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,7 @@ const MaterialList = ({ cafeId }) => {
|
||||
const [selectedMaterialId, setSelectedMaterialId] = useState(null);
|
||||
const [currentQuantity, setCurrentQuantity] = useState(0);
|
||||
const [quantityChange, setQuantityChange] = useState(0);
|
||||
const [sortOrder, setSortOrder] = useState("desc");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMaterials = async () => {
|
||||
@@ -63,7 +64,7 @@ const MaterialList = ({ cafeId }) => {
|
||||
if (materialMutations.length > 0) {
|
||||
const latestMutation = materialMutations.reduce(
|
||||
(latest, current) =>
|
||||
new Date(current.timestamp) > new Date(latest.timestamp)
|
||||
new Date(current.createdAt) > new Date(latest.createdAt)
|
||||
? current
|
||||
: latest,
|
||||
materialMutations[0]
|
||||
@@ -75,10 +76,24 @@ const MaterialList = ({ cafeId }) => {
|
||||
}
|
||||
}, [selectedMaterialId, mutations]);
|
||||
|
||||
const handleSortChange = (e) => {
|
||||
setSortOrder(e.target.value);
|
||||
};
|
||||
|
||||
const filteredMutations = selectedMaterialId
|
||||
? mutations.filter((mutation) => mutation.materialId === selectedMaterialId)
|
||||
: [];
|
||||
|
||||
const sortedMutations = filteredMutations
|
||||
.filter((mutation) => mutation.materialId === selectedMaterialId)
|
||||
.sort((a, b) => {
|
||||
if (sortOrder === "asc") {
|
||||
return new Date(a.createdAt) - new Date(b.createdAt);
|
||||
} else {
|
||||
return new Date(b.createdAt) - new Date(a.createdAt);
|
||||
}
|
||||
});
|
||||
|
||||
const handleCreateMaterial = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
@@ -166,11 +181,10 @@ const MaterialList = ({ cafeId }) => {
|
||||
try {
|
||||
const newStock = currentQuantity + quantityChange;
|
||||
const formData = new FormData();
|
||||
formData.append("materialId", selectedMaterialId);
|
||||
formData.append("newStock", newStock);
|
||||
formData.append("reason", "Stock update");
|
||||
|
||||
await createMaterialMutation(cafeId, formData);
|
||||
await createMaterialMutation(selectedMaterialId, formData);
|
||||
setQuantityChange(0);
|
||||
const updatedMutations = await getMaterialMutations(cafeId);
|
||||
setMutations(updatedMutations);
|
||||
@@ -188,7 +202,11 @@ const MaterialList = ({ cafeId }) => {
|
||||
const currentMaterial = materials.find(
|
||||
(material) => material.materialId === selectedMaterialId
|
||||
);
|
||||
|
||||
const formatDate = (timestamp) => {
|
||||
const date = new Date(timestamp);
|
||||
return date.toLocaleString();
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<h1 style={styles.heading}>Materials List</h1>
|
||||
@@ -299,7 +317,7 @@ const MaterialList = ({ cafeId }) => {
|
||||
-
|
||||
</button>
|
||||
<button style={styles.quantityDisplay}>
|
||||
{currentQuantity + quantityChange}
|
||||
{currentQuantity + quantityChange}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleQuantityChange(1)}
|
||||
@@ -346,15 +364,27 @@ const MaterialList = ({ cafeId }) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={styles.sortContainer}>
|
||||
<label htmlFor="sortOrder">Sort by : </label>
|
||||
<select
|
||||
id="sortOrder"
|
||||
value={sortOrder}
|
||||
onChange={handleSortChange}
|
||||
style={styles.sortSelect}
|
||||
>
|
||||
<option value="desc">latest</option>
|
||||
<option value="asc">oldest</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{selectedMaterialId && !loading && (
|
||||
<div style={styles.mutationContainer}>
|
||||
{filteredMutations.length > 0 ? (
|
||||
filteredMutations.map((mutation) => (
|
||||
{sortedMutations.length > 0 ? (
|
||||
sortedMutations.map((mutation) => (
|
||||
<div key={mutation.id} style={styles.mutationCard}>
|
||||
<h4 style={styles.mutationTitle}>Mutation ID: {mutation.id}</h4>
|
||||
<h4 style={styles.mutationTitle}>{formatDate(mutation.createdAt)}</h4>
|
||||
<p>Details: {mutation.reason}</p>
|
||||
<p>dari {mutation.oldStock}</p>
|
||||
<p>ke {mutation.newStock}</p>
|
||||
<p>stok {mutation.newStock}</p>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
|
||||
259
src/pages/Reports.js
Normal file
259
src/pages/Reports.js
Normal file
@@ -0,0 +1,259 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { ThreeDots } from "react-loader-spinner";
|
||||
import { getFavourite, getAnalytics } from "../helpers/transactionHelpers.js";
|
||||
import CircularDiagram from "./CircularDiagram";
|
||||
import styles from "./Transactions.module.css";
|
||||
|
||||
const RoundedRectangle = ({ bgColor, title, value, percentage }) => {
|
||||
const containerStyle = {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "center",
|
||||
width: "calc(100% / 2 - 10px)",
|
||||
maxWidth: "250px",
|
||||
height: "auto",
|
||||
borderRadius: "15px",
|
||||
padding: "20px",
|
||||
margin: "5px",
|
||||
textAlign: "left",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
boxSizing: "border-box",
|
||||
};
|
||||
|
||||
const titleStyle = {
|
||||
fontWeight: "bold",
|
||||
marginBottom: "10px",
|
||||
width: "100%",
|
||||
};
|
||||
|
||||
const valueAndPercentageContainerStyle = {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
};
|
||||
|
||||
const valueStyle = {
|
||||
fontSize: "20px",
|
||||
fontWeight: "bold",
|
||||
flex: "1",
|
||||
textAlign: "left",
|
||||
};
|
||||
|
||||
const percentageStyle = {
|
||||
fontSize: "16px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
textAlign: "right",
|
||||
};
|
||||
|
||||
const arrowStyle = {
|
||||
marginLeft: "5px",
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
...containerStyle,
|
||||
backgroundColor: bgColor,
|
||||
}}
|
||||
>
|
||||
<div style={titleStyle}>{title}</div>
|
||||
<div style={valueAndPercentageContainerStyle}>
|
||||
<div style={valueStyle}>{value}</div>
|
||||
<div
|
||||
style={{
|
||||
...percentageStyle,
|
||||
color: percentage > 0 ? "#007bff" : "red",
|
||||
}}
|
||||
>
|
||||
{percentage}%
|
||||
<span style={arrowStyle}>{percentage > 0 ? "↗" : "↘"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const App = ({ cafeId }) => {
|
||||
const [favouriteItems, setFavouriteItems] = useState([]);
|
||||
const [analytics, setAnalytics] = useState({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [filter, setFilter] = useState("monthly"); // Default filter is 'monthly'
|
||||
const [colors, setColors] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const items = await getFavourite(cafeId);
|
||||
const analyticsData = await getAnalytics(cafeId);
|
||||
if (items) setFavouriteItems(items);
|
||||
if (analyticsData) setAnalytics(analyticsData);
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [cafeId]);
|
||||
|
||||
useEffect(() => {
|
||||
const getRandomColor = () => {
|
||||
const letters = "0123456789ABCDEF";
|
||||
let color = "#";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
|
||||
const r = parseInt(color.substring(1, 3), 16);
|
||||
const g = parseInt(color.substring(3, 5), 16);
|
||||
const b = parseInt(color.substring(5, 7), 16);
|
||||
|
||||
return `rgba(${r}, ${g}, ${b}, 1)`;
|
||||
};
|
||||
|
||||
const colors = favouriteItems[filter]?.map(() => getRandomColor()) || [];
|
||||
setColors(colors);
|
||||
}, [favouriteItems, filter]);
|
||||
|
||||
if (loading)
|
||||
return (
|
||||
<div className="Loader">
|
||||
<div className="LoaderChild">
|
||||
<ThreeDots />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const [sold, percentage] = analytics[filter] || [0, 0];
|
||||
const filteredItems = favouriteItems[filter] || [];
|
||||
|
||||
const totalSold = filteredItems.reduce((sum, item) => sum + item.sold, 0);
|
||||
|
||||
const segments = filteredItems.map((item, index) => ({
|
||||
value: item.sold,
|
||||
color: colors[index] || "#cccccc",
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className={styles.Transactions} style={{ backgroundColor: "#cfcfcf" }}>
|
||||
<h2 className={styles["Transactions-title"]}>Reports</h2>
|
||||
<div style={{ marginTop: "30px", textAlign: "center" }}>
|
||||
<div>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="filter"
|
||||
value="daily"
|
||||
checked={filter === "daily"}
|
||||
onChange={() => setFilter("daily")}
|
||||
/>
|
||||
Daily
|
||||
</label>
|
||||
<label style={{ marginLeft: "10px" }}>
|
||||
<input
|
||||
type="radio"
|
||||
name="filter"
|
||||
value="weekly"
|
||||
checked={filter === "weekly"}
|
||||
onChange={() => setFilter("weekly")}
|
||||
/>
|
||||
Weekly
|
||||
</label>
|
||||
<label style={{ marginLeft: "10px" }}>
|
||||
<input
|
||||
type="radio"
|
||||
name="filter"
|
||||
value="monthly"
|
||||
checked={filter === "monthly"}
|
||||
onChange={() => setFilter("monthly")}
|
||||
/>
|
||||
Monthly
|
||||
</label>
|
||||
<label style={{ marginLeft: "10px" }}>
|
||||
<input
|
||||
type="radio"
|
||||
name="filter"
|
||||
value="yearly"
|
||||
checked={filter === "yearly"}
|
||||
onChange={() => setFilter("yearly")}
|
||||
/>
|
||||
Yearly
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "center",
|
||||
marginTop: "20px",
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
<RoundedRectangle
|
||||
bgColor="#E3F5FF"
|
||||
title="Transactions"
|
||||
value={sold}
|
||||
percentage={percentage}
|
||||
/>
|
||||
{filteredItems[0]?.Item != undefined && (
|
||||
<RoundedRectangle
|
||||
bgColor="#E5ECF6"
|
||||
title={filteredItems[0]?.Item.name}
|
||||
value={filteredItems[0]?.sold}
|
||||
percentage={filteredItems[0]?.percentageByPreviousPeriod}
|
||||
/>
|
||||
)}
|
||||
{filteredItems[0]?.Item == undefined && (
|
||||
<RoundedRectangle bgColor="#E5ECF6" value={"No item"} />
|
||||
)}
|
||||
<RoundedRectangle
|
||||
bgColor="#E5ECF6"
|
||||
title="Transactions"
|
||||
value={sold}
|
||||
percentage={percentage}
|
||||
/>
|
||||
<RoundedRectangle
|
||||
bgColor="#E3F5FF"
|
||||
title="Transactions"
|
||||
value={sold}
|
||||
percentage={percentage}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 1 }}>
|
||||
<CircularDiagram segments={segments} />
|
||||
</div>
|
||||
<div style={{ flex: 1, marginLeft: "20px" }}>
|
||||
{filteredItems.map((item, index) => (
|
||||
<h6
|
||||
key={item.itemId}
|
||||
style={{
|
||||
textAlign: "left",
|
||||
color: colors[index],
|
||||
margin: "5px 0",
|
||||
}}
|
||||
>
|
||||
{item.Item.name}: {item.sold}
|
||||
</h6>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
@@ -3,7 +3,7 @@ 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";
|
||||
import { getTableByCode } from "../helpers/tableHelper";
|
||||
|
||||
const GuestSideLogin = ({ socket }) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -15,9 +15,8 @@ const GuestSideLogin = ({ socket }) => {
|
||||
navigate("/" + shopId);
|
||||
});
|
||||
|
||||
const setLoginGuestSide = () => {
|
||||
const token = getLocalStorage("auth");
|
||||
socket.emit("read_qrCode", { qrCode, token });
|
||||
const setLoginGuestSide = async () => {
|
||||
window.location.href = qrCode;
|
||||
};
|
||||
|
||||
// Function to handle QR code scan
|
||||
@@ -39,7 +38,7 @@ const GuestSideLogin = ({ socket }) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (qrCode.length === 11) {
|
||||
if (qrCode.length === 40) {
|
||||
const timer = setTimeout(() => {
|
||||
setLoginGuestSide();
|
||||
}, 1000); // Delay of 1 second (1000 milliseconds)
|
||||
|
||||
Reference in New Issue
Block a user