turkmentv_front/components/vote/ParticipantsList.tsx

407 lines
14 KiB
TypeScript
Raw Normal View History

2024-10-17 13:31:55 +00:00
'use client';
import React, { useContext, useEffect, useState } from 'react';
import GradientTitle from './GradientTitle';
import ParticipantCard from './ParticipantCard';
import { v4 } from 'uuid';
import { IAllVotes, VotingItem } from '@/models/allVotes.model';
import { Queries } from '@/api/queries';
import Loader from '../Loader';
import VoteContext from '@/context/VoteContext';
import PageBage from './PageBage';
import Image from 'next/image';
import { useMediaQuery } from 'usehooks-ts';
import Countdown from './Countdown';
import Link from 'next/link';
import { useWindowSize } from 'react-use';
import Confetti from '../common/Confetti';
2024-08-19 12:44:56 +00:00
interface IParams {
vote_id?: string;
}
2024-09-03 12:35:46 +00:00
interface ISocketMessage {
voting_id: number;
voting_item_id: number;
client_id: number;
message: string;
date: string;
}
2024-08-19 12:44:56 +00:00
const ParticipantsList = ({ vote_id }: IParams) => {
const [data, setData] = useState<IAllVotes>();
2024-09-03 12:35:46 +00:00
const [participantsData, setParticipantsData] = useState<VotingItem[]>([]);
2024-08-19 12:44:56 +00:00
const [voteStatus, setVoteStatus] = useState<string>();
const [eventStatus, setEventStatus] = useState<string>('Not started');
2024-10-17 13:31:55 +00:00
const [manualClose, setManualClose] = useState(false); // Track manual closure
2024-08-19 12:44:56 +00:00
const [winnersCount, setWinnersCount] = useState<number>(0);
2024-09-03 12:35:46 +00:00
// States realted to web socket
const [smsNumber, setSmsNumber] = useState<string | null>(null);
const [socket, setSocket] = useState<WebSocket | null>(null);
const [isConnected, setIsConnected] = useState(false);
2024-10-17 13:31:55 +00:00
const mobile = useMediaQuery('(max-width: 768px)');
const { width, height } = useWindowSize();
2024-08-19 12:44:56 +00:00
const { setVoteDescription } = useContext(VoteContext).voteDescriptionContext;
useEffect(() => {
if (!vote_id) {
Queries.getAllVotes().then((res) => {
setData(res);
2024-09-03 12:35:46 +00:00
setParticipantsData([...res.data.voting_items]);
2024-08-19 12:44:56 +00:00
setVoteDescription(res.data.description);
setVoteStatus(res.data.status);
2024-09-03 12:35:46 +00:00
setSmsNumber(res.data.sms_number);
2024-08-19 12:44:56 +00:00
});
} else {
Queries.getVote(vote_id).then((res) => {
setData(res);
setParticipantsData(res.data.voting_items);
setVoteDescription(res.data.description);
setVoteStatus(res.data.status);
2024-09-03 12:35:46 +00:00
setSmsNumber(res.data.sms_number);
2024-08-19 12:44:56 +00:00
});
}
2024-09-03 12:35:46 +00:00
if (participantsData) {
winnersCountHandle(participantsData);
2024-08-19 12:44:56 +00:00
}
2024-09-03 12:35:46 +00:00
}, []);
2024-08-19 12:44:56 +00:00
2024-09-03 12:35:46 +00:00
useEffect(() => {
let socket: WebSocket | null = null;
let reconnectTimeout: NodeJS.Timeout | null = null;
let pingInterval: NodeJS.Timeout | null = null;
2024-08-19 12:44:56 +00:00
2024-09-03 12:35:46 +00:00
const connectWebSocket = () => {
try {
2024-10-17 13:31:55 +00:00
// Only connect if manualClose is false
if (!manualClose) {
socket = new WebSocket(`wss://sms.turkmentv.gov.tm/ws/voting?dst=${smsNumber}`);
setSocket(socket);
socket.onopen = () => {
console.log('WebSocket is connected');
setIsConnected(true);
pingInterval = setInterval(() => {
if (socket?.readyState === WebSocket.OPEN) {
try {
socket.send(JSON.stringify({ type: 'ping' }));
} catch (error) {
console.error('Error sending ping:', error);
}
2024-09-03 12:35:46 +00:00
}
2024-10-17 13:31:55 +00:00
}, 25000); // Ping every 25 seconds
};
socket.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
handleWebSocketMessage(message);
} catch (error) {
console.error('Error processing message:', error);
}
};
socket.onerror = (error) => {
console.error('WebSocket error:', error);
if (!manualClose && !reconnectTimeout) {
reconnectTimeout = setTimeout(() => {
console.log('Attempting to reconnect WebSocket after error...');
connectWebSocket();
}, 5000); // Reconnect after 5 seconds
2024-09-03 12:35:46 +00:00
}
2024-10-17 13:31:55 +00:00
};
socket.onclose = () => {
console.log('WebSocket is closed');
setIsConnected(false);
if (pingInterval) {
clearInterval(pingInterval);
}
// Clean up resources on close
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
}
};
}
2024-09-03 12:35:46 +00:00
} catch (error) {
2024-10-17 13:31:55 +00:00
console.error('WebSocket connection error:', error);
2024-09-03 12:35:46 +00:00
}
};
2024-10-17 13:31:55 +00:00
// WebSocket connection only if eventStatus is 'Started'
if (smsNumber && eventStatus === 'Started' && !manualClose) {
2024-09-03 12:35:46 +00:00
connectWebSocket();
2024-08-19 12:44:56 +00:00
}
2024-09-03 12:35:46 +00:00
return () => {
if (socket) {
2024-10-17 13:31:55 +00:00
setManualClose(true); // Mark it as a manual close
2024-09-03 12:35:46 +00:00
socket.close();
}
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
}
if (pingInterval) {
clearInterval(pingInterval);
}
};
2024-10-17 13:31:55 +00:00
}, [smsNumber, eventStatus, manualClose]); // Add manualClose to dependencies
2024-09-03 12:35:46 +00:00
const handleWebSocketMessage = (message: ISocketMessage) => {
setParticipantsData((prevVotingItems) => {
if (!prevVotingItems) return [];
// Update the corresponding voting item
const updatedItems = prevVotingItems.map((item, index) =>
2024-10-17 13:31:55 +00:00
item.id === message.voting_item_id ? { ...item, votes_count: item.votes_count + 1 } : item,
2024-09-03 12:35:46 +00:00
);
// Sort the updated items array by votes_count in descending order
return updatedItems.sort((a, b) => b.votes_count - a.votes_count);
});
};
const addVotes = () => {
setParticipantsData((prevVotingItems) => {
if (!prevVotingItems) return [];
// Update the corresponding voting item
const updatedItems = prevVotingItems.map((item, index) =>
2024-10-17 13:31:55 +00:00
index === 1 ? { ...item, votes_count: item.votes_count + 1 } : item,
2024-09-03 12:35:46 +00:00
);
2024-10-17 13:31:55 +00:00
console.log('votes updated');
2024-09-10 12:36:03 +00:00
console.log(updatedItems.sort((a, b) => b.votes_count - a.votes_count));
2024-09-03 12:35:46 +00:00
// Sort the updated items array by votes_count in descending order
return updatedItems.sort((a, b) => b.votes_count - a.votes_count);
});
};
2024-08-19 12:44:56 +00:00
const winnersCountHandle = (winners: VotingItem[]) => {
let count = 0;
winners.map((winner) => {
2024-10-17 13:31:55 +00:00
if (winner.votes_percents === 100 && winner.votes_count === winners[0].votes_count) {
2024-08-19 12:44:56 +00:00
count++;
setWinnersCount(count);
}
});
return count;
};
2024-09-19 12:17:04 +00:00
const hasVotes = participantsData.some((item) => item.votes_count > 0);
2024-08-19 12:44:56 +00:00
if (data) {
if (!data?.data) {
return (
<div className="py-12">
2024-10-17 13:31:55 +00:00
<GradientTitle title={'No voting to show on the site'} size="big" />
2024-08-19 12:44:56 +00:00
</div>
);
}
return (
<div className="flex flex-col gap-[20px] sm:gap-[40px] w-full items-center">
2024-10-17 13:31:55 +00:00
{data.data.description ? <PageBage title={data.data.description} /> : null}
2024-08-19 12:44:56 +00:00
2025-02-10 11:46:43 +00:00
{eventStatus === 'Finished' && <Confetti />}
2024-08-19 12:44:56 +00:00
{data.data.banner ? (
<div className="relative w-full md:min-h-[150px] md:h-auto h-[100px] ">
{mobile ? (
<Image
fill
2024-10-17 13:31:55 +00:00
src={data.data.banner_mobile !== null ? data.data.banner_mobile : data.data.banner}
2024-08-19 12:44:56 +00:00
alt={data.data.title}
unselectable="off"
unoptimized
className="rounded-[40px]"
/>
) : (
<Image
fill
src={data.data.banner}
alt={data.data.title}
unselectable="off"
unoptimized
className="rounded-[40px]"
/>
)}
</div>
) : null}
<div className="flex flex-col gap-[10px] sm:gap-[20px] w-full items-center">
{/* {data.data.title ? <GradientTitle title={data.data.title} size="big" /> : null} */}
{data.data.ends_at && data.data.starts_at ? (
2024-09-03 12:35:46 +00:00
<Countdown
endsAt={data.data.ends_at}
startsAt={data.data.starts_at}
setVoteStatus={setVoteStatus}
2024-10-17 13:31:55 +00:00
setEventStatus={setEventStatus}
eventStatus={eventStatus}
2024-09-03 12:35:46 +00:00
/>
2024-08-19 12:44:56 +00:00
) : null}
<div className="flex w-full flex-col items-center gap-[10px] sm:gap-[20px]">
2024-10-17 13:31:55 +00:00
{winnersCount > 1 ? <GradientTitle title="победители" size="small" /> : null}
2024-08-19 12:44:56 +00:00
2024-09-03 12:35:46 +00:00
{participantsData && participantsData[0].votes_count > 0 ? (
2024-08-19 12:44:56 +00:00
<div className="flex flex-col items-center overflow-hidden bg-fillNavyBlue rounded-[10px] sm:rounded-[30px] max-w-[940px] w-full px-[5px] py-[20px] sm:p-[20px] sm:gap-[20px] gap-[10px]">
2024-09-10 12:36:03 +00:00
{participantsData.map((participant, index) =>
2024-10-17 13:31:55 +00:00
participant.votes_count === participantsData[0].votes_count ? (
2024-09-04 11:08:37 +00:00
participant.url ? (
<Link
2024-10-17 13:31:55 +00:00
href={participant.url ? participant.url : ''}
2024-09-04 11:08:37 +00:00
target="_blank"
2024-10-15 07:11:25 +00:00
className="w-full"
2024-10-17 13:31:55 +00:00
key={v4()}>
2024-09-04 11:08:37 +00:00
<ParticipantCard
2024-09-10 12:36:03 +00:00
index={index}
2024-09-05 11:31:36 +00:00
hasUrl={true}
2024-10-17 13:31:55 +00:00
voteStatus={voteStatus ? voteStatus : ''}
2024-09-10 12:36:03 +00:00
isFirst={index === 0 ? true : false}
2024-09-04 11:08:37 +00:00
name={participant.title}
progress={participant.votes_percents}
votes={participant.votes_count}
voteCode={participant.vote_code}
2024-09-10 12:36:03 +00:00
number={index + 1}
2024-09-04 11:08:37 +00:00
photo={participant.photo}
smsNumber={data.data.sms_number}
winner={true}
/>
</Link>
) : (
2024-09-03 12:35:46 +00:00
<ParticipantCard
key={v4()}
2024-09-10 12:36:03 +00:00
index={index}
2024-09-05 11:31:36 +00:00
hasUrl={false}
2024-10-17 13:31:55 +00:00
voteStatus={voteStatus ? voteStatus : ''}
2024-09-10 12:36:03 +00:00
isFirst={index === 0 ? true : false}
2024-09-03 12:35:46 +00:00
name={participant.title}
progress={participant.votes_percents}
votes={participant.votes_count}
voteCode={participant.vote_code}
2024-09-10 12:36:03 +00:00
number={index + 1}
2024-09-03 12:35:46 +00:00
photo={participant.photo}
smsNumber={data.data.sms_number}
winner={true}
/>
2024-09-04 11:08:37 +00:00
)
2024-10-17 13:31:55 +00:00
) : null,
2024-08-19 12:44:56 +00:00
)}
</div>
) : null}
2024-10-17 13:31:55 +00:00
{winnersCount > 1 ? <div className="w-full h-[1px] bg-[#3636A3]"></div> : null}
2024-08-19 12:44:56 +00:00
</div>
2024-09-10 12:36:03 +00:00
2024-09-04 11:08:37 +00:00
<div className="flex flex-col items-center max-w-[940px] w-full gap-5 justify-center mx-auto">
{participantsData
2024-09-10 12:36:03 +00:00
? participantsData.map((participant, index) =>
2024-09-19 12:17:04 +00:00
!hasVotes ? (
2024-09-04 11:08:37 +00:00
participant.url ? (
<Link
2024-10-17 13:31:55 +00:00
href={participant.url ? participant.url : ''}
2024-09-04 11:08:37 +00:00
target="_blank"
2024-10-15 07:11:25 +00:00
className="w-full mx-auto"
2024-10-17 13:31:55 +00:00
key={v4()}>
2024-09-04 11:08:37 +00:00
<ParticipantCard
2024-09-10 12:36:03 +00:00
index={index}
2024-09-05 11:31:36 +00:00
hasUrl={true}
2024-10-17 13:31:55 +00:00
voteStatus={voteStatus ? voteStatus : ''}
2024-09-10 12:36:03 +00:00
isFirst={index === 0 ? true : false}
2024-09-04 11:08:37 +00:00
name={participant.title}
progress={participant.votes_percents}
votes={participant.votes_count}
voteCode={participant.vote_code}
2024-09-10 12:36:03 +00:00
number={index + 1}
2024-09-04 11:08:37 +00:00
photo={participant.photo}
smsNumber={data.data.sms_number}
winner={false}
/>
</Link>
) : (
<ParticipantCard
2024-09-05 11:31:36 +00:00
hasUrl={false}
2024-09-04 11:08:37 +00:00
key={v4()}
2024-09-10 12:36:03 +00:00
index={index}
2024-10-17 13:31:55 +00:00
voteStatus={voteStatus ? voteStatus : ''}
2024-09-10 12:36:03 +00:00
isFirst={index === 0 ? true : false}
2024-09-04 11:08:37 +00:00
name={participant.title}
progress={participant.votes_percents}
votes={participant.votes_count}
voteCode={participant.vote_code}
2024-09-10 12:36:03 +00:00
number={index + 1}
2024-09-04 11:08:37 +00:00
photo={participant.photo}
smsNumber={data.data.sms_number}
winner={false}
/>
)
2024-09-19 12:17:04 +00:00
) : (
2024-10-17 13:31:55 +00:00
participant.votes_count !== participantsData[0].votes_count &&
2024-09-19 12:17:04 +00:00
(participant.url ? (
<Link
2024-10-17 13:31:55 +00:00
href={participant.url ? participant.url : ''}
2024-09-19 12:17:04 +00:00
target="_blank"
2024-10-15 07:11:25 +00:00
className="w-full mx-auto"
2024-10-17 13:31:55 +00:00
key={v4()}>
2024-09-19 12:17:04 +00:00
<ParticipantCard
index={index}
hasUrl={true}
2024-10-17 13:31:55 +00:00
voteStatus={voteStatus ? voteStatus : ''}
2024-09-19 12:17:04 +00:00
isFirst={index === 0 ? true : false}
name={participant.title}
progress={participant.votes_percents}
votes={participant.votes_count}
voteCode={participant.vote_code}
number={index + 1}
photo={participant.photo}
smsNumber={data.data.sms_number}
winner={false}
/>
</Link>
) : (
<ParticipantCard
hasUrl={false}
key={v4()}
index={index}
2024-10-17 13:31:55 +00:00
voteStatus={voteStatus ? voteStatus : ''}
2024-09-19 12:17:04 +00:00
isFirst={index === 0 ? true : false}
name={participant.title}
progress={participant.votes_percents}
votes={participant.votes_count}
voteCode={participant.vote_code}
number={index + 1}
photo={participant.photo}
smsNumber={data.data.sms_number}
winner={false}
/>
))
2024-10-17 13:31:55 +00:00
),
2024-09-04 11:08:37 +00:00
)
: null}
</div>
2024-08-19 12:44:56 +00:00
</div>
</div>
);
} else {
return (
<main className="h-full py-[100px]">
<div className="container">
<Loader />
</div>
</main>
);
}
};
export default ParticipantsList;