diff --git a/src/_nav.js b/src/_nav.js index 42588c3..f0e52e1 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -16,6 +16,7 @@ import { cilShare, cilSpeedometer, cilStar, + cilStorage, } from '@coreui/icons' import { CNavGroup, CNavItem, CNavTitle } from '@coreui/react' @@ -102,181 +103,17 @@ const _nav = [ ], }, // { - // component: CNavGroup, - // name: 'Buttons', - // to: '/buttons', - // icon: , - // items: [ - // { - // component: CNavItem, - // name: 'Buttons', - // to: '/buttons/buttons', - // }, - // { - // component: CNavItem, - // name: 'Buttons groups', - // to: '/buttons/button-groups', - // }, - // { - // component: CNavItem, - // name: 'Dropdowns', - // to: '/buttons/dropdowns', - // }, - // ], - // }, - // { - // component: CNavGroup, - // name: 'Forms', - // icon: , - // items: [ - // { - // component: CNavItem, - // name: 'Form Control', - // to: '/forms/form-control', - // }, - // { - // component: CNavItem, - // name: 'Select', - // to: '/forms/select', - // }, - // { - // component: CNavItem, - // name: 'Checks & Radios', - // to: '/forms/checks-radios', - // }, - // { - // component: CNavItem, - // name: 'Range', - // to: '/forms/range', - // }, - // { - // component: CNavItem, - // name: 'Input Group', - // to: '/forms/input-group', - // }, - // { - // component: CNavItem, - // name: 'Floating Labels', - // to: '/forms/floating-labels', - // }, - // { - // component: CNavItem, - // name: 'Layout', - // to: '/forms/layout', - // }, - // { - // component: CNavItem, - // name: 'Validation', - // to: '/forms/validation', - // }, - // ], - // }, - // { // component: CNavItem, - // name: 'Charts', - // to: '/charts', - // icon: , - // }, - // { - // component: CNavGroup, - // name: 'Icons', - // icon: , - // items: [ - // { - // component: CNavItem, - // name: 'CoreUI Free', - // to: '/icons/coreui-icons', - // badge: { - // color: 'success', - // text: 'NEW', - // }, - // }, - // { - // component: CNavItem, - // name: 'CoreUI Flags', - // to: '/icons/flags', - // }, - // { - // component: CNavItem, - // name: 'CoreUI Brands', - // to: '/icons/brands', - // }, - // ], - // }, - // { - // component: CNavGroup, - // name: 'Notifications', - // icon: , - // items: [ - // { - // component: CNavItem, - // name: 'Alerts', - // to: '/notifications/alerts', - // }, - // { - // component: CNavItem, - // name: 'Badges', - // to: '/notifications/badges', - // }, - // { - // component: CNavItem, - // name: 'Modal', - // to: '/notifications/modals', - // }, - // { - // component: CNavItem, - // name: 'Toasts', - // to: '/notifications/toasts', - // }, - // ], - // }, - // { - // component: CNavItem, - // name: 'Widgets', - // to: '/widgets', - // icon: , - // badge: { - // color: 'info', - // text: 'NEW', - // }, - // }, - // { - // component: CNavTitle, - // name: 'Extras', - // }, - // { - // component: CNavGroup, - // name: 'Pages', - // icon: , - // items: [ - // { - // component: CNavItem, - // name: 'Login', - // to: '/login', - // }, - // { - // component: CNavItem, - // name: 'Register', - // to: '/register', - // }, - // { - // component: CNavItem, - // name: 'Error 404', - // to: '/404', - // }, - // { - // component: CNavItem, - // name: 'Error 500', - // to: '/500', - // }, - // ], - // }, - // { - // component: CNavItem, - // name: 'Docs', - // href: 'https://coreui.io/react/docs/templates/installation/', - // icon: , + // name: 'Stock', + // to: '/stock', + // icon: , // }, + { + component: CNavItem, + name: 'Announcements', + to: '/announcemnets', + icon: , + }, ] export default _nav diff --git a/src/routes.js b/src/routes.js index 072d38b..e507d61 100644 --- a/src/routes.js +++ b/src/routes.js @@ -14,6 +14,8 @@ import ProcessingInvoices from './views/pages/rdOrders/processingInvoices' import ViewInvoices from './views/pages/rdOrders/invoiceView' import DispatchedInvoices from './views/pages/rdOrders/dispatchedInvoices' import DeliveredInvoices from './views/pages/rdOrders/deliveredInvoices' +import StockTable from './views/pages/stock/stockTable' +import Announcements from './views/pages/announcements/announcements' const Dashboard = React.lazy(() => import('./views/dashboard/Dashboard')) const Shop = React.lazy(() => import('./views/shops/Shop')) @@ -50,6 +52,8 @@ const routes = [ { path: '/change-password', name: 'Change password', element: ChangePassword }, { path: '/cart', name: 'Cart', element: Cart }, + { path: '/stock', name: 'Stock', element: StockTable }, + { path: '/announcemnets', name: 'Announcement', element: Announcements }, ] export default routes diff --git a/src/views/dashboard/Dashboard.js b/src/views/dashboard/Dashboard.js index 504c331..2f8872a 100644 --- a/src/views/dashboard/Dashboard.js +++ b/src/views/dashboard/Dashboard.js @@ -42,6 +42,7 @@ const Dashboard = () => { cancelled: '#f44336', // Red processing: '#ff9800', // Orange delivered: '#9c27b0', // Purple + pending: '#56F000 ', } const statusIcons = { new: , diff --git a/src/views/pages/announcements/announcements.js b/src/views/pages/announcements/announcements.js new file mode 100644 index 0000000..f80068c --- /dev/null +++ b/src/views/pages/announcements/announcements.js @@ -0,0 +1,151 @@ +import React, { useState, useEffect } from 'react' +import { + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TablePagination, + Paper, + Button, + Typography, + Skeleton, + Box, +} from '@mui/material' +import axios from 'axios' +// import { Box } from '@material-ui/core' +import { useNavigate } from 'react-router-dom' +import { isAutheticated } from '../../../auth' +import Axios from '../../../axios' + +const Announcements = () => { + const [announcements, setAnnouncements] = useState([]) + const [loading, setLoading] = useState(true) + const [page, setPage] = useState(0) + const [rowsPerPage, setRowsPerPage] = useState(5) + const [totalAnnouncements, setTotalAnnouncements] = useState(0) + const token = isAutheticated() + + const fetchAnnouncements = async (page, rowsPerPage) => { + setLoading(true) + try { + const response = await Axios.get('/api/announcement/PDs', { + params: { + page: page + 1, // backend uses 1-based index + rowsPerPage, + }, + headers: { + Authorization: `Bearer ${token}`, // if token is necessary for authorization + }, + }) + console.log(response, 'this is the response ') + const { announcements, totalAnnouncements } = response.data + setAnnouncements(announcements) + setTotalAnnouncements(totalAnnouncements) + } catch (error) { + console.error('Error fetching announcements', error) + } finally { + setLoading(false) + } + } + + useEffect(() => { + fetchAnnouncements(page, rowsPerPage) + }, [page, rowsPerPage]) + + const handleChangePage = (event, newPage) => { + setPage(newPage) + } + + const handleChangeRowsPerPage = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)) + setPage(0) // Reset page to 0 when changing rows per page + } + + const formatAMPM = (dateString) => { + const date = new Date(dateString) + let hours = date.getHours() + let minutes = date.getMinutes() + const ampm = hours >= 12 ? 'PM' : 'AM' + hours = hours % 12 || 12 + minutes = minutes < 10 ? '0' + minutes : minutes + return `${hours}:${minutes} ${ampm}` + } + const navigate = useNavigate() + + return ( + + + + Announcements + + + + + + + + ID + Time + {/* Sent to */} + Message + {/* Action */} + + + + {loading ? ( + Array.from(new Array(rowsPerPage)).map((_, index) => ( + + + + + + )) + ) : announcements.length > 0 ? ( + announcements.map((announcement) => ( + + {announcement.uniqueId} + + {new Date(announcement.createdAt).toDateString()} + , {formatAMPM(announcement.createdAt)} + + {/* {announcement.sentTo.join(', ')} */} + {announcement.message} + {/* + + */} + + )) + ) : ( + + + Data not found + + + )} + +
+ +
+
+ ) +} + +export default Announcements diff --git a/src/views/pages/cart/addressAndPayment.js b/src/views/pages/cart/addressAndPayment.js index 3d69674..af3e2af 100644 --- a/src/views/pages/cart/addressAndPayment.js +++ b/src/views/pages/cart/addressAndPayment.js @@ -13,6 +13,8 @@ import { FormHelperText, } from '@mui/material' import React, { useState } from 'react' +import Axios from '../../../axios' +import { isAutheticated } from '../../../auth' const AddressAndPayment = ({ billTo, @@ -22,9 +24,11 @@ const AddressAndPayment = ({ paymentMode, setPaymentMode, handleTabChange, + address, }) => { const [billToError, setBillToError] = useState(false) const [shipToError, setShipToError] = useState(false) + const [paymentModeError, setPaymentModeError] = useState(false) const handleReviewOrderClick = (e) => { @@ -59,6 +63,10 @@ const AddressAndPayment = ({ handleTabChange(e, 2) } } + const formatAddress = (address) => { + const { street, city, state, postalCode } = address + return `${street}, ${city}, ${state} - ${postalCode}` + } return (
@@ -79,15 +87,12 @@ const AddressAndPayment = ({ label="Bill Address" onChange={(e) => setBillTo(e.target.value)} > - - 123, MG Road, Bengaluru, Karnataka - 560001 - - - 456, Park Street, Kolkata, West Bengal - 700016 - - - 789, Connaught Place, New Delhi - 110001 - + {address && + address.map((address) => ( + + {formatAddress(address)} + + ))} {billToError && Bill Address is required} @@ -103,16 +108,14 @@ const AddressAndPayment = ({ label="Ship Address" onChange={(e) => setShipTo(e.target.value)} > - - 123, MG Road, Bengaluru, Karnataka - 560001 - - - 456, Park Street, Kolkata, West Bengal - 700016 - - - 789, Connaught Place, New Delhi - 110001 - + {address && + address.map((address) => ( + + {formatAddress(address)} + + ))} + {shipToError && Ship Address is required} diff --git a/src/views/pages/cart/cart.js b/src/views/pages/cart/cart.js index 8723c1f..a967de0 100644 --- a/src/views/pages/cart/cart.js +++ b/src/views/pages/cart/cart.js @@ -20,6 +20,8 @@ import { selectCartItems, selectCartSubtotal, } from '../../../redux-store/CartStore/ducs' +import { isAutheticated } from '../../../auth' +import Axios from '../../../axios' const TabItem = ({ label, active, complete, onClick, reference, stepNumber }) => ( { const dispatch = useDispatch() const [paymentMode, setPaymentMode] = useState('') const cartItems = useSelector(selectCartItems) - const totalItemCount = useSelector(selectCartItemCount) - const cartSubtotal = useSelector(selectCartSubtotal) + + const [address, setAddress] = useState([]) + const token = isAutheticated() const [value, setValue] = useState(0) + + const getAddress = async () => { + try { + const res = await Axios.get('/api/shipping/address/user/address/', { + headers: { + 'Access-Control-Allow-Origin': '*', + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }) + console.log(res) + setAddress(res?.data?.UserShippingAddress) + } catch (error) { + console.log(error) + } + } + useEffect(() => { + getAddress() + }, []) const handleTabChange = (event, newValue) => { if (value === 3 && newValue !== 3) { setPaymentMode('') @@ -216,6 +238,7 @@ const Cart = () => { paymentMode={paymentMode} setPaymentMode={setPaymentMode} handleTabChange={handleTabChange} + address={address} /> )} diff --git a/src/views/pages/rdOrders/partialOrderModal.js b/src/views/pages/rdOrders/partialOrderModal.js index fc7e6c5..013eb24 100644 --- a/src/views/pages/rdOrders/partialOrderModal.js +++ b/src/views/pages/rdOrders/partialOrderModal.js @@ -63,43 +63,45 @@ const OrderDetailsDialog = ({ open, onClose, order, onSubmit }) => { {order?.orderItem.map((item, index) => { - const subtotal = item.price * item.remainingQuantity - const gstAmount = ((item.GST * item.price) / 100) * item.remainingQuantity - const totalWithGST = subtotal + gstAmount + if (item.remainingQuantity > 0) { + const subtotal = item.price * item.remainingQuantity + const gstAmount = ((item.GST * item.price) / 100) * item.remainingQuantity + const totalWithGST = subtotal + gstAmount - return ( - - - {item.productId.name} - {item.productId.name} - - ₹{item.price} - {item.remainingQuantity} - - - handleAvailabilityChange(index, Number(e.target.value)) - } - inputProps={{ - min: 0, - max: item.remainingQuantity, - }} - style={{ width: '60px', textAlign: 'center' }} - /> - - ₹{subtotal} - {item.GST}% - ₹{gstAmount} - ₹{totalWithGST} - - ) + return ( + + + {item.productId.name} + {item.productId.name} + + ₹{item.price} + {item.remainingQuantity} + + + handleAvailabilityChange(index, Number(e.target.value)) + } + inputProps={{ + min: 0, + max: item.remainingQuantity, + }} + style={{ width: '60px', textAlign: 'center' }} + /> + + ₹{subtotal} + {item.GST}% + ₹{gstAmount} + ₹{totalWithGST} + + ) + } })} diff --git a/src/views/pages/rdOrders/viewOrders.js b/src/views/pages/rdOrders/viewOrders.js index 7a83a74..a52a85e 100644 --- a/src/views/pages/rdOrders/viewOrders.js +++ b/src/views/pages/rdOrders/viewOrders.js @@ -119,13 +119,13 @@ const ViewOrders = () => { } } else if (orderStatus === 'processing') { const processingOrderInvoice = order?.orderItem - .filter((item) => item.remainingQuantity > 0) // Only include items with remainingQuantity > 0 + .filter((item) => item.remainingQuantity > 0) .map((item) => ({ ...item, productId: item.productId, - processquantity: item.remainingQuantity, // Add processquantity only for items with remainingQuantity > 0 + processquantity: item.remainingQuantity, })) - console.log(processingOrderInvoice) + console.log(processingOrderInvoice, 'process invoices', order._id) const cancellationRes = await Axios.post( `/api/pd-process-order`, diff --git a/src/views/pages/stock/stockTable.js b/src/views/pages/stock/stockTable.js new file mode 100644 index 0000000..aa0799f --- /dev/null +++ b/src/views/pages/stock/stockTable.js @@ -0,0 +1,267 @@ +import React, { useState, useEffect } from 'react' +import { + TableContainer, + Table, + TableHead, + TableRow, + TableCell, + TableBody, + Paper, + Typography, + TablePagination, + Skeleton, + TextField, + MenuItem, + Select, + FormControl, + InputLabel, + Grid, + Button, +} from '@mui/material' +import axios from 'axios' +import Axios from '../../../axios' + +function StockTable({ apiEndpoint, totalProducts }) { + const [stocks, setStocks] = useState([]) + const [loading, setLoading] = useState(true) + const [page, setPage] = useState(0) + const [rowsPerPage, setRowsPerPage] = useState(5) + const [category, setCategory] = useState('') + const [brand, setBrand] = useState('') + const [productName, setProductName] = useState('') + const [isInitialStockMode, setIsInitialStockMode] = useState(false) + const [initialStock, setInitialStock] = useState([]) + const [products, setProducts] = useState() + + console.log(initialStock, 'initial stock ') + + const fetchProducts = async () => { + try { + const response = await Axios.get('/api/product/getAll/user/') + setProducts(response.data?.products || []) + console.log(response, 'resp of products ') + } catch (error) { + console.log(error) + } + const response = await Axios.get('/api/product/getAll/user/') + setProducts(response.data?.products || []) + } + // Fetch Products from Backend API + const fetchStocks = async () => { + setLoading(true) + try { + const response = await axios.get(apiEndpoint, { + params: { category, brand, productName, page, limit: rowsPerPage }, + }) + setStocks(response.data.stocks) + } catch (error) { + console.error('Error fetching products:', error) + } finally { + setLoading(false) + } + } + useEffect(() => { + fetchProducts() + }, []) + useEffect(() => { + fetchStocks() + }, [category, brand, productName, page, rowsPerPage]) + + // Pagination Handlers + const handleChangePage = (event, newPage) => setPage(newPage) + const handleChangeRowsPerPage = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)) + setPage(0) + } + + const handleStockChange = (productId, value) => { + setInitialStock((prevStock) => { + const stockExists = prevStock.find((item) => item.productId === productId) + + if (stockExists) { + // Update the existing stock entry + return prevStock.map((item) => + item.productId === productId ? { ...item, stock: value } : item, + ) + } else { + // Add a new stock entry + return [...prevStock, { productId, stock: value }] + } + }) + } + + const handleSubmitInitialStock = async () => { + try { + await axios.post(`${apiEndpoint}/initial-stock`, { stock: initialStock }) + setIsInitialStockMode(false) + fetchProducts() + } catch (error) { + console.error('Error adding initial stock:', error) + } + } + + const formatDateTime = (dateString) => { + const date = new Date(dateString) + return `${date.toDateString()}, ${formatAMPM(date)}` + } + + const formatAMPM = (date) => { + let hours = date.getHours() + const minutes = date.getMinutes() + const ampm = hours >= 12 ? 'PM' : 'AM' + hours = hours % 12 || 12 + return `${hours}:${minutes < 10 ? '0' + minutes : minutes} ${ampm}` + } + + // Check if stock exists + const stockExists = stocks.length > 0 + + return ( +
+ {!stockExists && !isInitialStockMode ? ( + + ) : isInitialStockMode ? ( + <> + + Add Initial Stock + + + + + + Product Name + SKU + Stock + + + + {products.map((product) => ( + + {product.name} + {product.SKU} + + item.productId === product._id)?.stock || '' + } + onChange={(e) => handleStockChange(product._id, e.target.value)} + /> + + + ))} + +
+
+ + + ) : ( + <> + {/* Filter Controls */} + + + + Category + + + + + + Brand + + + + + setProductName(e.target.value)} + /> + + + + {/* Products Table */} + + + + + Product Name + SKU + Category + Brand + Added On + Price + Stock + + + + {loading ? ( + Array.from(new Array(rowsPerPage)).map((_, index) => ( + + + + + + )) + ) : stocks.length > 0 ? ( + products.map((product) => ( + + {product.name} + {product.sku} + {product.categoryName} + {product.brandName} + {formatDateTime(product.addedOn)} + {product.price.toFixed(2)} + {product.stock} + + )) + ) : ( + + + Data not found + + + )} + +
+ +
+ + )} +
+ ) +} + +export default StockTable