Quiz page socket added and X winner answer fix
This commit is contained in:
parent
9c9a9f3779
commit
827c6f7897
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 ? (
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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[];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue