point of sale

This commit is contained in:
Sibunnayak 2024-04-16 15:32:11 +05:30
parent eb51adde42
commit ae13816aa9
3 changed files with 1001 additions and 1 deletions

View File

@ -306,7 +306,13 @@ const _nav = [
icon: <CIcon icon={cilNotes} customClassName="nav-icon" />,
to: "/blogs",
},
//Point of Sale start
{
component: CNavItem,
name: "Point of Sale",
icon: <CIcon icon={cilNotes} customClassName="nav-icon" />,
to: "/pos",
},
// {
// component: CNavGroup,
// name: "Blog",

View File

@ -136,6 +136,7 @@ import CityRevenueCharts from "./views/Charts/CityRevenue";
import { element } from "prop-types";
import OrderdayChart from "./views/Charts/OrderDaywise";
import RevenueCharts from "./views/Charts/RevenueCharts";
import Pos from "./views/PointOfSale/Pos";
const routes = [
{ path: "/", exact: true, name: "Home" },
{
@ -593,6 +594,12 @@ const routes = [
name: "Revenue (Day Wise)",
element: RevenueCharts,
},
//Point of Sale Section
{
path: "/pos",
name: "Point of Sale",
element: Pos,
},
];
export default routes;

View File

@ -0,0 +1,987 @@
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import Button from "@material-ui/core/Button";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import { isAutheticated } from "src/auth";
import swal from "sweetalert";
import {
Box,
FormControl,
IconButton,
InputLabel,
MenuItem,
Select,
TextField,
Container,
Grid,
Paper,
Typography,
} from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import ClearIcon from "@mui/icons-material/Clear";
// import PercentIcon from "@mui/icons-material/Percent";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
const Pos = () => {
const token = isAutheticated();
const [query, setQuery] = useState("");
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [success, setSuccess] = useState(true);
const [posData, setPosData] = useState([]);
const [user, setUser] = useState([
// {
// id: 1,
// first_Name: "John",
// last_Name: "Doe",
// street: "123 Main St",
// city: "New York",
// state: "NY",
// country: "USA",
// postalCode: "10001",
// phone_Number: "123-456-7890",
// },
// {
// id: 2,
// first_Name: "Jane",
// last_Name: "Smith",
// street: "456 Elm St",
// city: "Los Angeles",
// state: "CA",
// country: "USA",
// postalCode: "90001",
// phone_Number: "123-456-7890",
// },
// {
// id: 3,
// first_Name: "Michael",
// last_Name: "Johnson",
// street: "789 Oak St",
// city: "Chicago",
// state: "IL",
// country: "USA",
// postalCode: "60007",
// phone_Number: "123-456-7890",
// },
// {
// id: 4,
// first_Name: "Sarah",
// last_Name: "Williams",
// street: "101 Pine St",
// city: "Miami",
// state: "FL",
// country: "USA",
// postalCode: "33101",
// phone_Number: "123-456-7890",
// },
// {
// id: 5,
// first_Name: "John",
// last_Name: "Doe",
// street: " Main St",
// city: "New York",
// state: "NY",
// country: "USA",
// postalCode: "10001",
// phone_Number: "123-122-3210",
// },
]);
const getUsers = async () => {
axios
.get(`/api/v1/admin/users`, {
headers: {
Authorization: `Bearer ${token}`,
},
})
.then((res) => {
setUser(res.data.users);
setLoading(false);
})
.catch((error) => {
swal({
title: error,
text: "please login to access the resource or refresh the page ",
icon: "error",
button: "Retry",
dangerMode: true,
});
setLoading(false);
});
};
const [showData, setShowData] = useState(user);
const [currentUser, setCurrentUser] = useState(null);
const [salesType, setSalesType] = useState("");
const [storedelivery, setStoreDelivery] = useState("");
// Function to handle change in radio button selection
const handleSalesTypeChange = (event) => {
setSalesType(event.target.value);
};
const handlestoredeliveryChange = (event) => {
setStoreDelivery(event.target.value);
};
useEffect(() => {
setTimeout(() => {
if (query !== "") {
setCurrentUser(null);
const lowerCaseQuery = query.toLowerCase(); // Convert query to lowercase
const searchedResult = user.filter((item) =>
item.name.toString().toLowerCase().includes(lowerCaseQuery)
);
setShowData(searchedResult);
setLoading(false);
}
// else {
// setShowData(user); // Show all users when query is empty
// setCurrentUser(null); // Reset current user when query is empty
// }
}, 100);
}, [query]);
const handleClick = (id) => {
setQuery("");
const customer = user.find((user) => user.id === id);
setCurrentUser(customer);
};
// part 2*****************************8
const [productData, setProductData] = useState([]);
const [filteredItems, setFilteredItems] = useState([]);
const [categories, setCategories] = useState([]);
const [selectedCategories, setSelectedCategories] = useState([]);
const [categoryValue, setCategoryValue] = useState("All");
const [cartItem, setCartItem] = useState([]);
const [individualSubtotals, setIndividualSubtotals] = useState([]);
const [total, setTotal] = useState(0);
const getAllProducts = async () => {
try {
const response = await axios.get("/api/product/getAll/");
if (response.status === 200) {
// setProductData(response?.data?.product);
const activeProducts = response?.data?.product.filter(
(product) => product.product_Status === "Active"
);
setProductData(activeProducts);
}
} catch (error) {
console.error("Error fetching products:", error);
}
};
const getCaterogy = async () => {
try {
const response = await axios.get("/api/category/getCategories");
if (response.status === 200) {
setCategories(response?.data?.categories);
}
} catch (error) {
console.error("Error fetching categories:", error);
}
};
const handleChange = (event) => {
const { name, value } = event.target;
if (name === "category") {
setCategoryValue(value);
setSelectedCategories((prevCategories) => {
if (prevCategories.includes(value)) {
return prevCategories.filter((category) => category !== value);
} else {
return [...prevCategories, value];
}
});
}
};
const items = () => {
setFilteredItems(
productData?.filter((item) => {
const categoryMatch =
categoryValue === "All" ||
item.category.categoryName === categoryValue;
return categoryMatch;
})
);
};
useEffect(() => {
setLoading(true);
getAllProducts()
.then(() => {
getCaterogy();
})
.catch((error) => {
console.error("Error fetching products:", error);
})
.finally(() => {
setLoading(false); // Set loading to false after data fetching
});
}, [token]);
useEffect(() => {
items();
}, [categoryValue, productData]);
const styles = {
selectHeading: {
fontFamily: "inter",
fontWeight: "600",
fontSize: "16px",
color: "#6C7275",
marginBottom: ".5rem",
},
tableContainer: {
maxHeight: 360,
height: 360,
overflowY: "auto", // Enable vertical scrolling
},
headingStyle: {
fontFamily: "inter",
fontWeight: "600",
fontSize: "16px",
color: "#121212",
width: "70%",
borderBottom: "1px solid black",
},
};
const addToCart = (item) => {
// Check if the item is already in the cart
const isItemInCart = cartItem.find((cartItem) => cartItem._id === item._id);
if (isItemInCart) {
// Item is already in the cart, show a confirmation message
swal("Item already in cart", "", "info");
} else {
// Item is not in the cart, add it to the cart with quantity initialized to 1
setCartItem([...cartItem, { ...item, quantity: 1 }]);
// Show a success message
swal("Item added to cart", "", "success");
}
};
const handleIncrease = (index) => {
const newCart = [...cartItem];
newCart[index].quantity += 1;
newCart[index].total_amount =
newCart[index].quantity * newCart[index].price +
newCart[index].gst_amount; // Recalculate total amount
setCartItem(newCart);
};
const handleDecrease = (index) => {
const newCart = [...cartItem];
if (newCart[index].quantity > 1) {
newCart[index].quantity -= 1;
newCart[index].total_amount =
newCart[index].quantity * newCart[index].price +
newCart[index].gst_amount; // Recalculate total amount
setCartItem(newCart);
}
};
const removeCartItemHandler = (id) => {
console.log("id", id);
const newCart = cartItem.filter((item) => item._id !== id);
setCartItem(newCart);
};
// Calculate subtotal of all items in cart
const calculateTotal = () => {
let subtotal = 0;
cartItem.forEach((item) => {
subtotal += item.total_amount;
});
setTotal(subtotal);
};
useEffect(() => {
calculateTotal();
}, [cartItem]);
console.log(user);
return (
<div className="main-content">
<div className="page-content">
<div className="container-fluid">
{/* Part 1: Top Part */}
<div className="row">
<div className="col-lg-12">
<div className="card">
<div
className="card-body"
style={{ backgroundColor: "#D3D3D3" }}
>
{/* Customer search */}
<div
style={{
flex: "1",
display: "flex",
margin: "1rem 1rem 1rem 0rem",
}}
>
<Typography
style={{
fontWeight: "bold",
marginTop: "1rem",
marginRight: "2rem",
}}
>
Select Customer:
</Typography>
<TextField
style={{
background: "white",
padding: "0.5rem",
borderRadius: "8px",
border: "1px solid grey",
marginRight: "2rem",
height: "3rem",
position: "relative",
width: "300px",
}}
placeholder="Search here..."
variant="standard"
color="white"
value={query}
onChange={(e) => setQuery(e.target.value)}
InputProps={{
endAdornment: (
<IconButton
sx={{
background: "white",
color: "grey",
height: "2.9rem",
width: "3rem",
position: "absolute",
right: "-8px",
top: "-8px",
borderRadius: "0px 8px 8px 0px",
}}
// onClick={() => handleSearchClick(query)}
>
<SearchIcon fontSize="small" />
</IconButton>
),
disableUnderline: true,
}}
/>
</div>
{query !== "" && (
<div className="table-responsive table-shoot mt-3">
<table
className="table table-centered table-nowrap"
style={{ border: "1px solid" }}
>
<thead
className="thead-info"
style={{ background: "rgb(140, 213, 213)" }}
>
<tr>
<th className="text-start">Customer Name</th>
<th className="text-start">Address</th>
<th className="text-start">Mobile No.</th>
</tr>
</thead>
<tbody>
{!loading && showData.length === 0 && (
<tr className="text-center">
<td colSpan="3">
<div className="page-title-right">
<Button
variant="contained"
color="primary"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
}}
onClick={() => {
navigate("/pos/new-customer", {
replace: true,
});
}}
>
Add New Customer
</Button>
</div>
</td>
</tr>
)}
{loading ? (
<tr>
<td className="text-center" colSpan="3">
Loading...
</td>
</tr>
) : (
showData.map((user, index) => (
<tr
key={index}
onClick={() => handleClick(user?._id)}
className="cursor-pointer hover:bg-gray-100"
>
<td className="text-start">{user.name}</td>
{/* <td className="text-start">{`${user?.street}, ${user?.city}, ${user?.state}, ${user?.country}, ${user?.postalCode}`}</td>
<td className="text-start">{`${user?.phone_Number}`}</td> */}
</tr>
))
)}
</tbody>
</table>
</div>
)}
{/* Display selected customer */}
{currentUser && (
// Display customer details
<div style={{ display: "flex" }}>
<div style={{ flex: "1" }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<div style={{ display: "flex" }}>
<Typography
style={{
fontWeight: "bold",
marginRight: "0.5rem",
}}
>
Customer Name:
</Typography>
<Typography>{`${currentUser?.first_Name} ${currentUser?.last_Name}`}</Typography>
</div>
<div style={{ display: "flex" }}>
<Typography
style={{
fontWeight: "bold",
marginRight: "0.5rem",
}}
>
Mobile No.:
</Typography>
<Typography>{`${currentUser?.phone_Number}`}</Typography>
</div>
<div style={{ display: "flex" }}>
<Typography
style={{
fontWeight: "bold",
marginRight: "0.5rem",
}}
>
Address:
</Typography>
<Typography>{`${currentUser?.street}, ${currentUser?.city}, ${currentUser?.state}, ${currentUser?.country}, ${currentUser?.postalCode}`}</Typography>
</div>
</div>
</div>
</div>
)}
{/* Sales Type radio buttons */}
<div style={{ display: "flex", alignItems: "center" }}>
<Typography
style={{ fontWeight: "bold", marginRight: "0.5rem" }}
>
Sales Type:
</Typography>
<div style={{ display: "flex", flexDirection: "row" }}>
<div
style={{
display: "flex",
marginRight: "0.5rem",
}}
>
<input
type="radio"
id="inStoreDelivery"
name="salesType"
value="inStoreDelivery"
checked={salesType === "inStoreDelivery"}
onChange={handleSalesTypeChange}
className="mr-2"
/>
<label
htmlFor="inStoreDelivery"
style={{ marginTop: "2px" }}
>
In Store Delivery
</label>
</div>
<div style={{ display: "flex" }}>
<input
type="radio"
id="shipToCustomer"
name="salesType"
value="shipToCustomer"
checked={salesType === "shipToCustomer"}
onChange={handleSalesTypeChange}
className="mr-2"
/>
<label
htmlFor="shipToCustomer"
style={{ marginTop: "2px" }}
>
Ship to Customer
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/* Part 2: Panel 1 and Panel 2 */}
<div className="row" style={{ marginTop: "0.5rem" }}>
{/* Panel 1 (Left Hand Side) */}
<div className="col-lg-5">
<div className="card-body" style={{ border: "1px solid black" }}>
{/* Category selection */}
<div
style={{
display: "flex",
alignItems: "center",
marginLeft: "1rem",
}}
>
<Typography
style={{
fontWeight: "bold",
marginRight: "0.5rem",
marginBottom: "1rem",
}}
>
Categories:
</Typography>
<FormControl>
<Select
name="category"
value={categoryValue}
onChange={handleChange}
style={{
display: "flex",
marginBottom: "1rem",
width: "120px",
height: "2rem",
}}
>
<MenuItem
value="All"
style={{ display: "block", marginLeft: "0.5rem" }}
>
All
</MenuItem>
{categories.map((category, index) => (
<MenuItem
value={category.categoryName}
key={index}
style={{ display: "block", marginLeft: "0.5rem" }}
>
{category.categoryName}
</MenuItem>
))}
</Select>
</FormControl>
</div>
{/* Product display */}
<div style={{ marginTop: "-0.5rem" }}>
<div
className="table-container"
style={styles.tableContainer}
>
<table className="table table-centered table-nowrap ">
<thead className="thead-info">
<tr>
<th>Product Image</th>
<th>Product Name</th>
<th>Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{filteredItems.map((item, index) => (
<tr key={index}>
<td>
{item.image && item.image.length > 0 && (
<img
src={item.image[0].url}
alt="Product Image"
style={{ width: "50px" }}
/>
)}
</td>
<td>{item.name}</td>
<td>{item.price}</td>
<td>
<Button
variant="contained"
color="primary"
onClick={() => addToCart(item)}
>
Add
</Button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
{/* Panel 2 (Right Hand Side) */}
<div
className="col-lg-7"
style={{ marginLeft: "-1rem", marginTop: "2px" }}
>
<div
className="card-body"
style={{
height: "400px",
maxHeight: "400px",
overflowY: "auto",
border: "1px solid black",
}}
>
{/* Display added products */}
<Typography
style={{
fontWeight: "bold",
marginBottom: "0.5rem",
marginLeft: "1rem",
}}
>
Added Products:
</Typography>
{/* Display added products */}
<div>
<Box>
<Grid container>
<Grid item lg={12}>
<Box>
<TableContainer
component={Paper}
elevation={0}
style={{
borderBottom: "1.5px solid rgb(207 210 213)",
borderRadius: "0",
}}
>
<Table aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell style={styles.headingStyle}>
Product
</TableCell>
<TableCell style={styles.headingStyle}>
Quantity
</TableCell>
<TableCell style={styles.headingStyle}>
Price
</TableCell>
<TableCell style={styles.headingStyle}>
GST
</TableCell>
<TableCell style={styles.headingStyle}>
Subtotal
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{cartItem.length === 0 ? (
<TableRow>
<TableCell
colSpan={5}
align="center"
style={{ padding: "1rem" }}
>
<Typography variant="h6">
Add products for shopping
</Typography>
</TableCell>
</TableRow>
) : (
cartItem.map((row, index) => (
<TableRow
key={index}
style={{ padding: "0.5rem" }}
sx={{
"&:last-child td, &:last-child th": {
border: 0,
},
}}
>
<TableCell component="th" scope="row">
{/* {row.product} */}
<Box
sx={{
display: "flex",
width: "50%",
}}
>
<Grid
sx={{
width: "90px",
height: "60px",
mr: "0.5rem",
}}
>
<img
style={{
height: "100%",
width: "100%",
}}
src={
row.image && row.image[0].url
}
alt=""
/>
</Grid>
<Grid sx={{ width: "20%" }}>
<Typography
sx={{
fontFamily: "inter",
fontWeight: "600",
fontSize: "14px",
color: "#141718",
ml: "10px",
}}
>
{row.name}
</Typography>
<Box
onClick={() =>
removeCartItemHandler(row._id)
}
sx={{
color: "#6C7275",
width: "105%",
display: "flex",
alignItems: "center",
// justifyContent: "space-between",
cursor: "pointer",
ml: "10px",
// border: 'solid'
}}
>
<ClearIcon fontSize="small" />
<Typography
sx={{
fontFamily: "inter",
fontWeight: "600",
fontSize: "14px",
}}
>
Remove
</Typography>
</Box>
</Grid>
</Box>
</TableCell>
<TableCell>
<Box
sx={{
border: "1px solid #6C7275",
borderRadius: "4px",
height: "3%",
width: "60%",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<Typography
ml={1}
onClick={() =>
handleDecrease(index)
}
>
-
</Typography>
{row && row.quantity}
<Typography
mr={1}
onClick={() =>
handleIncrease(index)
}
>
+
</Typography>
</Box>
</TableCell>
<TableCell
sx={{
fontFamily: "inter",
fontWeight: "400",
fontSize: "18px",
color: "#121212",
}}
>
{row.price}
</TableCell>
<TableCell
sx={{
fontFamily: "inter",
fontWeight: "400",
fontSize: "18px",
color: "#121212",
}}
>
{row?.gst_amount}
</TableCell>
<TableCell
sx={{
fontFamily: "inter",
fontWeight: "600",
fontSize: "18px",
color: "#121212",
}}
>
{/* ${row.subtotal}${individualSubtotals[index]} */}
{row.total_amount}
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</TableContainer>
</Box>
</Grid>
</Grid>
</Box>
</div>
</div>
</div>
</div>
<div className="row" style={{ marginTop: "0.5rem" }}>
<div className="col-lg-12">
<div className="card">
<div
className="card-body"
style={{ backgroundColor: "#D3D3D3" }}
>
<div
style={{ display: "flex", justifyContent: "space-between" }}
>
{/* Left side content */}
<div>
{salesType === "" ? null : salesType ===
"inStoreDelivery" ? (
<div style={{ display: "flex", flexDirection: "row" }}>
{/* Display in-store delivery options */}
<Typography
style={{
fontWeight: "bold",
marginRight: "0.5rem",
}}
>
In-Store delivery:
</Typography>
<div style={{ marginRight: "0.5rem" }}>
<input
type="radio"
id="QRCode"
name="storedelivery"
value="QRCode"
checked={storedelivery === "QRCode"}
onChange={handlestoredeliveryChange}
className="mr-2"
/>
<label htmlFor="QRCode">QR Code</label>
</div>
<div style={{ marginRight: "5rem" }}>
<input
type="radio"
id="Cash"
name="storedelivery"
value="Cash"
checked={storedelivery === "Cash"}
onChange={handlestoredeliveryChange}
className="mr-2"
/>
<label htmlFor="Cash">Cash</label>
</div>
<Button
variant="contained"
color="primary"
style={{
fontWeight: "bold",
textTransform: "capitalize",
}}
>
Checkout
</Button>
</div>
) : (
<div>
{/* Display button for sending payment link */}
<Button variant="contained" color="primary">
Send Payment Link to Customer
</Button>
<Button
variant="contained"
color="primary"
style={{
fontWeight: "bold",
marginLeft: "5rem",
textTransform: "capitalize",
}}
>
Checkout
</Button>
</div>
)}
</div>
{/* Total Section */}
<div
style={{
display: "flex",
alignItems: "center",
marginRight: "2rem",
}}
>
<Typography
style={{
fontFamily: "inter",
fontWeight: "400",
fontSize: "16px",
marginRight: "0.5rem",
}}
>
Total:
</Typography>
<Typography
style={{
fontFamily: "inter",
fontWeight: "600",
fontSize: "16px",
}}
>
{total}
</Typography>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default Pos;