Towncouncil filter on Leaderboard

This commit is contained in:
ZacTohZY
2024-08-02 02:37:20 +08:00
parent 6b340c30c4
commit 42cae27ec6
3 changed files with 61 additions and 48 deletions

View File

@@ -19,7 +19,7 @@ const InsertImage: React.FC<InsertImageProps> = ({ onImageSelected }) => {
return ( return (
<div <div
className="flex flex-col items-center p-5 bg-white dark:bg-zinc-800 rounded-md" className="flex flex-col items-center p-5 bg-white dark:bg-zinc-800 rounded-md"
style={{ width: 350, height: 500 }} style={{ width: 400, height: 500 }}
> >
<input <input
type="file" type="file"
@@ -30,7 +30,7 @@ const InsertImage: React.FC<InsertImageProps> = ({ onImageSelected }) => {
<img <img
src={previewImage} src={previewImage}
alt="Selected Image" alt="Selected Image"
className="w-full h-full object-cover rounded-md" className="w-full h-[410px] object-cover rounded-md"
/> />
)} )}
</div> </div>

View File

@@ -9,6 +9,7 @@ interface User {
id: string; id: string;
firstName: string; firstName: string;
lastName: string; lastName: string;
townCouncil: string;
} }
interface CombinedData { interface CombinedData {
@@ -16,6 +17,7 @@ interface CombinedData {
formId: string; formId: string;
name: string; name: string;
avgBill: number; avgBill: number;
townCouncil: string;
} }
export default function HBContestPage() { export default function HBContestPage() {
@@ -33,15 +35,11 @@ export default function HBContestPage() {
}; };
const [combinedData, setCombinedData] = useState<CombinedData[]>([]); const [combinedData, setCombinedData] = useState<CombinedData[]>([]);
const [filteredData, setFilteredData] = useState<CombinedData[]>([]);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const handleOpenModal = () => {
setIsModalOpen(true);
};
const [isInfoModalOpen, setIsInfoModalOpen] = useState(false); const [isInfoModalOpen, setIsInfoModalOpen] = useState(false);
const [townCouncils, setTownCouncils] = useState<string[]>([]);
const handleOpenInfoModal = () => setIsInfoModalOpen(true); const [selectedTownCouncil, setSelectedTownCouncil] = useState<string>("");
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
@@ -61,6 +59,7 @@ export default function HBContestPage() {
formId: form.userId, formId: form.userId,
name: user ? `${user.firstName} ${user.lastName}` : "Unknown Name", name: user ? `${user.firstName} ${user.lastName}` : "Unknown Name",
avgBill: form.avgBill, avgBill: form.avgBill,
townCouncil: user ? user.townCouncil : "Unknown Town Council",
}; };
}); });
@@ -68,6 +67,11 @@ export default function HBContestPage() {
combined.sort((a, b) => a.avgBill - b.avgBill); combined.sort((a, b) => a.avgBill - b.avgBill);
setCombinedData(combined); setCombinedData(combined);
setFilteredData(combined);
// Fetch town councils
const townCouncilsResponse = await instance.get(`${config.serverAddress}/users/town-councils-metadata`);
setTownCouncils(JSON.parse(townCouncilsResponse.data).townCouncils);
} catch (error) { } catch (error) {
console.log("Failed to fetch data"); console.log("Failed to fetch data");
} }
@@ -76,8 +80,16 @@ export default function HBContestPage() {
fetchData(); fetchData();
}, []); }, []);
const topUser = combinedData.length > 0 ? combinedData[0] : null; useEffect(() => {
const top5Users = combinedData.slice(1, 10); // Filter combined data based on selected town council
const filtered = combinedData.filter((data) =>
selectedTownCouncil ? data.townCouncil === selectedTownCouncil : true
);
setFilteredData(filtered);
}, [selectedTownCouncil, combinedData]);
const topUser = filteredData.length > 0 ? filteredData[0] : null;
const top5Users = filteredData.slice(1, 10);
return ( return (
<div className="flex flex-col items-center justify-center"> <div className="flex flex-col items-center justify-center">
@@ -85,12 +97,12 @@ export default function HBContestPage() {
<section className="bg-red-50 dark:bg-primary-950 border border-primary-100 p-10 rounded-xl w-full max-w-2xl flex flex-col items-center"> <section className="bg-red-50 dark:bg-primary-950 border border-primary-100 p-10 rounded-xl w-full max-w-2xl flex flex-col items-center">
<div className="w-full flex justify-end"> <div className="w-full flex justify-end">
<Tooltip content="Information"> <Tooltip content="Information">
<Button isIconOnly variant="light" onPress={handleOpenInfoModal}> <Button isIconOnly variant="light" onPress={() => setIsInfoModalOpen(true)}>
<InfoIcon /> <InfoIcon />
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip content="Leaderboard"> <Tooltip content="Leaderboard">
<Button isIconOnly variant="light" onPress={handleOpenModal}> <Button isIconOnly variant="light" onPress={() => setIsModalOpen(true)}>
<TrophyIcon /> <TrophyIcon />
</Button> </Button>
</Tooltip> </Tooltip>
@@ -142,19 +154,35 @@ export default function HBContestPage() {
<ModalHeader className="flex justify-center items-center font-bold text-2xl text-red-900"> <ModalHeader className="flex justify-center items-center font-bold text-2xl text-red-900">
<span>Leaderboard</span> <span>Leaderboard</span>
</ModalHeader> </ModalHeader>
<ModalBody className="pb-8"> {/* Add padding-bottom for white space */} <ModalBody className="pb-8">
<div className="flex flex-col gap-4"> <div className="mb-4">
{townCouncils.length > 0 && (
<select
value={selectedTownCouncil}
onChange={(e) => setSelectedTownCouncil(e.target.value)}
>
<option value="">All locations</option>
{townCouncils.map((townCouncil) => (
<option key={townCouncil} value={townCouncil}>
{townCouncil}
</option>
))}
</select>
)}
</div>
<div className="flex flex-col gap-2">
{topUser && ( {topUser && (
<div className="p-4 border rounded-md bg-red-100 flex items-center"> <div className="p-2 border rounded-md bg-red-100 dark:bg-primary-950 flex items-center">
<TrophyIcon /> <TrophyIcon />
<p className="text-lg flex-1 text-center font-bold">{topUser.name}</p> {/* Center text */} <p className="text-lg flex-1 text-center font-bold">{topUser.name}</p>
</div> </div>
)} )}
</div> </div>
<div className="grid grid-rows-1 md:grid-rows-2 gap-4"> <div className="grid grid-rows-1 md:grid-rows-2 gap-2">
{top5Users.map((user, index) => ( {top5Users.map((user, index) => (
<div key={user.userId} className="p-4 border rounded-md bg-red-100 flex items-center"> <div key={user.userId} className="p-3 border rounded-md bg-red-100 dark:bg-primary-950 flex items-center">
<span className="text-xl font-bold w-8">{index + 2}</span> <span className="text-lg font-bold w-8">{index + 2}</span>
<span className="flex-1 text-center">{user.name}</span> <span className="flex-1 text-center">{user.name}</span>
</div> </div>
))} ))}
@@ -178,35 +206,20 @@ export default function HBContestPage() {
<div className="space-y-4 text-gray-700 dark:text-gray-300"> <div className="space-y-4 text-gray-700 dark:text-gray-300">
<p className="font-semibold">Form Completion:</p> <p className="font-semibold">Form Completion:</p>
<ul className="list-disc list-inside pl-4"> <ul className="list-disc list-inside pl-4">
Participants must complete the online form, providing the following details Participants must fill out all required fields to complete the form.
<li>Water bill amount</li>
<li>Electricity bill amount</li>
<li>Number of dependents</li>
</ul> </ul>
<p className="font-semibold">Eligibility:</p>
<p className="font-semibold">Document Upload</p>
<ul className="list-disc list-inside pl-4"> <ul className="list-disc list-inside pl-4">
<li>Participants must upload clear, legible images of their water and electricity bills.</li> Residents must be registered with the town council to be eligible.
<li>Both documents should clearly show the bill amount and date.</li>
</ul> </ul>
<p className="font-semibold">Submission Deadline:</p>
<p className="font-semibold">Data Accuracy</p>
<ul className="list-disc list-inside pl-4"> <ul className="list-disc list-inside pl-4">
<li>All provided information must be accurate and truthful. Inaccurate or misleading information will result in disqualification.</li> Ensure to submit the form before the end of the month.
<li>All submitted bills and details will be subject to verification.</li>
</ul>
<p className="font-semibold">Privacy and Data Handling</p>
<ul className="list-disc list-inside pl-4">
<li>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.</li>
<li>Data will be used solely for contest purposes and to verify the validity of entries.</li>
</ul> </ul>
</div> </div>
</ModalBody> </ModalBody>
</ModalContent> </ModalContent>
</Modal> </Modal>
</div>
</div >
); );
} }

View File

@@ -139,12 +139,12 @@ export default function HBFormPage() {
return ( return (
<div className="w-full h-full"> <div className="w-full h-full">
<section className="w-7/12 mx-auto"> <section className="w-8/12 mx-auto p-6 bg-red-100 dark:bg-red-950 border border-primary-100 rounded-2xl h-600px">
<Button variant="light" onPress={() => navigate(-1)}> <div className="py-2">
<ArrowUTurnLeftIcon /> <Button variant="light" onPress={() => navigate(-1)}>
</Button> <ArrowUTurnLeftIcon />
</section> </Button>
<section className="w-7/12 mx-auto p-5 bg-red-100 dark:bg-red-950 border border-primary-100 rounded-2xl h-600px"> </div>
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
validationSchema={validationSchema} validationSchema={validationSchema}
@@ -170,7 +170,7 @@ export default function HBFormPage() {
<Form> <Form>
<div className="flex flex-col gap-5"> <div className="flex flex-col gap-5">
<div className="flex flex-row gap-10"> <div className="flex flex-row gap-10">
<div className="flex flex-col gap-10"> <div className="flex flex-col gap-5 p-1">
<NextUIFormikInput <NextUIFormikInput
label="Electrical Bill" label="Electrical Bill"
name="electricalBill" name="electricalBill"