Quiz page socket added and X winner answer fix

This commit is contained in:
Kakabay 2024-09-09 16:27:10 +05:00
parent 9c9a9f3779
commit 827c6f7897
5 changed files with 260 additions and 59 deletions

View File

@ -133,9 +133,13 @@ const page = ({ params }: IParams) => {
/> />
) : null} ) : null}
{data?.data.id && quizFinished ? ( {data?.data.id && (
<QuizWinnerTable quizId={data?.data.id} quizFinished={quizFinished} /> <QuizWinnerTable
) : null} smsNumber={data.data.sms_number}
quizId={data?.data.id}
quizFinished={quizFinished}
/>
)}
</div> </div>
</QuizProvider> </QuizProvider>
</div> </div>

View File

@ -94,9 +94,16 @@ const page = () => {
/> />
) : null} ) : null}
{data?.data.id && quizFinished ? ( {/* {data?.data.id && quizFinished ? (
<QuizWinnerTable quizId={data?.data.id} quizFinished={quizFinished} /> <QuizWinnerTable quizId={data?.data.id} quizFinished={quizFinished} />
) : null} ) : null} */}
{data?.data.id && (
<QuizWinnerTable
smsNumber={data.data.sms_number}
quizId={data?.data.id}
quizFinished={quizFinished}
/>
)}
</div> </div>
</QuizProvider> </QuizProvider>
</div> </div>

View File

@ -25,6 +25,10 @@ const QuizQuestionList = ({
const { quizSearchData } = useContext(QuizContext).quizSearchContext; const { quizSearchData } = useContext(QuizContext).quizSearchContext;
const { setQuestionsData } = useContext(QuizContext).quizQuestionsContext; const { setQuestionsData } = useContext(QuizContext).quizQuestionsContext;
const [smsNumber, setSmsNumber] = useState<string>();
const [socket, setSocket] = useState<WebSocket | null>(null);
const [isConnected, setIsConnected] = useState(false);
// useEffect(() => { // useEffect(() => {
// Queries.getQuizQuestions().then((res) => setData(res)); // Queries.getQuizQuestions().then((res) => setData(res));
// data?.data.questions.map((question) => // data?.data.questions.map((question) =>
@ -56,48 +60,69 @@ const QuizQuestionList = ({
// ); // );
if (!dynamic && quizFinished === false) { if (!dynamic && quizFinished === false) {
const interval = setInterval(() => { // const interval = setInterval(() => {
Queries.getQuizQuestions().then((res) => { // Queries.getQuizQuestions().then((res) => {
setData(res); // setData(res);
setQuestionsData(res.data.questions); // setQuestionsData(res.data.questions);
res.data.questions.map((question) => // res.data.questions.map((question) =>
question.status === 'active' || question.status === 'new' // question.status === 'active' || question.status === 'new'
? setQuizFinished(false) // ? setQuizFinished(false)
: setQuizFinished(true), // : setQuizFinished(true),
); // );
}); // });
// const isActive = data?.data.questions.some( // // const isActive = data?.data.questions.some(
// (question) => question.status === 'active' || question.status === 'new', // // (question) => question.status === 'active' || question.status === 'new',
// ); // // );
// data.data.questions.map((question) => // // data.data.questions.map((question) =>
// question.status === 'active' || question.status === 'new' // // question.status === 'active' || question.status === 'new'
// ? setQuizFinished(false) // // ? setQuizFinished(false)
// : setQuizFinished(true), // // : setQuizFinished(true),
// ); // // );
}, 60000); // }, 60000);
return () => clearInterval(interval); // return () => clearInterval(interval);
Queries.getQuizQuestions().then((res) => {
setData(res);
setQuestionsData(res.data.questions);
setSmsNumber(res.data.sms_number);
res.data.questions.map((question) =>
question.status === 'active' || question.status === 'new'
? setQuizFinished(false)
: setQuizFinished(true),
);
});
} else { } else {
const interval = setInterval(() => { // const interval = setInterval(() => {
Queries.getQuiz(id).then((res) => { // Queries.getQuiz(id).then((res) => {
setData(res); // setData(res);
setQuestionsData(res.data.questions); // setQuestionsData(res.data.questions);
res.data.questions.map((question) => // res.data.questions.map((question) =>
question.status === 'active' || question.status === 'new' // question.status === 'active' || question.status === 'new'
? setQuizFinished(false) // ? setQuizFinished(false)
: setQuizFinished(true), // : setQuizFinished(true),
); // );
}); // });
}, 60000); // }, 60000);
return () => clearInterval(interval); // return () => clearInterval(interval);
Queries.getQuiz(id).then((res) => {
setData(res);
setQuestionsData(res.data.questions);
setSmsNumber(res.data.sms_number);
res.data.questions.map((question) =>
question.status === 'active' || question.status === 'new'
? setQuizFinished(false)
: setQuizFinished(true),
);
});
} }
}, [quizFinished]); }, [quizFinished]);
console.log(data);
return ( return (
<div className="flex flex-col gap-[40px] md:gap-[160px]"> <div className="flex flex-col gap-[40px] md:gap-[160px]">
{data && !quizSearchData ? ( {data && !quizSearchData ? (

View File

@ -2,38 +2,192 @@
import { Queries } from '@/api/queries'; import { Queries } from '@/api/queries';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { useState, useEffect, useContext } from 'react'; import { useState, useEffect, useContext, Dispatch, SetStateAction } from 'react';
import { IQuizQuestionsWinners } from '@/models/quizQuestionsWinners.model'; import { IQuizQuestionsWinners } from '@/models/quizQuestionsWinners.model';
import QuizContext from '@/context/QuizContext'; import QuizContext from '@/context/QuizContext';
import { IQuizQuestions } from '@/models/quizQuestions.model';
interface Message {
answer: string;
score: number;
date: string;
serial_number: number;
serial_number_for_correct: number;
starred_src: string;
quiz_id: number;
question_id: number;
}
interface Winner {
total_score_of_client: string;
correct_answers_time: string;
client_id: number;
client: {
id: number;
phone: string;
answers: {
id: number;
question_id: number;
score: number;
serial_number_for_correct: number;
client_id: number;
}[];
};
}
interface IProps { interface IProps {
quizId: number; quizId: number;
quizFinished: boolean; quizFinished: boolean;
smsNumber: string;
} }
const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => { const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
// const [questionsData, setQuestionsData] = useState<IQuizQuestions>(); // const [questionsData, setQuestionsData] = useState<IQuizQuestions>();
const [winnersData, setWinnersData] = useState<IQuizQuestionsWinners>(); const [winnersData, setWinnersData] = useState<IQuizQuestionsWinners>();
const { questionsData } = useContext(QuizContext).quizQuestionsContext; const { questionsData } = useContext(QuizContext).quizQuestionsContext;
useEffect(() => { console.log('Questions:', questionsData);
if (quizFinished) {
// Queries.getQuizQuestions().then((res) => {
// setQuestionsData(res);
// });
Queries.getQuizWinners(quizId).then((res) => {
setWinnersData(res);
});
}
}, []);
// const { data, error, isFetching } = useQuery({
// queryKey: ['quiz_questions_winners'],
// queryFn: () => Queries.getQuizWinners(quizId),
// keepPreviousData: true,
// });
// if (isFetching) return <Loader />; const [socket, setSocket] = useState<WebSocket | null>(null);
// if (error) return <h1>{JSON.stringify(error)}</h1>; const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
Queries.getQuizWinners(quizId).then((res) => {
setWinnersData(res);
console.log(res);
});
}, [quizId]);
useEffect(() => {
let socket: WebSocket | null = null;
let reconnectTimeout: NodeJS.Timeout | null = null;
let pingInterval: NodeJS.Timeout | null = null;
const connectWebSocket = () => {
try {
socket = new WebSocket(`wss://sms.turkmentv.gov.tm/ws/quiz?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);
}
}
}, 25000); // Ping every 25 seconds
};
socket.onmessage = (event) => {
try {
console.log('Message received from WebSocket:', event.data);
const message = JSON.parse(event.data);
handleOnMessage(message);
} catch (error) {
console.error('Error processing message:', error);
}
};
socket.onerror = (error) => {
console.error('WebSocket error:', error);
};
socket.onclose = () => {
console.log('WebSocket is closed');
setIsConnected(false);
if (pingInterval) {
clearInterval(pingInterval);
}
if (!reconnectTimeout) {
reconnectTimeout = setTimeout(() => {
console.log('Attempting to reconnect WebSocket...');
connectWebSocket();
}, 5000); // Reconnect after 5 seconds
}
};
} catch (error) {
console.error('WebSocket connection error:', error);
}
};
if (smsNumber && winnersData) {
connectWebSocket();
}
return () => {
if (socket) {
socket.close();
}
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
}
if (pingInterval) {
clearInterval(pingInterval);
}
};
}, [smsNumber]);
// Function to handle incoming WebSocket message and update winnersData
const handleOnMessage = (message: Message) => {
if (!winnersData) {
console.error('winnersData is undefined');
return;
}
console.log('updating winnersData');
// Update the winnersData by matching phone with starred_src from the message
setWinnersData((prevWinnersData) => {
if (!prevWinnersData) {
return prevWinnersData;
}
return {
...prevWinnersData,
data: prevWinnersData.data.map((winner) => {
if (winner.client.phone === message.starred_src) {
const updatedAnswers = [
...winner.client.answers,
{
id: message.question_id,
question_id: message.question_id,
score: message.score,
serial_number_for_correct: message.serial_number_for_correct,
client_id: winner.client.id,
},
];
// Calculate the new correct_answers_time by summing serial_number_for_correct
const updatedCorrectAnswersTime = updatedAnswers
.reduce((sum, answer) => sum + answer.serial_number_for_correct, 0)
.toString();
return {
...winner,
client: {
...winner.client,
answers: updatedAnswers,
},
total_score_of_client: (
parseInt(winner.total_score_of_client) + message.score
).toString(),
correct_answers_time: updatedCorrectAnswersTime, // Update correct_answers_time
};
}
return winner;
}),
};
});
console.log('winnersData is updated');
};
return winnersData?.data.length !== 0 ? ( return winnersData?.data.length !== 0 ? (
<div className="flex flex-col justify-center items-center gap-[60px]"> <div className="flex flex-col justify-center items-center gap-[60px]">
@ -41,7 +195,10 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
<h2 className="text-textBlack text-[28px] text-center md:text-left md:text-[32px] font-semibold"> <h2 className="text-textBlack text-[28px] text-center md:text-left md:text-[32px] font-semibold">
Bäsleşigiň jemi Bäsleşigiň jemi
</h2> </h2>
{/* Desktop table */}
<div className="table-desktop hidden sm:flex flex-col bg-fillTableHead rounded-[25px] shadow-quizButton overflow-hidden max-w-[1000px] w-full"> <div className="table-desktop hidden sm:flex flex-col bg-fillTableHead rounded-[25px] shadow-quizButton overflow-hidden max-w-[1000px] w-full">
{/* Table Head */}
<div className="flex border-b border-fillTableStrokeTableHead"> <div className="flex border-b border-fillTableStrokeTableHead">
{winnersData?.data[0].client_id ? ( {winnersData?.data[0].client_id ? (
<div className="text-center flex justify-center items-center text-base text-textBlack leading-[125%] font-semibold max-w-[54px] w-[100%] pl-6 pr-3 py-5"> <div className="text-center flex justify-center items-center text-base text-textBlack leading-[125%] font-semibold max-w-[54px] w-[100%] pl-6 pr-3 py-5">
@ -73,6 +230,7 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
) : null} ) : null}
</div> </div>
{/* Table Body */}
<div className=""> <div className="">
{winnersData?.data.map((winner, id) => ( {winnersData?.data.map((winner, id) => (
<div <div
@ -93,8 +251,9 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
{questionsData {questionsData
? questionsData.map((question) => { ? questionsData.map((question) => {
const matchingAnswer = winner.client.answers.find( const matchingAnswer = winner.client.answers.find(
(answer) => answer.question_id === question.id, (answer) => answer.question_id === question.id && answer.score !== 0,
); );
return ( return (
<span <span
key={v4()} key={v4()}
@ -137,7 +296,9 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
</div> </div>
</div> </div>
{/* Mobile table */}
<div className="sm:hidden flex flex-col bg-fillTableHead rounded-[13px] shadow-quizButton overflow-hidden max-w-[1000px] w-full"> <div className="sm:hidden flex flex-col bg-fillTableHead rounded-[13px] shadow-quizButton overflow-hidden max-w-[1000px] w-full">
{/* Table Head */}
<div className="flex border-b border-fillTableStrokeTableHead p-2 gap-[8px]"> <div className="flex border-b border-fillTableStrokeTableHead p-2 gap-[8px]">
{winnersData?.data[0].client_id ? ( {winnersData?.data[0].client_id ? (
<div className="text-center flex items-center text-xs text-textBlack leading-[125%] font-semibold max-w-[14px] w-[100%]"> <div className="text-center flex items-center text-xs text-textBlack leading-[125%] font-semibold max-w-[14px] w-[100%]">
@ -163,6 +324,7 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
) : null} ) : null}
</div> </div>
{/* Table Body */}
<div className=""> <div className="">
{winnersData?.data.map((winner, id) => ( {winnersData?.data.map((winner, id) => (
<div <div
@ -240,6 +402,8 @@ const QuizWinnerTable = ({ quizId, quizFinished }: IProps) => {
</div> </div>
</div> </div>
</div> </div>
{/* Rules block */}
<div className="flex flex-col gap-[20px] p-5 border border-strokeLightGray1 rounded-[25px] max-w-[1000px] w-full items-center justify-center"> <div className="flex flex-col gap-[20px] p-5 border border-strokeLightGray1 rounded-[25px] max-w-[1000px] w-full items-center justify-center">
<h3 className="text-[26px] text-textBlack font-semibold leading-[124%]"> <h3 className="text-[26px] text-textBlack font-semibold leading-[124%]">
Belgileriň düşündirilişi Belgileriň düşündirilişi

View File

@ -8,6 +8,7 @@ export interface Data {
title: string; title: string;
date: string; date: string;
banner: string; banner: string;
sms_number: string;
banner_mobile: string; banner_mobile: string;
description: string; description: string;
rules: Note[]; rules: Note[];