2024-11-13 18:37:05 +05:30
|
|
|
import React, { useEffect, useState } from "react";
|
2024-11-07 12:05:27 +05:30
|
|
|
import { Bar } from "react-chartjs-2";
|
2024-11-12 23:24:50 +05:30
|
|
|
import {
|
|
|
|
|
Chart as ChartJS,
|
|
|
|
|
BarElement,
|
|
|
|
|
CategoryScale,
|
|
|
|
|
LinearScale,
|
|
|
|
|
} from "chart.js";
|
2024-11-12 21:22:05 +05:30
|
|
|
import { useMutation, useQuery } from "react-query";
|
2024-11-10 16:16:28 +05:30
|
|
|
import { useParams } from "react-router-dom";
|
|
|
|
|
import getPollData from "../services/getPollData";
|
2024-11-12 19:47:58 +05:30
|
|
|
import ErrorFallback from "../components/Errors/ErrorFallback";
|
2024-11-12 21:22:05 +05:30
|
|
|
import createVoteService from "../services/createVoteService";
|
|
|
|
|
import { FaBookmark } from "react-icons/fa";
|
2024-11-12 23:24:50 +05:30
|
|
|
import { toast } from "react-toastify";
|
|
|
|
|
import { makeChartDataObjFromPollData } from "../utils/util";
|
|
|
|
|
import useBookmark from "../hooks/useBookmark";
|
2024-11-13 18:37:05 +05:30
|
|
|
import { io } from "socket.io-client";
|
2024-11-07 12:05:27 +05:30
|
|
|
|
|
|
|
|
ChartJS.register(BarElement, CategoryScale, LinearScale);
|
|
|
|
|
|
|
|
|
|
function VotingPage() {
|
2024-11-10 16:16:28 +05:30
|
|
|
const { pollId } = useParams();
|
2024-11-13 18:37:05 +05:30
|
|
|
const [selectedOption, setSelectedOption] = useState(null);
|
|
|
|
|
const { handleBookmark } = useBookmark();
|
|
|
|
|
const [poll, setPoll] = useState(null);
|
|
|
|
|
const [socket, setSocket] = useState(null);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2024-11-13 21:22:46 +05:30
|
|
|
const s = io("https://livepoll-anjx.onrender.com");
|
2024-11-13 18:37:05 +05:30
|
|
|
setSocket(s);
|
|
|
|
|
|
|
|
|
|
s.on("connect", () => {
|
|
|
|
|
console.log("Connected to the server");
|
|
|
|
|
s.emit("joinPoll", pollId);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
s.disconnect();
|
|
|
|
|
};
|
|
|
|
|
}, [pollId]);
|
|
|
|
|
|
2024-11-12 23:24:50 +05:30
|
|
|
|
|
|
|
|
const {
|
2024-11-13 18:37:05 +05:30
|
|
|
data,
|
2024-11-12 23:24:50 +05:30
|
|
|
isLoading,
|
|
|
|
|
isError,
|
|
|
|
|
refetch,
|
|
|
|
|
} = useQuery(["poll", pollId], () => getPollData(pollId), {
|
2024-11-13 18:37:05 +05:30
|
|
|
cacheTime: 10 * 60 * 1000, // 10 minutes
|
|
|
|
|
staleTime: 20 * 60 * 1000, // 20 minutes
|
|
|
|
|
onSuccess: (data) => {
|
|
|
|
|
setPoll(data);
|
|
|
|
|
},
|
2024-11-10 16:16:28 +05:30
|
|
|
});
|
2024-11-07 12:05:27 +05:30
|
|
|
|
2024-11-13 18:37:05 +05:30
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (socket) {
|
|
|
|
|
socket.on("pollDataUpdated", (data) => {
|
|
|
|
|
console.log("Received updated poll data:", data);
|
|
|
|
|
setPoll(data);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
socket.on("error", (error) => {
|
|
|
|
|
console.error("Socket error:", error.message);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
socket.off("pollDataUpdated");
|
|
|
|
|
socket.off("error");
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}, [socket]);
|
|
|
|
|
|
2024-11-12 21:22:05 +05:30
|
|
|
const mutation = useMutation(createVoteService, {
|
|
|
|
|
onSuccess: (data) => {
|
2024-11-13 18:37:05 +05:30
|
|
|
toast.success("Vote submitted successfully");
|
|
|
|
|
if (socket) {
|
|
|
|
|
socket.emit("vote", { pollId, success: data?.success });
|
|
|
|
|
}
|
2024-11-12 21:22:05 +05:30
|
|
|
},
|
|
|
|
|
onError: (error) => {
|
2024-11-13 18:37:05 +05:30
|
|
|
console.error(error);
|
2024-11-12 23:24:50 +05:30
|
|
|
toast.error(
|
|
|
|
|
error?.response?.data?.message || "An unexpected error occurred"
|
|
|
|
|
);
|
2024-11-12 21:22:05 +05:30
|
|
|
},
|
2024-11-12 23:24:50 +05:30
|
|
|
});
|
2024-11-12 21:22:05 +05:30
|
|
|
|
|
|
|
|
const handleOptionSelect = (id) => {
|
2024-11-13 18:37:05 +05:30
|
|
|
setSelectedOption(id);
|
2024-11-12 21:22:05 +05:30
|
|
|
mutation.mutate({ pollId, optionId: id });
|
2024-11-07 12:05:27 +05:30
|
|
|
};
|
|
|
|
|
|
2024-11-12 19:47:58 +05:30
|
|
|
if (isLoading) {
|
|
|
|
|
return <div className="skeleton h-64 w-full max-w-lg mt-12 mx-auto"></div>;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-12 23:24:50 +05:30
|
|
|
if (isError) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="h-64 w-full max-w-lg mt-12 mx-auto">
|
|
|
|
|
<ErrorFallback onRetry={refetch} />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2024-11-12 19:47:58 +05:30
|
|
|
}
|
|
|
|
|
|
2024-11-13 18:37:05 +05:30
|
|
|
const chartData = makeChartDataObjFromPollData(poll);
|
|
|
|
|
|
|
|
|
|
|
2024-11-07 12:05:27 +05:30
|
|
|
return (
|
|
|
|
|
<div className="bg-base-200 min-h-screen p-6 text-white flex flex-col items-center">
|
2024-11-12 21:22:05 +05:30
|
|
|
<div className="w-full flex justify-between max-w-lg">
|
2024-11-12 23:24:50 +05:30
|
|
|
{/* Poll Creator Info */}
|
|
|
|
|
<div className="flex items-center justify-center gap-4 mb-6">
|
|
|
|
|
<img
|
|
|
|
|
src="https://via.placeholder.com/100"
|
|
|
|
|
alt={poll?.data?.creatorData?.username}
|
|
|
|
|
className="rounded-full h-7 md:h-10 w-7 md:w-10"
|
|
|
|
|
/>
|
|
|
|
|
<h2 className="text-lg md:text-xl font-semibold">
|
|
|
|
|
{poll?.data?.creatorData?.username || "Unknown"}
|
|
|
|
|
</h2>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* BookMark Button */}
|
|
|
|
|
<button className="btn btn-circle btn-neutral" onClick={() => handleBookmark(pollId)}>
|
|
|
|
|
<FaBookmark />
|
|
|
|
|
</button>
|
2024-11-12 21:22:05 +05:30
|
|
|
</div>
|
|
|
|
|
|
2024-11-07 12:05:27 +05:30
|
|
|
{/* Poll Title */}
|
2024-11-12 23:24:50 +05:30
|
|
|
<h1 className="text-xl md:text-3xl font-bold text-center">
|
|
|
|
|
{poll?.data?.pollData?.title || "Loading.."}
|
|
|
|
|
</h1>
|
2024-11-12 21:22:05 +05:30
|
|
|
|
|
|
|
|
{/* Poll Description */}
|
2024-11-12 23:24:50 +05:30
|
|
|
<p className="text-sm font-light md:text-base mb-6 text-center">
|
|
|
|
|
{poll?.data?.pollData?.description || "Loading.."}
|
|
|
|
|
</p>
|
2024-11-07 12:05:27 +05:30
|
|
|
|
|
|
|
|
{/* Voting Options */}
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 w-full max-w-lg mb-6">
|
2024-11-12 23:24:50 +05:30
|
|
|
{poll?.data?.pollData?.options.map((option) => (
|
2024-11-07 12:05:27 +05:30
|
|
|
<div
|
2024-11-12 21:22:05 +05:30
|
|
|
onClick={() => handleOptionSelect(option._id)}
|
2024-11-12 19:47:58 +05:30
|
|
|
key={option._id}
|
2024-11-12 23:24:50 +05:30
|
|
|
className={`md:p-4 p-2 ${
|
2024-11-13 18:37:05 +05:30
|
|
|
selectedOption == option._id ? "bg-blue-500" : "bg-base-100"
|
2024-11-12 23:24:50 +05:30
|
|
|
} rounded-lg shadow-md flex items-center justify-center cursor-pointer ${
|
2024-11-13 18:37:05 +05:30
|
|
|
selectedOption == option._id ? "outline" : "hover:bg-base-300"
|
2024-11-12 23:24:50 +05:30
|
|
|
} transition`}
|
2024-11-07 12:05:27 +05:30
|
|
|
>
|
2024-11-12 19:47:58 +05:30
|
|
|
<span className="text-lg">{option.name}</span>
|
2024-11-07 12:05:27 +05:30
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Chart Visualization */}
|
|
|
|
|
<div className="w-full max-w-lg">
|
2024-11-12 23:24:50 +05:30
|
|
|
<Bar
|
|
|
|
|
data={makeChartDataObjFromPollData(poll)}
|
|
|
|
|
options={{ responsive: true, maintainAspectRatio: false }}
|
|
|
|
|
/>
|
2024-11-07 12:05:27 +05:30
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default VotingPage;
|