From 718365ef5dca4b7bad4ee8c376c3fec87e1e86a0 Mon Sep 17 00:00:00 2001 From: lakshan sanjeewa Date: Mon, 14 Oct 2024 19:03:57 +0530 Subject: [PATCH] Fixed validations (#126) --------- Co-authored-by: NIMENDRA --- .gitignore | 3 +- backend/controllers/DLDeliveryController.js | 14 +- backend/controllers/DLOcontroller.js | 28 +- backend/controllers/orderController.js | 11 +- backend/models/DLDeliveryModel.js | 22 + backend/models/DLOModel.js | 30 +- backend/routes/orderRoute.js | 2 +- backend/server.js | 1 + frontend/src/App.jsx | 5 +- frontend/src/Components/Admin/admnlogins.jsx | 2 +- frontend/src/Components/Home/DHeader.jsx | 19 - frontend/src/Components/delivery/DHeader.jsx | 31 + frontend/src/Layouts/BlogLayout.jsx | 1 - frontend/src/Layouts/DLayout.jsx | 2 +- frontend/src/Layouts/HelpLayout.jsx | 25 +- frontend/src/Pages/Admin/Afinance.jsx | 151 ++-- frontend/src/Pages/Admin/ManageShopIncome.jsx | 187 +++-- frontend/src/Pages/blog manage/UserBlog.jsx | 10 +- .../delivery/DLDriverRegistrationForm.jsx | 688 +++++++++--------- frontend/src/Pages/delivery/DLLogin.jsx | 2 +- .../src/Pages/delivery/DLviewDelivery.jsx | 127 +++- .../Pages/delivery/driver/ViewDelivery.jsx | 186 +++-- 22 files changed, 959 insertions(+), 588 deletions(-) delete mode 100644 frontend/src/Components/Home/DHeader.jsx create mode 100644 frontend/src/Components/delivery/DHeader.jsx diff --git a/.gitignore b/.gitignore index cb21b410..14355789 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,5 @@ certs docker-compose.caddy.yml -uploads \ No newline at end of file +uploads + diff --git a/backend/controllers/DLDeliveryController.js b/backend/controllers/DLDeliveryController.js index c93f5097..182729f4 100644 --- a/backend/controllers/DLDeliveryController.js +++ b/backend/controllers/DLDeliveryController.js @@ -52,15 +52,19 @@ export const assignDriverToOrder = async () => { driverID: driver._id, drID: driver.driverID, shopName: order.shopName, + shopEmail: order.shopEmail, + shopPhone: order.shopPhone, pickupAddress: `${order.shopAddress.houseNo || ''}, ${order.shopAddress.streetName || ''}, ${order.shopAddress.city || ''}, ${order.shopAddress.district || ''}` .trim() .replace(/^,|,$/g, ''), customerName: order.customerName, - dropOffAddress: - `${order.customerAddress.streetAddress || ''}, ${order.customerAddress.city || ''}, ${order.customerAddress.zipCode || ''}, ${order.customerAddress.district || ''}` - .trim() - .replace(/^,|,$/g, ''), + customerEmail: order.customerEmail, + customerNumber: order.customerNumber, + dropOffAddress: order.customerAddress, + // `${order.customerAddress.streetAddress || ''}, ${order.customerAddress.city || ''}, ${order.customerAddress.zipCode || ''}, ${order.customerAddress.district || ''}` + // .trim() + // .replace(/^,|,$/g, ''), }) await delivery.save() @@ -112,8 +116,10 @@ export const sendEmailToDriver = async (driver, delivery) => {
  • Order ID: ${delivery.oID}
  • Tracking ID: ${delivery.trackingID}
  • Shop Name: ${delivery.shopName}
  • +
  • Shop Contact: ${delivery.shopPhone}
  • Pickup Address: ${delivery.pickupAddress}
  • Customer Name: ${delivery.customerName}
  • +
  • Customer Contact: ${delivery.customerNumber}
  • Drop-Off Address: ${delivery.dropOffAddress}
  • diff --git a/backend/controllers/DLOcontroller.js b/backend/controllers/DLOcontroller.js index 00ad754c..b73824e0 100644 --- a/backend/controllers/DLOcontroller.js +++ b/backend/controllers/DLOcontroller.js @@ -148,22 +148,32 @@ const assignReadyOrders = async () => { const customerName = `${user.firstname || ''} ${user.lastname || ''}`.trim() - // Use the defaultAddress from the User model - const customerAddress = { - streetAddress: user.defaultAddress.streetAddress || 'N/A', - city: user.defaultAddress.city || 'N/A', - zipCode: user.defaultAddress.zipCode || 'N/A', - district: user.defaultAddress.district || 'N/A', - } - // console.log(`Customer Address: ${customerAddress.streetAddress}, ${customerAddress.city}`) + // // Use the defaultAddress from the User model + // const customerAddress = { + // streetAddress: user.defaultAddress.streetAddress || 'N/A', + // city: user.defaultAddress.city || 'N/A', + // zipCode: user.defaultAddress.zipCode || 'N/A', + // district: user.defaultAddress.district || 'N/A', + // } + // // console.log(`Customer Address: ${customerAddress.streetAddress}, ${customerAddress.city}`) + + // Extract the shippingAddress fields from the user + const { name, address, city, phone, email } = order.shippingAddress + + // Combine address and city into a single string + const cusAddress = `${address}, ${city}` // Create a new dOrder with the customer and shop address and order data const newDOrder = new dOrder({ oID: order._id.toString(), // Assigning the order ID from the Order model orderID: generateOrderId(), // Randomly generated order ID customerName: customerName, // Full name from User model - customerAddress: customerAddress, // Address from User model + customerEmail: email, // Email from User model + customerNumber: phone, // Phone number from User model + customerAddress: cusAddress, // Address from User model shopName: shop.name, // Shop name from the Shop model + shopEmail: shop.email, // Shop email from the Shop model + shopPhone: shop.contactNumber, // Shop phone from the Shop model shopAddress: { houseNo: shop.address.houseNo, // House number from the Shop model streetName: shop.address.streetName, // Street name from the Shop model diff --git a/backend/controllers/orderController.js b/backend/controllers/orderController.js index f06373dc..a4a1a573 100644 --- a/backend/controllers/orderController.js +++ b/backend/controllers/orderController.js @@ -249,7 +249,6 @@ export const getTotalSales = async (req, res) => { } } - export const getShopTotalIncome = async (req, res) => { try { const totalIncome = await Order.aggregate([ @@ -290,11 +289,13 @@ export const getShopTotalIncome = async (req, res) => { ownerName: '$farmerDetails.name', // Include the owner's name }, }, - ]); + ]) - res.status(200).json(totalIncome); // Send the total income response + 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' }); + 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/models/DLDeliveryModel.js b/backend/models/DLDeliveryModel.js index 1333f150..dd15e14f 100644 --- a/backend/models/DLDeliveryModel.js +++ b/backend/models/DLDeliveryModel.js @@ -26,13 +26,35 @@ const DLDeliverySchema = new mongoose.Schema({ drID: { type: String, }, + shopName: String, + shopEmail: { + type: String, + required: false, + }, + + shopPhone: { + type: String, + required: false, + }, + pickupAddress: { type: String, }, + customerName: String, + customerEmail: { + type: String, + required: false, + }, + + customerNumber: { + type: String, + required: false, + }, + dropOffAddress: { type: String, }, diff --git a/backend/models/DLOModel.js b/backend/models/DLOModel.js index c18da1dd..55abaf90 100644 --- a/backend/models/DLOModel.js +++ b/backend/models/DLOModel.js @@ -19,11 +19,23 @@ const dorderSchema = new mongoose.Schema( type: String, required: false, }, + + customerEmail: { + type: String, + required: false, + }, + + customerNumber: { + type: String, + required: false, + }, customerAddress: { - streetAddress: { type: String, required: false }, - city: { type: String, required: false }, - zipCode: { type: String, required: false }, - district: { type: String, required: false }, + type: String, + required: false, + // streetAddress: { type: String, required: false }, + // city: { type: String, required: false }, + // zipCode: { type: String, required: false }, + // district: { type: String, required: false }, }, shopName: { type: String, @@ -43,6 +55,16 @@ const dorderSchema = new mongoose.Schema( required: [false, 'City is required'], }, }, + shopEmail: { + type: String, + required: false, + }, + + shopPhone: { + type: String, + required: false, + }, + orderStatus: { type: String, enum: ['Pending', 'Ready', 'Picked Up', 'On The Way', 'Delivered'], diff --git a/backend/routes/orderRoute.js b/backend/routes/orderRoute.js index 7f489ef7..df864085 100644 --- a/backend/routes/orderRoute.js +++ b/backend/routes/orderRoute.js @@ -23,7 +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('/shop-total-income', getShopTotalIncome) orderRouter.get('/:id', getOrderById) // Fetch order details by ID with populated farmer and user details diff --git a/backend/server.js b/backend/server.js index 7b2763a0..96cae397 100644 --- a/backend/server.js +++ b/backend/server.js @@ -50,6 +50,7 @@ import commentRoutes from './routes/comments.js' import newsRoutes from './routes/newsRoutes.js' // Error handling + import { errorHandler, notFound } from './middlewares/errorMiddleware.js' // Set up port from environment or default to 8000 diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 042f9471..1c04123e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -219,7 +219,10 @@ const router = createBrowserRouter( /> } /> } /> - } /> + } + /> {/* diff --git a/frontend/src/Components/Admin/admnlogins.jsx b/frontend/src/Components/Admin/admnlogins.jsx index fa9a58d6..e8898337 100644 --- a/frontend/src/Components/Admin/admnlogins.jsx +++ b/frontend/src/Components/Admin/admnlogins.jsx @@ -62,7 +62,7 @@ const AdminLogin = ({ manager }) => { Logo

    Admin Login

    diff --git a/frontend/src/Components/Home/DHeader.jsx b/frontend/src/Components/Home/DHeader.jsx deleted file mode 100644 index 71d899f2..00000000 --- a/frontend/src/Components/Home/DHeader.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' -import { Link } from 'react-router-dom' -import logo from '../../assets/Logo.png' - -const Header = () => { - return ( -
    -
    -
    - - Logo - -
    -
    -
    - ) -} - -export default Header diff --git a/frontend/src/Components/delivery/DHeader.jsx b/frontend/src/Components/delivery/DHeader.jsx new file mode 100644 index 00000000..ccf7c8f1 --- /dev/null +++ b/frontend/src/Components/delivery/DHeader.jsx @@ -0,0 +1,31 @@ +import React from 'react' +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/Layouts/BlogLayout.jsx b/frontend/src/Layouts/BlogLayout.jsx index 41c17902..c5808565 100644 --- a/frontend/src/Layouts/BlogLayout.jsx +++ b/frontend/src/Layouts/BlogLayout.jsx @@ -1,7 +1,6 @@ import { Outlet } from 'react-router-dom' // import Footer from '../Components/Home/Footer' import Footer from '../Components/Home/FooterDashboard' -import DHeader from '../Components/Home/DHeader' import BlogHeader from '../Components/Home/BlogHeader' function BlogLayout() { diff --git a/frontend/src/Layouts/DLayout.jsx b/frontend/src/Layouts/DLayout.jsx index e1f0552e..23709275 100644 --- a/frontend/src/Layouts/DLayout.jsx +++ b/frontend/src/Layouts/DLayout.jsx @@ -1,7 +1,7 @@ import { Outlet } from 'react-router-dom' // import Footer from '../Components/Home/Footer' import Footer from '../Components/Home/FooterDashboard' -import DHeader from '../Components/Home/DHeader' +import DHeader from '../Components/delivery/DHeader' function DLayout() { return ( diff --git a/frontend/src/Layouts/HelpLayout.jsx b/frontend/src/Layouts/HelpLayout.jsx index 898c008c..974318f1 100644 --- a/frontend/src/Layouts/HelpLayout.jsx +++ b/frontend/src/Layouts/HelpLayout.jsx @@ -4,21 +4,22 @@ import HelpFooter from '../Components/Help/HelpFooter' import HelpChatIcon from '../Components/Help/HelpChatIcon' function HelpLayout() { - const user = JSON.parse(localStorage.getItem('user')) // Handle the case where user is null or undefined - const customer = user ? { - id: user._id, - name: user.firstname, - email: user.email, - createdAt: Date.now(), - } : { - id: '01JA3JDNTD35GP144D83PPJVR6', - name: 'Guest', - email: 'guest@example.com', - createdAt: Date.now(), - } + const customer = user + ? { + id: user._id, + name: user.firstname, + email: user.email, + createdAt: Date.now(), + } + : { + id: '01JA3JDNTD35GP144D83PPJVR6', + name: 'Guest', + email: 'guest@example.com', + createdAt: Date.now(), + } return (
    diff --git a/frontend/src/Pages/Admin/Afinance.jsx b/frontend/src/Pages/Admin/Afinance.jsx index 74b0b45f..4b3e5538 100644 --- a/frontend/src/Pages/Admin/Afinance.jsx +++ b/frontend/src/Pages/Admin/Afinance.jsx @@ -1,95 +1,99 @@ -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'; +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 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 + 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); + const response = await axios.get('/api/orders/shop-total-income') + setIncomeData(response.data) } catch (err) { - setError(err.message); + setError(err.message) } finally { - setLoading(false); + setLoading(false) } - }; + } useEffect(() => { - fetchShopIncomeData(); - }, []); + fetchShopIncomeData() + }, []) // Handle search input change const handleSearchChange = (e) => { - setSearchTerm(e.target.value); - }; + 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'); - }; + : b.totalIncome - a.totalIncome + }) + setIncomeData(sortedData) + setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc') + } // Generate PDF report const generatePDF = () => { - const doc = new jsPDF(); - const title = 'Shop Income Report'; - + const doc = new jsPDF() + const title = 'Shop Income Report' + // Set font size and style for title - doc.setFontSize(16); - doc.text(title, 14, 16); - + doc.setFontSize(16) + doc.text(title, 14, 16) + // Set font size for table headers - doc.setFontSize(12); - + doc.setFontSize(12) + // Create the table doc.autoTable({ head: [['Shop Name', 'Owner Name', 'Total Income']], body: incomeData - .filter(shop => shop.shopName.toLowerCase().includes(searchTerm.toLowerCase())) - .map(shop => [ + .filter((shop) => + shop.shopName + .toLowerCase() + .includes(searchTerm.toLowerCase()) + ) + .map((shop) => [ shop.shopName, shop.ownerName, - `Rs. ${shop.totalIncome.toFixed(2)}` + `Rs. ${shop.totalIncome.toFixed(2)}`, ]), margin: { top: 24 }, // Margin from the top styles: { fontSize: 10, cellPadding: 5, halign: 'left', // Align text to the right in cells - valign: 'middle' // Vertically center the text + valign: 'middle', // Vertically center the text }, headStyles: { fillColor: [22, 160, 133], // Header background color textColor: [255, 255, 255], // Header text color - fontStyle: 'bold' // Bold font style for headers + fontStyle: 'bold', // Bold font style for headers }, theme: 'striped', // Use striped theme for better readability - }); - - doc.save('shop_monthly_payment_report.pdf'); - }; - + }) + + doc.save('shop_monthly_payment_report.pdf') + } + // Filter data based on search term - const filteredData = incomeData.filter(shop => + const filteredData = incomeData.filter((shop) => shop.shopName.toLowerCase().includes(searchTerm.toLowerCase()) - ); + ) return (
    @@ -100,11 +104,11 @@ const Finance = () => {
    -

    Shop Income Report

    - - +

    + Shop Income Report +

    - + {/* Container for search field, buttons */}
    {/* Search Bar */} @@ -117,17 +121,26 @@ const Finance = () => { /> {/* Button to Manage Shop Income */} - {/* Sort Button */} {/* Download PDF Button */} -
    @@ -142,17 +155,29 @@ const Finance = () => { - - - + + + {filteredData.map((shop) => ( - - - + + + ))} @@ -162,7 +187,7 @@ const Finance = () => { - ); -}; + ) +} -export default Finance; +export default Finance diff --git a/frontend/src/Pages/Admin/ManageShopIncome.jsx b/frontend/src/Pages/Admin/ManageShopIncome.jsx index b4303b14..b7bf4d2a 100644 --- a/frontend/src/Pages/Admin/ManageShopIncome.jsx +++ b/frontend/src/Pages/Admin/ManageShopIncome.jsx @@ -1,66 +1,66 @@ -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'; +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 ManageShopIncome = () => { - const navigate = useNavigate(); - const [shopIncomeData, setShopIncomeData] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(''); - const [searchTerm, setSearchTerm] = useState(''); - const [sortOrder, setSortOrder] = useState('asc'); - const [taxRate, setTaxRate] = useState(20); - const [paymentStatus, setPaymentStatus] = useState({}); // State to store payment statuses + const navigate = useNavigate() + const [shopIncomeData, setShopIncomeData] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState('') + const [searchTerm, setSearchTerm] = useState('') + const [sortOrder, setSortOrder] = useState('asc') + const [taxRate, setTaxRate] = useState(20) + const [paymentStatus, setPaymentStatus] = useState({}) // State to store payment statuses // Function to fetch shop income data const fetchShopIncomeData = async () => { try { - const response = await axios.get('/api/orders/shop-total-income'); // Adjust endpoint as necessary - setShopIncomeData(response.data); + const response = await axios.get('/api/orders/shop-total-income') // Adjust endpoint as necessary + setShopIncomeData(response.data) // Initialize payment statuses with default values (e.g., 'Pending') - const initialStatus = {}; - response.data.forEach(shop => { - initialStatus[shop.shopId] = 'Pending'; // Default to 'Pending' - }); - setPaymentStatus(initialStatus); + const initialStatus = {} + response.data.forEach((shop) => { + initialStatus[shop.shopId] = 'Pending' // Default to 'Pending' + }) + setPaymentStatus(initialStatus) } catch (err) { - setError(err.message); + setError(err.message) } finally { - setLoading(false); + setLoading(false) } - }; + } useEffect(() => { - fetchShopIncomeData(); - }, []); + fetchShopIncomeData() + }, []) // Handle search input change const handleSearchChange = (e) => { - setSearchTerm(e.target.value); - }; + setSearchTerm(e.target.value) + } // Sort the shop income data based on total income const handleSort = () => { const sortedData = [...shopIncomeData].sort((a, b) => { return sortOrder === 'asc' ? a.totalIncome - b.totalIncome - : b.totalIncome - a.totalIncome; - }); - setShopIncomeData(sortedData); - setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); - }; + : b.totalIncome - a.totalIncome + }) + setShopIncomeData(sortedData) + setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc') + } const generatePDF = () => { - const doc = new jsPDF(); - const title = 'Shop Income Report'; + const doc = new jsPDF() + const title = 'Shop Income Report' // Set font size and style for title - doc.setFontSize(16); - doc.text(title, 14, 16); + doc.setFontSize(16) + doc.text(title, 14, 16) // Create the table doc.autoTable({ @@ -72,17 +72,19 @@ const ManageShopIncome = () => { `Tax (${taxRate}%)`, 'Final Shop Income', 'FarmCart Company Income', - 'Payment Status' // New header for payment status + 'Payment Status', // New header for payment status ], ], body: shopIncomeData .filter((shop) => - shop.shopName.toLowerCase().includes(searchTerm.toLowerCase()) + shop.shopName + .toLowerCase() + .includes(searchTerm.toLowerCase()) ) .map((shop) => { - const tax = (shop.totalIncome * (taxRate / 100)).toFixed(2); - const finalIncome = (shop.totalIncome - tax).toFixed(2); - const farmCartIncome = tax; // FarmCart company income is the same as the tax amount + const tax = (shop.totalIncome * (taxRate / 100)).toFixed(2) + const finalIncome = (shop.totalIncome - tax).toFixed(2) + const farmCartIncome = tax // FarmCart company income is the same as the tax amount return [ shop.shopName, shop.ownerName, @@ -90,8 +92,8 @@ const ManageShopIncome = () => { `Rs. ${tax}`, `Rs. ${finalIncome}`, `Rs. ${farmCartIncome}`, - paymentStatus[shop.shopId] // Add payment status to the PDF - ]; + paymentStatus[shop.shopId], // Add payment status to the PDF + ] }), margin: { top: 24 }, styles: { @@ -106,28 +108,29 @@ const ManageShopIncome = () => { fontStyle: 'bold', }, theme: 'striped', - }); + }) - doc.save('shop_income_report.pdf'); - }; + doc.save('shop_income_report.pdf') + } // Filter data based on search term const filteredData = shopIncomeData.filter((shop) => shop.shopName.toLowerCase().includes(searchTerm.toLowerCase()) - ); + ) // Handle payment status button click const handlePaymentStatusChange = (shopId, status) => { setPaymentStatus((prevStatus) => ({ ...prevStatus, - [shopId]: status // Update the payment status for the specific shop - })); + [shopId]: status, // Update the payment status for the specific shop + })) // Optionally, send the new payment status to the backend - axios.post('/api/payment-status', { shopId, status }) - .then(response => console.log("Payment status updated", response)) - .catch(err => console.error("Error updating payment status", err)); - }; + axios + .post('/api/payment-status', { shopId, status }) + .then((response) => console.log('Payment status updated', response)) + .catch((err) => console.error('Error updating payment status', err)) + } return (
    @@ -157,14 +160,17 @@ const ManageShopIncome = () => { placeholder="Enter Tax Rate" value={taxRate} onChange={(e) => { - const value = parseFloat(e.target.value); + const value = parseFloat(e.target.value) if (!isNaN(value) && value >= 0) { - setTaxRate(value); + setTaxRate(value) } }} className="border p-2 w-1/12 rounded-md shadow-md border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-400" /> -

    Adjust the tax rate

    +

    + {' '} + Adjust the tax rate +

    {filteredData.map((shop) => { - const tax = (shop.totalIncome * (taxRate / 100)).toFixed(2); - const finalIncome = (shop.totalIncome - tax).toFixed(2); + const tax = ( + shop.totalIncome * + (taxRate / 100) + ).toFixed(2) + const finalIncome = ( + shop.totalIncome - tax + ).toFixed(2) // Debugging: Log values to check calculations - console.log(`Shop: ${shop.shopName}, Total Income: ${shop.totalIncome}, Tax: ${tax}, Final Income: ${finalIncome}`); + console.log( + `Shop: ${shop.shopName}, Total Income: ${shop.totalIncome}, Tax: ${tax}, Final Income: ${finalIncome}` + ) return ( @@ -239,7 +252,10 @@ const ManageShopIncome = () => { {shop.ownerName} - ); + ) })}
    Shop NameOwner NameTotal Income + Shop Name + + Owner Name + + Total Income +
    {shop.shopName}{shop.ownerName}Rs.{shop.totalIncome.toFixed(2)} + {shop.shopName} + + {shop.ownerName} + + Rs.{shop.totalIncome.toFixed(2)} +
    - Rs.{shop.totalIncome.toFixed(2)} + Rs. + {shop.totalIncome.toFixed( + 2 + )} Rs.{tax} @@ -253,31 +269,58 @@ const ManageShopIncome = () => {
    @@ -286,7 +329,7 @@ const ManageShopIncome = () => {
    - ); -}; + ) +} -export default ManageShopIncome; +export default ManageShopIncome diff --git a/frontend/src/Pages/blog manage/UserBlog.jsx b/frontend/src/Pages/blog manage/UserBlog.jsx index 07c99718..f6486819 100644 --- a/frontend/src/Pages/blog manage/UserBlog.jsx +++ b/frontend/src/Pages/blog manage/UserBlog.jsx @@ -100,7 +100,10 @@ export default function TourismBlog() {
    {blog.newsImage && ( {blog.title} @@ -181,7 +184,10 @@ export default function TourismBlog() {
    {blog.newsImage && ( {blog.title} diff --git a/frontend/src/Pages/delivery/DLDriverRegistrationForm.jsx b/frontend/src/Pages/delivery/DLDriverRegistrationForm.jsx index 418eb063..c29195ee 100644 --- a/frontend/src/Pages/delivery/DLDriverRegistrationForm.jsx +++ b/frontend/src/Pages/delivery/DLDriverRegistrationForm.jsx @@ -164,6 +164,9 @@ const RegisterDriverForm = () => { errorMessage = 'licenseCardNumber must be in uppercase and follow the format A000000 .' } + if (value.length > 7) { + return // Prevent setting values longer than 7 digits + } } setFormData((prevData) => ({ ...prevData, @@ -272,369 +275,392 @@ const RegisterDriverForm = () => { return (
    -
    - Logo -

    - Driver Registration Form -

    -
    -
    - {/* Input fields for registration */} -
    - - {errors.firstName && ( -

    - {errors.firstName} -

    - )} -
    +
    + {/* New Login Section */} +
    +
    +

    + Do you have an account? +

    + +
    +
    + +
    + Logo +

    + Driver Registration Form +

    + +
    + {/* Input fields for registration */} +
    + + {errors.firstName && ( +

    + {errors.firstName} +

    + )} +
    -
    - - {errors.lastName && ( -

    - {errors.lastName} -

    - )} -
    +
    + + {errors.lastName && ( +

    + {errors.lastName} +

    + )} +
    -
    - -
    +
    + +
    -
    - - {errors.email && ( -

    - {errors.email} -

    - )} -
    +
    + + {errors.email && ( +

    + {errors.email} +

    + )} +
    -
    - - {errors.phone && ( -

    - {errors.phone} -

    - )} -
    +
    + + {errors.phone && ( +

    + {errors.phone} +

    + )} +
    -
    - +
    + + + {errors.dateOfBirth && ( +

    + {errors.dateOfBirth} +

    + )} +
    - {errors.dateOfBirth && ( -

    - {errors.dateOfBirth} -

    - )} -
    +
    + + + {errors.idCardNumber && ( +

    + {errors.idCardNumber} +

    + )} +
    -
    - +
    + + {errors.licenseCardNumber && ( +

    + {errors.licenseCardNumber} +

    + )} +
    - {errors.idCardNumber && ( -

    - {errors.idCardNumber} -

    - )} +
    + +
    + +
    + + + {errors.vehicleNumber && ( +

    + {errors.vehicleNumber} +

    + )} +
    + +
    + +
    -
    + {/* ID Card Image Upload */} +
    + + handleFileChange( + e, + setIdCardImageUrl, + setPreviewIdCard, + 'idCard' + ) + } + className="mt-1 block w-full px-4 py-2 bg-gray-50 border rounded-lg" required /> - {errors.licenseCardNumber && ( -

    - {errors.licenseCardNumber} -

    + {loading.idCard && ( +
    + + + + +
    + )} + {previewIdCard && ( + ID Card Preview )}
    -
    + {/* License Image Upload */} +
    + + handleFileChange( + e, + setLicenseImageUrl, + setPreviewLicense, + 'license' + ) + } + className="mt-1 block w-full px-4 py-2 bg-gray-50 border rounded-lg" required /> + {loading.license && ( +
    + + + + +
    + )} + {previewLicense && ( + License Preview + )}
    -
    + {/* Personal Image Upload */} +
    + + handleFileChange( + e, + setPersonalImageUrl, + setPreviewPersonal, + 'personal' + ) + } + className="mt-1 block w-full px-4 py-2 bg-gray-50 border rounded-lg" required /> - - {errors.vehicleNumber && ( -

    - {errors.vehicleNumber} -

    + {loading.personal && ( +
    + + + + +
    + )} + {previewPersonal && ( + Personal Preview )}
    -
    - -
    -
    - - {/* ID Card Image Upload */} -
    - - - handleFileChange( - e, - setIdCardImageUrl, - setPreviewIdCard, - 'idCard' - ) - } - className="mt-1 block w-full px-4 py-2 bg-gray-50 border rounded-lg" - required - /> - {loading.idCard && ( -
    - - - - -
    + {successMessage && ( +

    + {successMessage} +

    )} - {previewIdCard && ( - ID Card Preview - )} -
    - - {/* License Image Upload */} -
    - - - handleFileChange( - e, - setLicenseImageUrl, - setPreviewLicense, - 'license' - ) - } - className="mt-1 block w-full px-4 py-2 bg-gray-50 border rounded-lg" - required - /> - {loading.license && ( -
    - - - - -
    - )} - {previewLicense && ( - License Preview - )} -
    - {/* Personal Image Upload */} -
    - - - handleFileChange( - e, - setPersonalImageUrl, - setPreviewPersonal, - 'personal' - ) +
    - - {successMessage && ( -

    - {successMessage} -

    - )} - - - + ? 'Uploading...' + : 'Submit'} + + +
    ) diff --git a/frontend/src/Pages/delivery/DLLogin.jsx b/frontend/src/Pages/delivery/DLLogin.jsx index ec7e4f9e..16d161e5 100644 --- a/frontend/src/Pages/delivery/DLLogin.jsx +++ b/frontend/src/Pages/delivery/DLLogin.jsx @@ -32,7 +32,7 @@ const DLLogin = () => { Logo

    Driver Login

    diff --git a/frontend/src/Pages/delivery/DLviewDelivery.jsx b/frontend/src/Pages/delivery/DLviewDelivery.jsx index 2825ecbd..749bf239 100644 --- a/frontend/src/Pages/delivery/DLviewDelivery.jsx +++ b/frontend/src/Pages/delivery/DLviewDelivery.jsx @@ -57,38 +57,111 @@ const DLViewDelivery = () => { const generatePDF = () => { const doc = new jsPDF('p', 'mm', 'a4') // Set to A4 size (portrait orientation) - // Add logo - doc.addImage(farmcartLogo, 'PNG', 10, 10, 50, 20) // Add logo with width and height + // 1. Add the logo and company details + addLogoAndCompanyInfo(doc) - // Add report title - doc.setFontSize(24) - doc.setTextColor(40) - doc.text('Delivery Details Report', 105, 40, null, null, 'center') // Centered title + // 2. Add a bold main title + addMainTitle(doc) + + // 3. Add a line divider below the title and company info + addDivider(doc, 70) // Positioned at y=70 to be below the title and company details + + // 4. Add tracking and issued details (tracking ID, dates, delivery status, etc.) + addTrackingAndIssuedDetails(doc) + + // 5. Add delivery details in a table format + addDeliveryDetailsTable(doc) + + // 6. Add footer with generated information and signature + addFooter(doc) + + // 7. Save the PDF with a dynamic name based on the delivery tracking ID + doc.save(`Delivery_${delivery.trackingID}.pdf`) + } - // Add company information + // Function to add the logo and company information + const addLogoAndCompanyInfo = (doc) => { + // Add the logo at the top, aligned to the center + doc.addImage(farmcartLogo, 'PNG', 70, 10, 70, 20) + + // Add the company information below the logo, centered doc.setFontSize(12) doc.text('FarmCart Lanka (PVT.) LTD', 105, 50, null, null, 'center') doc.text('No.78, Malabe, Colombo', 105, 55, null, null, 'center') doc.text('Phone: (+94) 011 34 56 837', 105, 60, null, null, 'center') doc.text('Website: www.farmcart.com', 105, 65, null, null, 'center') + } + + // Function to add the bold main title + const addMainTitle = (doc) => { + // Set font to bold and add the main title + doc.setFontSize(24) + doc.setFont('helvetica', 'bold') // Set font to bold + doc.setTextColor(40) + doc.text('Delivery Details Report', 105, 40, null, null, 'center') // Title centered at the top + } + + // Function to add tracking ID, assigned date (issued date), delivered date, and other details + const addTrackingAndIssuedDetails = (doc) => { + // Add Tracking ID, Assigned Date (as Issued Date), and Delivered Date or "Ongoing" + doc.setFontSize(12) + doc.setFont('helvetica', 'normal') // Reset font to normal - // Section for "Delivery Information" - doc.setFontSize(18) - doc.setTextColor(0, 51, 102) // Dark blue color - doc.text('Delivery Information', 14, 80) // Section title left-aligned + // Tracking ID + doc.text(`Tracking ID: ${delivery.trackingID}`, 14, 80) // Left-aligned tracking ID - // Delivery details table + // Assigned Date (as Issued Date) + doc.text( + `Assigned Date: ${new Date(delivery.assignDateTime).toLocaleDateString()}`, + 190, + 80, + null, + null, + 'right' + ) // Right-aligned assigned date + + // Delivery Status + doc.text(`Delivery Status: ${delivery.deliveryStatus}`, 14, 85) // Left-aligned delivery status + + // Delivered Date or Ongoing + const deliveredText = delivery.deliveredDateTime + ? new Date(delivery.deliveredDateTime).toLocaleDateString() // Delivered Date + : 'Ongoing' // If not delivered + doc.text( + `Delivered Date: ${deliveredText}`, + 190, + 85, + null, + null, + 'right' + ) // Right-aligned delivered/ongoing date + } + + // Function to add a divider line + const addDivider = (doc, yPos) => { + doc.setLineWidth(0.5) + doc.line(10, yPos, 200, yPos) // Horizontal line from x=10 to x=200 + } + + // Function to add delivery details in a table + const addDeliveryDetailsTable = (doc) => { doc.autoTable({ - startY: 90, // Positioning below "Delivery Information" + startY: 95, // Start below the tracking and issued details head: [['Field', 'Details']], // Table headers body: [ ['Tracking ID', delivery.trackingID], ['Order ID', delivery.oID], ['Driver ID', delivery.drID], ['Driver Name', driver.fullName], + ['Driver Email', driver.email], + ['Driver Contact', driver.phone], ['Shop Name', delivery.shopName], + ['Shop Contact Number', delivery.shopPhone], + ['Shop Email', delivery.shopEmail], ['Pickup Address', delivery.pickupAddress], ['Customer Name', delivery.customerName || 'N/A'], + ['Customer Contact Number', delivery.customerNumber || 'N/A'], + ['Customer Email', delivery.customerEmail || 'N/A'], ['Dropoff Address', delivery.dropOffAddress], [ 'Assigned Time', @@ -108,9 +181,13 @@ const DLViewDelivery = () => { alternateRowStyles: { fillColor: [245, 245, 245] }, // Light grey for alternate rows margin: { top: 10 }, }) + } - // Add footer section for generated date and signature space + // Function to add footer + const addFooter = (doc) => { const finalY = doc.autoTable.previous.finalY + 20 // Get Y position after the table + + // Add "Generated on" date doc.setFontSize(12) doc.text( `Report Generated on: ${new Date().toLocaleString()}`, @@ -122,9 +199,6 @@ const DLViewDelivery = () => { doc.text('Approved by:', 14, finalY + 10) doc.text('__________________________', 14, finalY + 20) // Placeholder for signature doc.text('Signature', 14, finalY + 25) - - // Save the PDF with a dynamic name based on the delivery tracking ID - doc.save(`Delivery_${delivery.trackingID}.pdf`) } return ( @@ -153,8 +227,10 @@ const DLViewDelivery = () => { ['Order ID', delivery.oID], ['Driver ID', delivery.drID], ['Driver Name', driver.fullName], - + ['Driver Email', driver.email], + ['Driver Contact', driver.phone], ['Shop Name', delivery.shopName], + ['Shop Email', delivery.shopEmail], [ 'Pickup Address', delivery.pickupAddress, @@ -163,6 +239,14 @@ const DLViewDelivery = () => { 'Customer Name', delivery.customerName || 'N/A', ], + [ + 'Customer ContactNumber', + delivery.customerNumber || 'N/A', + ], + [ + 'Customer Email', + delivery.customerEmail || 'N/A', + ], [ 'Dropoff Address', delivery.dropOffAddress, @@ -188,7 +272,12 @@ const DLViewDelivery = () => { ].map(([field, value], index) => ( {field} diff --git a/frontend/src/Pages/delivery/driver/ViewDelivery.jsx b/frontend/src/Pages/delivery/driver/ViewDelivery.jsx index cab79b80..ec0ffa2a 100644 --- a/frontend/src/Pages/delivery/driver/ViewDelivery.jsx +++ b/frontend/src/Pages/delivery/driver/ViewDelivery.jsx @@ -11,6 +11,8 @@ const DLViewDelivery = () => { const { id } = useParams() // Get the delivery ID from the URL const [delivery, setDelivery] = useState(null) // State for storing delivery data const [driver, setDriver] = useState(null) // State for driver info + const [customer, setCustomer] = useState(null) // State for driver info + const [loading, setLoading] = useState(true) const driverToken = localStorage.getItem('driverToken') // Get driver token from localStorage @@ -60,56 +62,133 @@ const DLViewDelivery = () => { const generatePDF = () => { const doc = new jsPDF('p', 'mm', 'a4') // Ensure A4 size document - // Add logo at the top - doc.addImage(farmcartLogo, 'PNG', 10, 10, 50, 20) // Add logo with width and height + // 1. Add the logo and company details + addLogoAndCompanyInfo(doc) + + // 2. Add a bold main title + addMainTitle(doc) + + // 3. Add a line divider below the title and company info + addDivider(doc, 60) // Positioned at y=60 to be below the title and company details + + // 4. Add tracking and issued details (tracking ID, dates, delivery status, etc.) + addTrackingAndIssuedDetails(doc) + + // 5. Add delivery details in a table format + addDeliveryDetailsTable(doc) + + // 6. Add footer with generated information and signature + addFooter(doc) - // Add main title (Delivery Details Report) + // 7. Save the PDF with a dynamic name based on the delivery tracking ID + doc.save(`Delivery_${delivery.trackingID}.pdf`) + } + + // Function to add the logo and company information + const addLogoAndCompanyInfo = (doc) => { + // Add the logo at the top, aligned to the left + doc.addImage(farmcartLogo, 'PNG', 10, 10, 50, 20) + + // Add the company information to the right of the logo + doc.setFontSize(12) + doc.text('FarmCart Lanka (PVT.) LTD', 190, 15, null, null, 'right') + doc.text('No.78, Malabe, Colombo', 190, 20, null, null, 'right') + doc.text('(+94) 011 34 56 837', 190, 25, null, null, 'right') + doc.text('contact@farmcart.com', 190, 30, null, null, 'right') + doc.text('www.farmcart.com', 190, 35, null, null, 'right') + } + + // Function to add the bold main title + const addMainTitle = (doc) => { + // Set font to bold and add the main title + doc.setFontSize(24) + doc.setFont('helvetica', 'bold') // Set font to bold + doc.setTextColor(40) + doc.text('Delivery Details Report', 105, 50, null, null, 'center') // Title centered at the top + } + + // Function to add tracking ID, assigned date (issued date), delivered date, and other details + const addTrackingAndIssuedDetails = (doc) => { + // Add the main title doc.setFontSize(24) + doc.setFont('helvetica', 'bold') // Set font to bold doc.setTextColor(40) - doc.text('Delivery Details Report', 105, 40, null, null, 'center') // Title centered at the top + doc.text('Delivery Details Report', 105, 50, null, null, 'center') // Title centered at the top - // Add subtitle (e.g., Report generated on) + // Add tracking ID, assigned date (as Issued Date), and delivered date or "Ongoing" doc.setFontSize(12) + doc.setFont('helvetica', 'normal') // Reset font to normal + + // Tracking ID + doc.text(`Tracking ID: ${delivery.trackingID}`, 14, 70) // Left-aligned tracking ID + + // Assigned Date (as Issued Date) doc.text( - `Report generated on: ${new Date().toLocaleDateString()}`, - 105, - 47, + `Assigned Date: ${new Date(delivery.assignDateTime).toLocaleDateString()}`, + 190, + 70, null, null, - 'center' - ) + 'right' + ) // Right-aligned assigned date - // Add company name and contact info - doc.setFontSize(12) - doc.text('FarmCart Lanka (PVT.) LTD', 105, 55, null, null, 'center') - doc.text('No.78, Malabe, Colombo', 105, 60, null, null, 'center') - doc.text('Phone: (+94) 011 34 56 837', 105, 65, null, null, 'center') - doc.text('Website: www.farmcart.com', 105, 70, null, null, 'center') + // Delivery Status + doc.text(`Delivery Status: ${delivery.deliveryStatus}`, 14, 75) // Left-aligned delivery status - // Add section for "Delivery Information" - doc.setFontSize(16) - doc.setTextColor(0, 51, 102) // Set dark blue color - doc.text('Delivery Information', 14, 85) // Left-aligned section title + // Delivered Date or Ongoing + const deliveredText = delivery.deliveredDateTime + ? new Date(delivery.deliveredDateTime).toLocaleDateString() // Delivered Date + : 'Ongoing' // If not delivered + doc.text( + `Delivered Date: ${deliveredText}`, + 190, + 75, + null, + null, + 'right' + ) // Right-aligned delivered/ongoing date + } + + // Function to add a divider line + const addDivider = (doc, yPos) => { + doc.setLineWidth(0.5) + doc.line(10, yPos, 200, yPos) // Horizontal line from x=10 to x=200 + } - // Add a table for delivery details + // Function to add delivery details in a table + const addDeliveryDetailsTable = (doc) => { doc.autoTable({ - startY: 90, // Positioning the table below the section title - head: [['Field', 'Details']], + startY: 90, // Start below the divider line + head: [['#', 'Field', 'Details']], // Add numbering in the table header body: [ - ['Tracking ID', delivery.trackingID], - ['Order ID', delivery.oID], - ['Driver Name', driver?.firstName + ' ' + driver?.lastName], - ['Driver ID', delivery.drID], - ['Shop Name', delivery.shopName], - ['Pickup Address', delivery.pickupAddress], - ['Customer Name', delivery.customerName || 'N/A'], - ['Dropoff Address', delivery.dropOffAddress], + ['1', 'Tracking ID', delivery.trackingID], + ['2', 'Order ID', delivery.oID], + [ + '3', + 'Driver Name', + driver?.firstName + ' ' + driver?.lastName, + ], + ['4', 'Driver ID', delivery.drID], + ['5', 'Shop Name', delivery.shopName], + ['6', 'Shop Contact Number', delivery.shopPhone], + ['7', 'Shop Email', delivery.shopEmail], + ['8', 'Pickup Address', delivery.pickupAddress], + ['9', 'Customer Name', delivery.customerName || 'N/A'], [ + '10', + 'Customer Contact Number', + delivery.customerNumber || 'N/A', + ], + ['11', 'Customer Email', delivery.customerEmail || 'N/A'], + ['12', 'Dropoff Address', delivery.dropOffAddress], + [ + '13', 'Assigned Time', new Date(delivery.assignDateTime).toLocaleString(), ], - ['Delivery Status', delivery.deliveryStatus], + ['14', 'Delivery Status', delivery.deliveryStatus], [ + '15', 'Delivered Time', delivery.deliveredDateTime ? new Date(delivery.deliveredDateTime).toLocaleString() @@ -120,7 +199,10 @@ const DLViewDelivery = () => { headStyles: { fillColor: [46, 204, 113] }, // Green background for table headers bodyStyles: { textColor: [0, 0, 0] }, // Black text color for table body }) + } + // Function to add footer + const addFooter = (doc) => { // Add "Generated at" timestamp at the bottom const generatedAt = new Date().toLocaleString() doc.setFontSize(12) @@ -130,17 +212,21 @@ const DLViewDelivery = () => { doc.autoTable.previous.finalY + 20 ) - // Add "Approved by" section at the bottom - doc.text('Approved by:', 14, doc.autoTable.previous.finalY + 30) + // Add "Generated by" section at the bottom + doc.text('Generated by:', 14, doc.autoTable.previous.finalY + 30) doc.text( - '__________________________', + `${driver?.firstName + ' ' + driver?.lastName}`, // Driver's full name 14, - doc.autoTable.previous.finalY + 40 - ) // Placeholder for signature - doc.text('Signature', 14, doc.autoTable.previous.finalY + 45) + doc.autoTable.previous.finalY + 35 // Positioning below "Generated by" + ) - // Save the PDF with a dynamic name based on the delivery tracking ID - doc.save(`Delivery_${delivery.trackingID}.pdf`) + // Placeholder for signature + doc.text( + '__________________________', + 14, + doc.autoTable.previous.finalY + 45 + ) + doc.text('Signature', 14, doc.autoTable.previous.finalY + 50) } if (loading) { @@ -183,6 +269,11 @@ const DLViewDelivery = () => { ], // Display driver name ['Driver ID', delivery.drID], ['Shop Name', delivery.shopName], + [ + 'Shop ContactNumber', + delivery.shopPhone, + ], + ['Shop Email', delivery.shopEmail], [ 'Pickup Address', delivery.pickupAddress, @@ -191,6 +282,14 @@ const DLViewDelivery = () => { 'Customer Name', delivery.customerName || 'N/A', ], + [ + 'Customer ContactNumber', + delivery.customerNumber || 'N/A', + ], + [ + 'Customer Email', + delivery.customerEmail || 'N/A', + ], [ 'Dropoff Address', delivery.dropOffAddress, @@ -216,7 +315,12 @@ const DLViewDelivery = () => { ].map(([field, value], index) => ( {field}