diff --git a/client/src/components/AdministratorNavigationPanel.tsx b/client/src/components/AdministratorNavigationPanel.tsx index 0dc8e9a..106475d 100644 --- a/client/src/components/AdministratorNavigationPanel.tsx +++ b/client/src/components/AdministratorNavigationPanel.tsx @@ -161,7 +161,7 @@ export default function AdministratorNavigationPanel() { Bill Contest

} onClickRef="ranking" /> diff --git a/client/src/pages/Ranking.tsx b/client/src/pages/Ranking.tsx index 79c2c2e..a56af92 100644 --- a/client/src/pages/Ranking.tsx +++ b/client/src/pages/Ranking.tsx @@ -1,9 +1,16 @@ import { useEffect, useState } from 'react'; import config from '../config'; import instance from '../security/http'; -import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, SortDescriptor, Button } from '@nextui-org/react'; +import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, SortDescriptor, Button, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from '@nextui-org/react'; import { EmailIcon, TrashDeleteIcon } from '../icons'; +interface User { + id: string; + firstName: string; + lastName: string; + email: string; +} + interface FormData { id: number; electricalBill: number; @@ -18,29 +25,39 @@ interface FormData { export default function Ranking() { const [formData, setFormData] = useState([]); + const [userData, setUserData] = useState([]); const [sortDescriptor, setSortDescriptor] = useState({ column: 'avgBill', direction: 'ascending', }); + const [isEmailModalOpen, setIsEmailModalOpen] = useState(false); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [selectedUser, setSelectedUser] = useState<{ email: string, name: string }>({ email: '', name: '' }); + const [selectedFormId, setSelectedFormId] = useState(null); useEffect(() => { - const getFormData = async () => { + const fetchData = async () => { try { - const response = await instance.get(`${config.serverAddress}/hbcform`); - const processedData = response.data.map((data: FormData) => ({ + // Fetch form data + const formResponse = await instance.get(`${config.serverAddress}/hbcform`); + const processedFormData = formResponse.data.map((data) => ({ ...data, electricalBill: Number(data.electricalBill), waterBill: Number(data.waterBill), totalBill: Number(data.totalBill), avgBill: Number(data.avgBill), })); - setFormData(processedData); + setFormData(processedFormData); + + // Fetch user data + const userResponse = await instance.get(`${config.serverAddress}/users/all`); + setUserData(userResponse.data); } catch (error) { - console.log("Failed to fetch form data"); + console.log("Failed to fetch data"); } }; - getFormData(); + fetchData(); }, []); const sortFormData = (list: FormData[], descriptor: SortDescriptor) => { @@ -71,6 +88,49 @@ export default function Ranking() { const sortedFormData = sortFormData(formData, sortDescriptor); + // Combine form data with user information + const combinedData = sortedFormData.map((data) => { + const user = userData.find((user) => user.id === data.userId); + return { + ...data, + userName: user ? `${user.firstName} ${user.lastName}` : 'Unknown User', + userEmail: user ? user.email : 'Unknown Email', + }; + }); + + const handleEmailClick = (email: string, name: string) => { + setSelectedUser({ email, name }); + setIsEmailModalOpen(true); + }; + + const sendEmail = () => { + const subject = "Home Bill Contest"; + const body = `Dear ${selectedUser.name}, + \nPlease submit another submission for the home bill contest with correct documents. + \nThank you for your cooperation. + \nYour Sincerely, + Admin from Ecoconnect.gov`; + window.location.href = `mailto:${selectedUser.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`; + setIsEmailModalOpen(false); + }; + + const handleDeleteClick = (id: number) => { + setSelectedFormId(id); + setIsDeleteModalOpen(true); + }; + + const deleteForm = async () => { + if (selectedFormId === null) return; + try { + await instance.delete(`${config.serverAddress}/hbcform/${selectedFormId}`); + setFormData(formData.filter((data) => data.id !== selectedFormId)); + setSelectedFormId(null); + setIsDeleteModalOpen(false); + } catch (error) { + console.error("Failed to delete form entry:", error); + } + }; + return (

Form Data

@@ -78,6 +138,8 @@ export default function Ranking() { User ID + User Name + User Email Electrical Bill Water Bill Total Bill @@ -90,9 +152,11 @@ export default function Ranking() { Actions - {sortedFormData.map((data) => ( + {combinedData.map((data) => ( {data.userId} + {data.userName} + {data.userEmail} ${data.electricalBill.toFixed(2)} ${data.waterBill.toFixed(2)} ${data.totalBill.toFixed(2)} @@ -105,14 +169,56 @@ export default function Ranking() { {data.wbPicture && Water Bill} - - + + ))}
+ {/* Email Confirmation Modal */} + + + {(onClose) => ( + <> + Send Email + +

Are you sure you want to send this email to {selectedUser.email}?

+
+ + + + + + )} +
+
+ {/* Delete Confirmation Modal */} + + + {(onClose) => ( + <> + Delete Entry + +

Are you sure you want to delete this entry?

+
+ + + + + + )} +
+
); } diff --git a/server/routes/hbcform.js b/server/routes/hbcform.js index ba7de79..15f5601 100644 --- a/server/routes/hbcform.js +++ b/server/routes/hbcform.js @@ -107,4 +107,21 @@ router.get("/wbPicture/:id", async (req, res) => { } }); +router.delete("/:id", async (req, res) => { + let id = req.params.id; + try { + const result = await HBCform.destroy({ where: { id } }); + if (result === 0) { + // No rows were deleted + res.sendStatus(404); + } else { + // Successfully deleted + res.sendStatus(204); + } + } catch (err) { + console.error("Error deleting form entry:", err); + res.status(500).json({ message: "Failed to delete form entry", error: err }); + } +}); + module.exports = router; \ No newline at end of file