diff --git a/backend/controllers/orderController.js b/backend/controllers/orderController.js
index ba9d6df6..f06373dc 100644
--- a/backend/controllers/orderController.js
+++ b/backend/controllers/orderController.js
@@ -248,3 +248,53 @@ export const getTotalSales = async (req, res) => {
return res.status(500).json({ message: 'Error fetching total sales' })
}
}
+
+
+export const getShopTotalIncome = async (req, res) => {
+ try {
+ const totalIncome = await Order.aggregate([
+ {
+ $group: {
+ _id: '$farmer.shopId', // Group by shop ID
+ totalIncome: { $sum: '$totalPrice' }, // Sum totalPrice for each shop
+ },
+ },
+ {
+ $lookup: {
+ from: 'shops', // Name of your shop collection
+ localField: '_id', // The shop ID from the previous group stage
+ foreignField: '_id', // The shop ID in the shops collection
+ as: 'shopDetails', // Output array field for joined shop details
+ },
+ },
+ {
+ $unwind: '$shopDetails', // Deconstruct the shopDetails array
+ },
+ {
+ $lookup: {
+ from: 'farmers', // Assuming the farmers collection holds owner information
+ localField: 'shopDetails.farmer', // Reference to farmer ID in the shopDetails
+ foreignField: '_id', // The farmer ID in the farmers collection
+ as: 'farmerDetails', // Output array field for joined farmer details
+ },
+ },
+ {
+ $unwind: '$farmerDetails', // Deconstruct the farmerDetails array
+ },
+ {
+ $project: {
+ _id: 0, // Exclude the default _id field
+ shopId: '$_id', // Include the shop ID
+ shopName: '$shopDetails.name', // Include the shop name
+ totalIncome: 1, // Include total income
+ ownerName: '$farmerDetails.name', // Include the owner's name
+ },
+ },
+ ]);
+
+ res.status(200).json(totalIncome); // Send the total income response
+ } catch (error) {
+ console.error('Error fetching shop total income with owner:', error);
+ res.status(500).json({ message: 'Error fetching total income for shops with owner' });
+ }
+}
diff --git a/backend/routes/orderRoute.js b/backend/routes/orderRoute.js
index 81e5a321..7f489ef7 100644
--- a/backend/routes/orderRoute.js
+++ b/backend/routes/orderRoute.js
@@ -10,6 +10,7 @@ import {
getOrderById,
getDailyOrders,
getTotalSales,
+ getShopTotalIncome,
} from '../controllers/orderController.js'
const orderRouter = express.Router()
@@ -22,6 +23,7 @@ orderRouter.get('/get-user-orders/:id', getOrdersByUserId)
orderRouter.put('/:id', updateOrderStatus)
orderRouter.delete('/:id', DeleteOrder)
orderRouter.get('/get-shop/:id', getShopByFarmerId)
+orderRouter.get('/shop-total-income', getShopTotalIncome);
orderRouter.get('/:id', getOrderById) // Fetch order details by ID with populated farmer and user details
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 208dfffb..042f9471 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -110,6 +110,7 @@ import HelpLayout from './Layouts/HelpLayout'
import Help from './Pages/Help/Help'
import SupportTicket from './Pages/Help/SupportTicket'
import Feedback from './Pages/Help/Feedback'
+import ManageShopIncome from './Pages/Admin/ManageShopIncome'
// Define all routes in a single Router
const router = createBrowserRouter(
@@ -218,6 +219,7 @@ const router = createBrowserRouter(
/>
} />
} />
+ } />
{/*
diff --git a/frontend/src/Pages/Admin/Afinance.jsx b/frontend/src/Pages/Admin/Afinance.jsx
index 7f4a57c4..74b0b45f 100644
--- a/frontend/src/Pages/Admin/Afinance.jsx
+++ b/frontend/src/Pages/Admin/Afinance.jsx
@@ -1,302 +1,168 @@
-import { useEffect, useMemo, useState } from 'react'
-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,
- Pagination,
- Table,
- TableBody,
- TableCell,
- TableColumn,
- 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'
+import React, { useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import Sidebar from '../../Components/Admin/AsideBar.jsx';
+import { Button } from '@nextui-org/react';
+import axios from 'axios';
+import jsPDF from 'jspdf';
+import 'jspdf-autotable';
const 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)
- }
+ const navigate = useNavigate();
+ const [incomeData, setIncomeData] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState('');
+ const [searchTerm, setSearchTerm] = useState('');
+ const [sortOrder, setSortOrder] = useState('asc'); // 'asc' for ascending, 'desc' for descending
+
+ // Function to fetch shop income data
+ const fetchShopIncomeData = async () => {
+ try {
+ const response = await axios.get('/api/orders/shop-total-income');
+ setIncomeData(response.data);
+ } catch (err) {
+ setError(err.message);
+ } 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
+ useEffect(() => {
+ fetchShopIncomeData();
+ }, []);
+
+ // Handle search input change
+ const handleSearchChange = (e) => {
+ setSearchTerm(e.target.value);
+ };
+
+ // Sort the income data based on total income
+ const handleSort = () => {
+ const sortedData = [...incomeData].sort((a, b) => {
+ return sortOrder === 'asc'
+ ? a.totalIncome - b.totalIncome
+ : b.totalIncome - a.totalIncome;
+ });
+ setIncomeData(sortedData);
+ setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
+ };
+
+ // Generate PDF report
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
+ const doc = new jsPDF();
+ const title = 'Shop Income Report';
+
+ // Set font size and style for title
+ doc.setFontSize(16);
+ doc.text(title, 14, 16);
+
+ // Set font size for table headers
+ doc.setFontSize(12);
+
+ // Create the table
doc.autoTable({
- head: [tableColumn],
- body: tableRows,
- startY: 50, // Positioning the table after the logo and title
+ head: [['Shop Name', 'Owner Name', 'Total Income']],
+ body: incomeData
+ .filter(shop => shop.shopName.toLowerCase().includes(searchTerm.toLowerCase()))
+ .map(shop => [
+ shop.shopName,
+ shop.ownerName,
+ `Rs. ${shop.totalIncome.toFixed(2)}`
+ ]),
+ margin: { top: 24 }, // Margin from the top
styles: {
- fontSize: 9, // Adjust this value to make the table content smaller
+ fontSize: 10,
+ cellPadding: 5,
+ halign: 'left', // Align text to the right in cells
+ valign: 'middle' // Vertically center the text
},
- })
-
- doc.save('staff-list.pdf')
- }
-
- if (loading) {
- return (
-
-
-
- )
- }
+ headStyles: {
+ fillColor: [22, 160, 133], // Header background color
+ textColor: [255, 255, 255], // Header text color
+ fontStyle: 'bold' // Bold font style for headers
+ },
+ theme: 'striped', // Use striped theme for better readability
+ });
+
+ doc.save('shop_monthly_payment_report.pdf');
+ };
+
+ // Filter data based on search term
+ const filteredData = incomeData.filter(shop =>
+ shop.shopName.toLowerCase().includes(searchTerm.toLowerCase())
+ );
return (
-
+
-