diff --git a/client/src/icons.tsx b/client/src/icons.tsx index 0b6d7cf..75c50ed 100644 --- a/client/src/icons.tsx +++ b/client/src/icons.tsx @@ -539,3 +539,78 @@ export const ArrowTopRightOnSquare = () => { ); }; + +export const TrashDeleteIcon = () => { + return ( + + + + ); +} + +export const EmailIcon = () => { + return ( + + + + ) +} + +export const InfoIcon = () => { + return ( + + + + ) +} + +export const TrophyIcon = () => { + return ( + + + + ) +} \ No newline at end of file diff --git a/client/src/pages/HBContestPage.tsx b/client/src/pages/HBContestPage.tsx index cc97f09..9cc9985 100644 --- a/client/src/pages/HBContestPage.tsx +++ b/client/src/pages/HBContestPage.tsx @@ -1,5 +1,22 @@ -import { Button } from "@nextui-org/react"; +import { Button, Tooltip, Modal, ModalContent, ModalHeader, ModalBody } from "@nextui-org/react"; +import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; +import instance from "../security/http"; +import config from "../config"; +import { InfoIcon, TrophyIcon } from "../icons"; + +interface User { + id: string; + firstName: string; + lastName: string; +} + +interface CombinedData { + userId: string; + formId: string; + name: string; + avgBill: number; +} export default function HBContestPage() { const navigate = useNavigate(); @@ -15,51 +32,181 @@ export default function HBContestPage() { } }; + const [combinedData, setCombinedData] = useState([]); + const [isModalOpen, setIsModalOpen] = useState(false); + + const handleOpenModal = () => { + setIsModalOpen(true); + }; + + const [isInfoModalOpen, setIsInfoModalOpen] = useState(false); + + const handleOpenInfoModal = () => setIsInfoModalOpen(true); + + useEffect(() => { + const fetchData = async () => { + try { + // Fetch form data + const formDataResponse = await instance.get(`${config.serverAddress}/hbcform`); + const processedFormData = formDataResponse.data; + + // Fetch user information + const userInfoResponse = await instance.get(`${config.serverAddress}/users/all`); + + // Combine form data with user information + const combined = processedFormData.map((form) => { + const user = userInfoResponse.data.find((user) => user.id === form.userId); + return { + userId: user ? user.id : "Unknown User", + formId: form.userId, + name: user ? `${user.firstName} ${user.lastName}` : "Unknown Name", + avgBill: form.avgBill, + }; + }); + + // Sort combined data by avgBill in ascending order + combined.sort((a, b) => a.avgBill - b.avgBill); + + setCombinedData(combined); + } catch (error) { + console.log("Failed to fetch data"); + } + }; + + fetchData(); + }, []); + + const topUser = combinedData.length > 0 ? combinedData[0] : null; + const top5Users = combinedData.slice(1, 10); + return ( -
-
-
-
-

- Home Bill Contest -

+
+
+
+
+ + + + + +
-
-

- This contest is to encourage residents to reduce the use of - electricity and water usage. This contest would be won by the - person with the lowest overall bill average. Join us in this - important effort to create a more sustainable future for everyone.{" "} - - Participants would be required to input and upload their bills into the form to ensure integrity and honesty.{" "} - -

+
+
+

+ Home Bill Contest +

+
+
+

+ This contest is to encourage residents to reduce the use of + electricity and water usage. This contest would be won by the + person with the lowest overall bill average. Join us in this + important effort to create a more sustainable future for everyone.{" "} +

+
+
+

+ Winners +

+
+
+

+ There will be 3 winners for each month. Each winner will receive + random vouchers. +

+

+

1st Place → 3 vouchers

+

2nd Place → 2 vouchers

+

3rd Place → 1 voucher

+
+
+ +
-
-

- Winners -

-
-
-

- There will 3 winners for each month. Each winner will receive - random food vouchers. -

-

1st → 3 vouchers

-

2nd → 2 vouchers

-

3rd → 1 voucher

-
-
- -
-
-
-
+
+
+ + + + + Leaderboard + + {/* Add padding-bottom for white space */} +
+ {topUser && ( +
+ +

{topUser.name}

{/* Center text */} +
+ )} +
+
+ {top5Users.map((user, index) => ( +
+ {index + 2} + {user.name} +
+ ))} +
+
+
+
+ + {/* Info Modal */} + + + + Information + + +
+

Form Completion:

+
    + Participants must complete the online form, providing the following details +
  • Water bill amount
  • +
  • Electricity bill amount
  • +
  • Number of dependents
  • +
+ +

Document Upload

+
    +
  • Participants must upload clear, legible images of their water and electricity bills.
  • +
  • Both documents should clearly show the bill amount and date.
  • +
+ +

Data Accuracy

+
    +
  • All provided information must be accurate and truthful. Inaccurate or misleading information will result in disqualification.
  • +
  • All submitted bills and details will be subject to verification.
  • +
+ +

Privacy and Data Handling

+
    +
  • All personal data and bill images submitted will be handled in accordance with privacy regulations and will not be shared with third parties without consent.
  • +
  • Data will be used solely for contest purposes and to verify the validity of entries.
  • +
+
+
+
+
+ + + ); -} \ No newline at end of file +} diff --git a/client/src/pages/HBFormPage.tsx b/client/src/pages/HBFormPage.tsx index 96ba4a5..efb0f8d 100644 --- a/client/src/pages/HBFormPage.tsx +++ b/client/src/pages/HBFormPage.tsx @@ -139,12 +139,12 @@ export default function HBFormPage() { return (
-
+
-
+
{ }, electricalBill: { type: DataTypes.DECIMAL(7, 2), - allowNull: false + allowNull: false, }, waterBill: { type: DataTypes.DECIMAL(7, 2), - allowNull: false + allowNull: false, }, totalBill: { type: DataTypes.DECIMAL(7, 2), - allowNull: false + allowNull: false, }, noOfDependents: { type: DataTypes.INTEGER, - allowNull: false + allowNull: false, }, avgBill: { type: DataTypes.DECIMAL(7, 2), - allowNull: false + allowNull: false, }, ebPicture: { type: DataTypes.BLOB("long"), @@ -40,6 +40,7 @@ module.exports = (sequelize, DataTypes) => { }, userId: { type: DataTypes.UUID, + allowNull: false, }, }, {