diff --git a/backend/controllers/Admin/AcustomerController.js b/backend/controllers/Admin/AcustomerController.js index 98b51c2a..a30bf995 100644 --- a/backend/controllers/Admin/AcustomerController.js +++ b/backend/controllers/Admin/AcustomerController.js @@ -11,6 +11,8 @@ export const getAllUsers = async (req, res) => { } } + + // Fetch single user by ID export const getUserById = async (req, res) => { try { @@ -24,6 +26,7 @@ export const getUserById = async (req, res) => { } } + // Update user by ID export const updateUserById = async (req, res) => { const { @@ -96,3 +99,12 @@ export const validateUserPassword = async (req, res) => { res.status(500).json({ message: 'Error validating password', error }) } } + +export const getUserCount = async (req, res) => { + try { + const count = await User.countDocuments() + res.status(200).json({ count }) + } catch (error) { + res.status(500).json({ error: 'Error fetching user count' }) + } +} \ No newline at end of file diff --git a/backend/controllers/Admin/Afarmer.js b/backend/controllers/Admin/Afarmer.js new file mode 100644 index 00000000..b30aff6c --- /dev/null +++ b/backend/controllers/Admin/Afarmer.js @@ -0,0 +1,20 @@ + +import Farmer from '../../models/farmerModel' +import mongoose from 'mongoose' +export const getFarmersCount = async (req, res) => { + try { + const count = await Farmer.countDocuments() + res.status(200).json({ count }) + } catch (error) { + res.status(500).json({ error: 'Error fetching staff count' }) + } +} + +export const getAllFarmers = async (req, res) => { + try { + const farmer = await Farmer.find({}) // Change sorting to firstName + res.status(200).json(farmer) + } catch (error) { + res.status(500).json({ message: error.message }) + } +} \ No newline at end of file diff --git a/backend/controllers/DLDriverController.js b/backend/controllers/DLDriverController.js index 9d2ed507..1461d761 100644 --- a/backend/controllers/DLDriverController.js +++ b/backend/controllers/DLDriverController.js @@ -378,3 +378,4 @@ export { verifyPassword, getAllDrivers, } + diff --git a/backend/controllers/couponController.js b/backend/controllers/couponController.js index 11e8efca..dc23fe85 100644 --- a/backend/controllers/couponController.js +++ b/backend/controllers/couponController.js @@ -90,3 +90,11 @@ export const deleteCoupon = async (req, res) => { res.status(500).json({ message: error.message }) } } +export const getCouponCount = async (req, res) => { + try { + const count = await Coupon.countDocuments() + res.status(200).json({ count }) + } catch (error) { + res.status(500).json({ error: 'Error fetching coupon count' }) + } +} diff --git a/backend/controllers/farmerController.js b/backend/controllers/farmerController.js index eaf92913..36dfa4d5 100644 --- a/backend/controllers/farmerController.js +++ b/backend/controllers/farmerController.js @@ -285,3 +285,20 @@ export { deleteFarmerAccount, getFarmerById, } +export const getAllFarmers = async (req, res) => { + try { + const farmer = await Farmer.find({}) // Change sorting to firstName + res.status(200).json(farmer) + } catch (error) { + res.status(500).json({ message: error.message }) + } +} + +export const getFarmersCount = async (req, res) => { + try { + const count = await Farmer.countDocuments() + res.status(200).json({ count }) + } catch (error) { + res.status(500).json({ error: 'Error fetching staff count' }) + } +} \ No newline at end of file diff --git a/backend/routes/Admin/AfarmerRoute.js b/backend/routes/Admin/AfarmerRoute.js new file mode 100644 index 00000000..0a08cb23 --- /dev/null +++ b/backend/routes/Admin/AfarmerRoute.js @@ -0,0 +1,12 @@ +import express from 'express' +import { + getFarmersCount, +getAllFarmers, +} from '../../controllers/Admin/Afarmer' + +const router = express.Router() + +router.get('/', getAllFarmers) +router.post('/count', getFarmersCount) + +export default router \ No newline at end of file diff --git a/backend/routes/Admin/AuserRoute.js b/backend/routes/Admin/AuserRoute.js index bafa6d31..bbcc635a 100644 --- a/backend/routes/Admin/AuserRoute.js +++ b/backend/routes/Admin/AuserRoute.js @@ -5,10 +5,13 @@ import { updateUserById, deleteUserById, validateUserPassword, + getUserCount } from '../../controllers/Admin/AcustomerController.js' const router = express.Router() +router.get('/count',getUserCount) + // Get all users router.get('/', getAllUsers) diff --git a/backend/routes/couponRouter.js b/backend/routes/couponRouter.js index 1303fd35..52f53b26 100644 --- a/backend/routes/couponRouter.js +++ b/backend/routes/couponRouter.js @@ -1,5 +1,6 @@ import express from 'express' import { + getCouponCount, createCoupon, validCoupon, getAllCoupons, @@ -10,6 +11,7 @@ import { const couponRouter = express.Router() +couponRouter.get('/count', getCouponCount) couponRouter.post('/valid-coupon', validCoupon) couponRouter.post('/', createCoupon) couponRouter.get('/', getAllCoupons) diff --git a/backend/routes/farmerRoute.js b/backend/routes/farmerRoute.js index 3aed03d8..860c3ade 100644 --- a/backend/routes/farmerRoute.js +++ b/backend/routes/farmerRoute.js @@ -7,6 +7,8 @@ import { logoutFarmer, deleteFarmerAccount, getFarmerById, + getAllFarmers, + getFarmersCount, } from '../controllers/farmerController.js' import { protect } from '../middlewares/farmerauthMiddleware.js' @@ -15,6 +17,9 @@ const router = express.Router() // Register a new farmer router.post('/register', registerFarmer) +router.get('/',getAllFarmers) +router.get('/count',getFarmersCount) + // Authenticate farmer and get token router.post('/login', authFarmer) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index fff3cad0..f44de65e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -50,6 +50,7 @@ import AddStaff from './Pages/Admin/AaddStaff' import UpdateStaff from './Pages/Admin/AupdateStaff' import AddCoupon from './Pages/Admin/AaddCoupons' import UpdateCoupon from './Pages/Admin/AupdateCoupon' +import AdminLayout from './Layouts/Alayout' import AdminLogin from './Components/Admin/admnlogins' // Farmer Pages @@ -174,7 +175,8 @@ const router = createBrowserRouter( {/* Admin Routes */} - + }> + } /> {/* Staff */} diff --git a/frontend/src/Components/Admin/Aheader.jsx b/frontend/src/Components/Admin/Aheader.jsx new file mode 100644 index 00000000..4f45e93b --- /dev/null +++ b/frontend/src/Components/Admin/Aheader.jsx @@ -0,0 +1,29 @@ +import { Link } from 'react-router-dom' +import logo from '../../assets/Logo.png' +const Header = () => { + return ( +
+
+
+ + Logo + +
+ +
+
+

Welcome to FarmCart🌱

+ + Help & Support + +
+
+
+
+ ) +} + +export default Header diff --git a/frontend/src/Components/Admin/AsideBar.jsx b/frontend/src/Components/Admin/AsideBar.jsx index c0e1021b..3d9d45cc 100644 --- a/frontend/src/Components/Admin/AsideBar.jsx +++ b/frontend/src/Components/Admin/AsideBar.jsx @@ -1,6 +1,6 @@ import React from 'react' import { Link } from 'react-router-dom' -import { FaHome, FaUsers, FaStore, FaCog, FaSignOutAlt } from 'react-icons/fa' +import { FaHome, FaUsers, FaUserAlt, FaTags, FaChartLine, FaSignOutAlt } from 'react-icons/fa' const AdminSidebar = () => { return ( @@ -13,7 +13,7 @@ const AdminSidebar = () => { className="flex items-center px-4 py-2 text-gray-700 hover:bg-green-500 hover:text-white rounded text-left" > - Home + Dashboard
  • @@ -30,17 +30,17 @@ const AdminSidebar = () => { to="/users" className="flex items-center px-4 py-2 text-gray-700 hover:bg-green-500 hover:text-white rounded text-left" > - + Users
  • - - Coupens + + Coupons
  • @@ -48,10 +48,19 @@ const AdminSidebar = () => { to="/finance" className="flex items-center px-4 py-2 text-gray-700 hover:bg-green-500 hover:text-white rounded text-left" > - + Finance
  • +
  • + + + Logout + +
  • diff --git a/frontend/src/Components/Admin/DailyOrdersChart.jsx b/frontend/src/Components/Admin/DailyOrdersChart.jsx index 1430ed7e..c2faf27f 100644 --- a/frontend/src/Components/Admin/DailyOrdersChart.jsx +++ b/frontend/src/Components/Admin/DailyOrdersChart.jsx @@ -102,27 +102,28 @@ const DailyOrdersChart = () => { } } } - - return ( -
    -

    - Daily Orders -

    - {chartData.labels ? ( - <> - - - - ) : ( -

    Loading chart...

    - )} -
    - ) -} - -export default DailyOrdersChart + }; + + return ( +
    +

    Daily Orders

    + {chartData.labels ? ( + <> + + + + + + ) : ( +

    Loading chart...

    + )} +
    + ); +}; + +export default DailyOrdersChart; diff --git a/frontend/src/Layouts/Alayout.jsx b/frontend/src/Layouts/Alayout.jsx new file mode 100644 index 00000000..fdd9553f --- /dev/null +++ b/frontend/src/Layouts/Alayout.jsx @@ -0,0 +1,21 @@ +import { Outlet } from 'react-router-dom' +import Footer from '../Components/Home/FooterDashboard' +import Header from '../Components/Admin/Aheader' + +function AdminLayout() { + return ( + <> +
    +
    + +
    + + +
    +
    +
    + + ) +} + +export default AdminLayout diff --git a/frontend/src/Pages/Admin/Acoupons.jsx b/frontend/src/Pages/Admin/Acoupons.jsx index 6df6dc84..a9d02e80 100644 --- a/frontend/src/Pages/Admin/Acoupons.jsx +++ b/frontend/src/Pages/Admin/Acoupons.jsx @@ -20,6 +20,10 @@ import { FaRegEye } from 'react-icons/fa' import { MdDeleteSweep } from 'react-icons/md' import Loading from '../../Components/Loading.jsx' import Swal from 'sweetalert2' +import jsPDF from 'jspdf' +import 'jspdf-autotable' +import logo from '../../assets/logo.PNG'; +import { FaDownload } from 'react-icons/fa'; const Acoupon = () => { const [coupons, setCoupons] = useState([]) @@ -97,6 +101,83 @@ const Acoupon = () => { } } + const generateCouponPDF = () => { + const doc = new jsPDF(); + const img = new Image(); + + // Check if the logo is defined correctly + img.src = logo; + + // Ensure the image loads before adding it to the PDF + img.onload = () => { + // Add the logo after it's loaded + doc.addImage(img, 'PNG', 20, 35, 30, 5); + + // Add title below the logo + doc.setFontSize(15); + doc.text('Coupon List', 105, 40, null, null, 'center'); + + // Prepare the table data + const tableColumn = ['Id', 'Coupon Code', 'Discount', 'Expiry Date']; + const tableRows = []; + + filteredCoupons.forEach((coupon, index) => { + const couponData = [ + index + 1, + coupon.couponCode, + `${coupon.discount}%`, // Assuming discount is a percentage + coupon.expiryDate.split('T')[0], // Assuming expiryDate is in ISO format + ]; + tableRows.push(couponData); + }); + + // Add table to PDF + doc.autoTable({ + head: [tableColumn], + body: tableRows, + startY: 50, // Positioning the table after the logo and title + styles: { + fontSize: 9, // Adjust the table font size as needed + } + }); + + // Save the generated PDF + doc.save('coupon-list.pdf'); + }; + + // If the logo image doesn't load or isn't available, you can skip adding it: + img.onerror = () => { + doc.setFontSize(15); + doc.text('Coupon List', 105, 40, null, null, 'center'); + + const tableColumn = ['Id', 'Coupon Code', 'Discount', 'Expiry Date']; + const tableRows = []; + + filteredCoupons.forEach((coupon, index) => { + const couponData = [ + index + 1, + coupon.couponCode, + `${coupon.discount}%`, + coupon.expiryDate.split('T')[0], + ]; + tableRows.push(couponData); + }); + + doc.autoTable({ + head: [tableColumn], + body: tableRows, + startY: 50, + styles: { + fontSize: 9, + } + }); + + doc.save('coupon-list.pdf'); + }; + }; + + + const handleAddNewCoupon = () => { navigate('/addcoupons') // Adjust to your route for adding coupons } @@ -121,25 +202,20 @@ const Acoupon = () => {
    -
    -

    - Coupons +
    +

    + COUPON

    -
    - setSearch(e.target.value)} - /> -
    + setSearch(e.target.value)} + className="rounded-full border-[#99DD05] border-2 focus:border-[#99DD05] focus:ring-[#99DD05] placeholder-green-500" // Add custom classes here + /> +

    { ))}
    +
    + + +
    + {/* Add modal for confirmation if required */}
    diff --git a/frontend/src/Pages/Admin/Adashboard.jsx b/frontend/src/Pages/Admin/Adashboard.jsx index 5be7eaec..e0b2a22f 100644 --- a/frontend/src/Pages/Admin/Adashboard.jsx +++ b/frontend/src/Pages/Admin/Adashboard.jsx @@ -3,13 +3,17 @@ import axios from '../../axios.jsx' import Sidebar from '../../Components/Admin/AsideBar.jsx' import Loading from '../../Components/Loading.jsx' import DailyOrdersChart from '../../Components/Admin/DailyOrdersChart.jsx' +import { FaUsers, FaUserTie, FaTags, FaTruck, FaStore, FaDollarSign } from 'react-icons/fa'; + const Dashboard = () => { const [customerCount, setCustomerCount] = useState(0) + const [farmercount, setFarmerCount] = useState(0) + const [driverCount, setDriverCount] = useState(0) const [staffCount, setStaffCount] = useState(0) const [totalRevenue, setTotalRevenue] = useState(0) // Assuming you have revenue data const [loading, setLoading] = useState(true) - + const [couponCount, setCouponCount] = useState(0) // Fetch dashboard data useEffect(() => { const fetchData = async () => { @@ -23,7 +27,18 @@ const Dashboard = () => { // Fetch staff count const staffResponse = await axios.get('/staff/count') setStaffCount(staffResponse.data.count) + //fetch coupon count + const couponResponse = await axios.get('/coupon/count') + setCouponCount(couponResponse.data.count) + + const customerResponse = await axios.get('/customers/count') + setCustomerCount(customerResponse.data.count) + const farmerResponse = await axios.get('/farmers/count') + setFarmerCount(farmerResponse.data.count) + + const driverResponse = await axios.get('/drivers/count') + setDriverCount(driverResponse.data.count) // Assuming there's an endpoint for total revenue (adjust or remove if unnecessary) const revenueResponse = await axios.get('/revenue/total') setTotalRevenue(revenueResponse.data.total) @@ -53,20 +68,74 @@ const Dashboard = () => {
    -
    -

    - Dashboard +
    +

    + DASHBOARD

    - {/* */} - - -
    + {/* Total Staff */} + + Total Staff + + } + count={staffCount} + /> + + {/* Total Coupon */} + + Total Coupon + + } + count={couponCount} + /> + + {/* Total Customers */} + + Total Customers + + } + count={customerCount} + /> + + {/* Total Drivers */} + + Total Drivers + + } + count={driverCount} + /> + + {/* Total Farmers */} + + Total Farmers + + } + count={farmercount} + /> + + {/* Total Revenue */} + + Total Revenue + + } + count={`$${totalRevenue.toFixed(2)}`} + /> +

    + {/* Additional dashboard content can be added here */} @@ -79,13 +148,14 @@ const Dashboard = () => { // Card component to display each metric const Card = ({ title, count }) => { return ( -
    +

    {title}

    {count}

    - ) + ); } + export default Dashboard diff --git a/frontend/src/Pages/Admin/Afinance.jsx b/frontend/src/Pages/Admin/Afinance.jsx index 6b111430..d0730228 100644 --- a/frontend/src/Pages/Admin/Afinance.jsx +++ b/frontend/src/Pages/Admin/Afinance.jsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from 'react' -import axios from '../../axios.jsx' -import Sidebar from '../../Components/Admin/AsideBar.jsx' // Make sure to update the path if necessary -import { useNavigate, Link } from 'react-router-dom' +import axios from '../../axios' +import Sidebar from '../../Components/Admin/AsideBar.jsx' +import { useNavigate } from 'react-router-dom' import { useDisclosure } from '@nextui-org/react' import { Input, @@ -13,11 +13,291 @@ import { TableHeader, TableRow, Tooltip, + Button, } from '@nextui-org/react' import { FaRegEye } from 'react-icons/fa' import { MdDeleteSweep } from 'react-icons/md' +import Loading from '../../Components/Loading' +import Swal from 'sweetalert2' +import jsPDF from 'jspdf' +import 'jspdf-autotable' +import logo from '../../assets/logo.PNG'; +import { FaDownload } from 'react-icons/fa'; + + const Finance = () => { - return
    finance
    + const [staffMembers, setStaffMembers] = useState([]) + const [loading, setLoading] = useState(true) + const [refetch, setRefetch] = useState(false) + const [page, setPage] = useState(1) + const [search, setSearch] = useState('') + + const rowsPerPage = 4 + const navigate = useNavigate() + + // Fetch staff members + useEffect(() => { + const fetchStaffMembers = async () => { + setLoading(true) + try { + const { data } = await axios.get('/staff') + setStaffMembers(data) + } catch (error) { + console.error('Error fetching staff members:', error) + } finally { + setLoading(false) + } + } + + fetchStaffMembers() + }, [refetch]) + + // Search functionality + const filteredStaff = useMemo(() => { + return staffMembers.filter((member) => + `${member.firstName} ${member.lastName}` + .toLowerCase() + .includes(search.toLowerCase()) + ) + }, [search, staffMembers]) + + // Pagination + const items = useMemo(() => { + const start = (page - 1) * rowsPerPage + const end = start + rowsPerPage + return filteredStaff.slice(start, end) + }, [page, filteredStaff]) + + const pages = Math.ceil(filteredStaff.length / rowsPerPage) + + const handleDeleteStaff = async (staffId) => { + const result = await Swal.fire({ + title: 'Are you sure?', + text: 'Do you really want to delete this staff member? This process cannot be undone.', + icon: 'warning', + showCancelButton: true, + confirmButtonText: 'Yes', + cancelButtonText: 'Cancel', + }) + + if (result.isConfirmed) { + try { + await axios.delete(`/staff/${staffId}`) + Swal.fire( + 'Deleted!', + 'Staff member has been deleted.', + 'success' + ) + setRefetch((prev) => !prev) + } catch (error) { + console.error('Error deleting staff:', error) + Swal.fire( + 'Error!', + 'There was an error deleting the staff member.', + 'error' + ) + } + } + } + + const handleAddNewStaff = () => { + navigate('/AddStaff') + } + + const handleEditStaff = (memberId) => { + navigate(`/updatestaff/${memberId}`) + } + + // PDF Generation + const generatePDF = () => { + const doc = new jsPDF() + const img = new Image(); + img.src = logo; + // Add logo + doc.addImage(img, 'PNG', 20, 35, 30, 5); // Adjust the X, Y, width, and height as needed // Adjust x, y, width, height as needed + + // Add title below the logo + doc.setFontSize(15) + doc.text('Staff List', 105, 40, null, null, 'center') // Centered below logo + + // Prepare the table data + const tableColumn = ['Id', 'Full Name','NIC','Email','Birth Day','Address','Role'] + const tableRows = [] + + filteredStaff.forEach((member, index) => { + const memberData = [ + index + 1, + `${member.firstName} ${member.lastName}`, + member.nic, + member.email, + member.birthday.split('T')[0], + `${member.Address.home} ${member.Address.street} ${member.Address.city}`, + member.role + + ] + tableRows.push(memberData) + }) + + // Add table to PDF + doc.autoTable({ + head: [tableColumn], + body: tableRows, + startY: 50, // Positioning the table after the logo and title + styles: { + fontSize: 9 // Adjust this value to make the table content smaller + } + }) + + doc.save('staff-list.pdf') + } + + if (loading) { + return ( +
    + +
    + ) + } + + + return ( +
    +
    + + +
    +
    +

    + STAFF +

    +
    + +
    + setSearch(e.target.value)} + className="rounded-full border-[#99DD05] border-2 focus:border-[#99DD05] focus:ring-[#99DD05] placeholder-green-500" // Add custom classes here + /> +
    + + + + setPage(page)} + /> + + } + > + + Id + Full Name + NIC + Email + Birthday + Address + Role + Action + + + + + {items.map((member, index) => ( + + {index + 1} + {`${member.firstName} ${member.lastName}`} + {member.nic} + {member.email} + {member.birthday.split('T')[0]} + {`${member.Address.home} ${member.Address.street} ${member.Address.city}`} + {member.role} + + + + { + // Open the modal for more details if implemented + }} + /> + + + + + + + + + + { + handleDeleteStaff( + member._id + ) + }} + /> + + + + + ))} + +
    +
    + + + +
    + + + +
    +
    +
    + ) } export default Finance + diff --git a/frontend/src/Pages/Admin/Astaff.jsx b/frontend/src/Pages/Admin/Astaff.jsx index 454399c2..29909e11 100644 --- a/frontend/src/Pages/Admin/Astaff.jsx +++ b/frontend/src/Pages/Admin/Astaff.jsx @@ -19,6 +19,12 @@ import { FaRegEye } from 'react-icons/fa' import { MdDeleteSweep } from 'react-icons/md' import Loading from '../../Components/Loading' import Swal from 'sweetalert2' +import jsPDF from 'jspdf' +import 'jspdf-autotable' +import logo from '../../assets/logo.PNG'; +import { FaDownload } from 'react-icons/fa'; +import Header from '../../Components/Admin/Aheader.jsx' + const Staff = () => { const [staffMembers, setStaffMembers] = useState([]) @@ -27,7 +33,7 @@ const Staff = () => { const [page, setPage] = useState(1) const [search, setSearch] = useState('') - const rowsPerPage = 5 + const rowsPerPage = 4 const navigate = useNavigate() // Fetch staff members @@ -103,6 +109,49 @@ const Staff = () => { navigate(`/updatestaff/${memberId}`) } + // PDF Generation + const generatePDF = () => { + const doc = new jsPDF() + const img = new Image(); + img.src = logo; + // Add logo + doc.addImage(img, 'PNG', 20, 35, 30, 5); // Adjust the X, Y, width, and height as needed // Adjust x, y, width, height as needed + + // Add title below the logo + doc.setFontSize(15) + doc.text('Staff List', 105, 40, null, null, 'center') // Centered below logo + + // Prepare the table data + const tableColumn = ['Id', 'Full Name','NIC','Email','Birth Day','Address','Role'] + const tableRows = [] + + filteredStaff.forEach((member, index) => { + const memberData = [ + index + 1, + `${member.firstName} ${member.lastName}`, + member.nic, + member.email, + member.birthday.split('T')[0], + `${member.Address.home} ${member.Address.street} ${member.Address.city}`, + member.role + + ] + tableRows.push(memberData) + }) + + // Add table to PDF + doc.autoTable({ + head: [tableColumn], + body: tableRows, + startY: 50, // Positioning the table after the logo and title + styles: { + fontSize: 9 // Adjust this value to make the table content smaller + } + }) + + doc.save('staff-list.pdf') + } + if (loading) { return (
    @@ -111,6 +160,7 @@ const Staff = () => { ) } + return (
    @@ -119,25 +169,22 @@ const Staff = () => {
    -
    -

    - Staff Members -

    - -
    -
    - setSearch(e.target.value)} - /> -
    +
    +

    + STAFF +

    +
    + +
    + setSearch(e.target.value)} + className="rounded-full border-[#99DD05] border-2 focus:border-[#99DD05] focus:ring-[#99DD05] placeholder-green-500" // Add custom classes here + /> +
    + { Id Full Name + NIC Email + Birthday + Address Role Action + + {items.map((member, index) => ( @@ -170,7 +222,10 @@ const Staff = () => { > {index + 1} {`${member.firstName} ${member.lastName}`} + {member.nic} {member.email} + {member.birthday.split('T')[0]} + {`${member.Address.home} ${member.Address.street} ${member.Address.city}`} {member.role} { ))}
    +
    + + + +
    + + - {/* Add modal for confirmation if required */}
    diff --git a/frontend/src/Pages/Admin/Ausers.jsx b/frontend/src/Pages/Admin/Ausers.jsx index 9eb10448..d7f8627e 100644 --- a/frontend/src/Pages/Admin/Ausers.jsx +++ b/frontend/src/Pages/Admin/Ausers.jsx @@ -1,6 +1,8 @@ import { useEffect, useState, useMemo } from 'react' import axios from '../../axios' import Sidebar from '../../Components/Admin/AsideBar' +import { FaRegEye } from 'react-icons/fa' +import { MdDeleteSweep } from 'react-icons/md' import { useNavigate } from 'react-router-dom' import { Input, @@ -16,6 +18,11 @@ import { } from '@nextui-org/react' import Swal from 'sweetalert2' +import jsPDF from 'jspdf' +import 'jspdf-autotable' +import logo from '../../assets/logo.PNG'; +import { FaDownload } from 'react-icons/fa'; + const CustomerList = () => { const [customers, setCustomers] = useState([]) const [loading, setLoading] = useState(true) @@ -43,8 +50,10 @@ const CustomerList = () => { // Search functionality const filteredCustomers = useMemo(() => { - return customers.filter( - (customer) => customer.firstname || customer.lastname + return customers.filter((customer) => + `${customer.firstname} ${customer.lastname}` + .toLowerCase() + .includes(search.toLowerCase()) ) }, [search, customers]) @@ -87,68 +96,155 @@ const CustomerList = () => { navigate(`/updateCustomer/${customerId}`) } + const generateCustomerPDF = () => { + const doc = new jsPDF(); + const img = new Image(); + img.src = logo; // Assuming you have the logo stored or imported as "logo" + + // Add logo to PDF + doc.addImage(img, 'PNG', 20, 35, 30, 5); // Adjust position (x, y, width, height) as needed + + // Add title below the logo + doc.setFontSize(15); + doc.text('Customer List', 105, 40, null, null, 'center'); // Centered below logo + + // Prepare the table data + const tableColumn = ['Id', 'First Name', 'Last Name', 'Email', 'Membership', 'Contact Number']; + const tableRows = []; + + // Loop through filtered customer data and format it for the table + filteredCustomers.forEach((customer, index) => { + const customerData = [ + index + 1, + customer.firstname, + customer.lastname, + customer.email, + customer.membershipType, // Assuming membershipType exists in customer data + customer.contactNumber // Assuming contactNumber exists in customer data + ]; + tableRows.push(customerData); + }); + + // Add table to PDF + doc.autoTable({ + head: [tableColumn], + body: tableRows, + startY: 50, // Positioning the table after the logo and title + styles: { + fontSize: 9 // Adjust this value to make the table content smaller + } + }); + + // Save the PDF + doc.save('customer-list.pdf'); + }; + + if (loading) { - return
    Loading...
    + return ( +
    + Loading... +
    + ) } return ( -
    - -
    -
    -

    Customers

    - setSearch(e.target.value)} - /> -
    - - - Id - First Name - Last Name - Email - Action - - - {items.map((customer, index) => ( - - {index + 1} - {customer.firstname} - {customer.lastname} - {customer.email} - - - - - - ))} - -
    - setPage(page)} - /> -
    +
    +
    + + +
    +
    +

    + CUSTOMERS +

    +
    + +
    + setSearch(e.target.value)} + className="rounded-full border-[#99DD05] border-2 focus:border-[#99DD05] focus:ring-[#99DD05] placeholder-green-500" + /> +
    + + + setPage(page)} + /> + + } + > + + Id + First Name + Last Name + Email + Action + + + {items.map((customer, index) => ( + + {index + 1} + {customer.firstname} + {customer.lastname} + {customer.email} + + + + + + { + handleDeleteCustomer( + member._id + ) + }} + /> + + + + + ))} + +
    + +
    + + +
    +
    +
    ) }