quiz/[id]/results: ui changes

This commit is contained in:
Ilgeldi 2025-03-20 15:51:51 +05:00
parent 94a4df4ff2
commit 3b06c9baa9
5 changed files with 160 additions and 199 deletions

View File

@ -2,9 +2,11 @@
import { Queries } from "@/api/queries";
import QuizHeader from "@/components/quiz/QuizHeader";
import QuizResultsSearch from "@/components/quiz/QuizResultsSearch";
import QuizResultsTabs from "@/components/quiz/QuizResultsTabs";
import QuizTapgyrResults from "@/components/quiz/QuizTapgyrResults";
import QuizTapgyrWinners from "@/components/quiz/QuizTapgyrWinners";
import { Data } from "@/models/quizQuestions.model";
import { useQuizResults } from "@/store/store";
import { notFound } from "next/navigation";
import React, { useEffect, useState } from "react";
@ -16,35 +18,46 @@ interface IParams {
const Page = ({ params }: IParams) => {
const [data, setData] = useState<Data>();
const [tab, setTab] = useState<number | string>(0);
const { resultData, error } = useQuizResults();
useEffect(() => {
if (!resultData.length && !error) {
Queries.getQuizById(params.quiz_id)
.then((res) => setData(res.data))
.catch(() => notFound());
}, []);
}
}, [resultData, error]);
return (
<section className="container py-[40px]">
<div className="flex flex-col w-full py-[40px] gap-[80px]">
<QuizHeader data={data} />
<QuizResultsSearch id={params.quiz_id} />
<QuizResultsTabs
steps={data?.steps ? data?.steps : []}
tab={tab}
setStep={setTab}
/>
{tab === "results" && (
<QuizTapgyrResults
id={params.quiz_id}
steps={
data?.steps ? data?.steps?.map((item) => String(item.tapgyr)) : []
}
/>
)}
{data?.has_steps &&
data.steps &&
data.steps?.length > 0 &&
data.steps.map((step) => (
tab !== "results" && (
<QuizTapgyrWinners
key={step.tapgyr}
key={data.steps[Number(tab)].tapgyr}
id={params.quiz_id}
tapgyr={step.tapgyr}
questions={step.questions}
tapgyr={data.steps[Number(tab)].tapgyr}
questions={data.steps[Number(tab)].questions}
/>
))}
)}
</div>
</section>
);

View File

@ -1,8 +1,8 @@
import React, { useContext } from 'react';
import QuizAccordion from './QuizAccordion';
import { Validator } from '@/utils/validator';
import QuizContext from '@/context/QuizContext';
import { v4 } from 'uuid';
import React, { useContext } from "react";
import QuizAccordion from "./QuizAccordion";
import { Validator } from "@/utils/validator";
import QuizContext from "@/context/QuizContext";
import { v4 } from "uuid";
type TProps = {
finished: string;
@ -15,26 +15,26 @@ type TProps = {
};
const numbers = [
'Birinji sowal',
'Ikinji sowal',
'Üçünji sowal',
'Dördünji sowal',
'Bäşinji sowal',
'Altynjy sowal',
'Ýedinji sowal',
'Sekizinji sowal',
'Dokuzynjy sowal',
'Onunjy sowal',
'On Birinji sowal',
'On Ikinji sowal',
'On Üçünji sowal',
'On Dördünji sowal',
'On Bäşinji sowal',
'On Altynji sowal',
'On Ýeddi sowal',
'On Sekiznji sowal',
'On Dokuzunji sowal',
'Ýigrinci sowal',
"Birinji sowal",
"Ikinji sowal",
"Üçünji sowal",
"Dördünji sowal",
"Bäşinji sowal",
"Altynjy sowal",
"Ýedinji sowal",
"Sekizinji sowal",
"Dokuzynjy sowal",
"Onunjy sowal",
"On Birinji sowal",
"On Ikinji sowal",
"On Üçünji sowal",
"On Dördünji sowal",
"On Bäşinji sowal",
"On Altynji sowal",
"On Ýeddi sowal",
"On Sekiznji sowal",
"On Dokuzunji sowal",
"Ýigrinci sowal",
];
const QuizQuestion = ({
@ -51,20 +51,21 @@ const QuizQuestion = ({
Object.values(quizSearchData.data).map((userQuestion, id) =>
userQuestion.question_id === questionId ? (
<div className="flex flex-col gap-[20px]" key={v4()}>
<div className="flex flex-col gap-[30px] max-w-[700px]">
<div className="flex flex-col gap-[30px]">
<div className="flex flex-col gap-[20px] md:gap-[10px]">
<h2 className="text-[24px] md:text-[32px] leading-[100%] font-semibold text-textBlack text-center md:text-left">
{numbers.map((number, id) => (id === questionNumber ? number : ''))}:
<h2 className="text-[24px] md:text-[32px] leading-[120%] font-semibold text-textBlack">
{question}
</h2>
<div className="flex flex-wrap justify-center md:justify-start md:gap-5 gap-[10px]">
<div className="flex gap-2 items-center">
<div
className={`w-[18px] h-[18px] md:w-4 md:h-4 ${
finished === 'closed' ? 'bg-fillRed' : 'bg-fillGreen'
} rounded-full`}></div>
finished === "closed" ? "bg-fillRed" : "bg-fillGreen"
} rounded-full`}
></div>
<span className="text-textLight text-sm md:text-base">
{finished === 'closed' ? 'ýapyk' : 'açyk'}
{finished === "closed" ? "ýapyk" : "açyk"}
</span>
</div>
<div className="flex gap-2 items-center">
@ -73,14 +74,16 @@ const QuizQuestion = ({
width="16"
height="16"
viewBox="0 0 16 16"
fill="none">
fill="none"
>
<path
d="M7.99967 13.3334C10.933 13.3334 13.333 10.9334 13.333 8.00004C13.333 5.06671 10.933 2.66671 7.99967 2.66671C5.06634 2.66671 2.66634 5.06671 2.66634 8.00004C2.66634 10.9334 5.06634 13.3334 7.99967 13.3334ZM7.99967 1.33337C11.6663 1.33337 14.6663 4.33337 14.6663 8.00004C14.6663 11.6667 11.6663 14.6667 7.99967 14.6667C4.33301 14.6667 1.33301 11.6667 1.33301 8.00004C1.33301 4.33337 4.33301 1.33337 7.99967 1.33337ZM10.1997 10.8L9.33301 11.3334L7.33301 7.86671V4.66671H8.33301V7.60004L10.1997 10.8Z"
fill="#878799"
/>
</svg>
<span className="text-textLight text-sm md:text-base">
{Validator.parseDate(startsAt)}-den {Validator.parseDate(endsAt)}-e çenli
{Validator.parseDate(startsAt)}-den{" "}
{Validator.parseDate(endsAt)}-e çenli
</span>
</div>
<div className="flex items-center gap-[5px]">
@ -89,7 +92,8 @@ const QuizQuestion = ({
width="18"
height="18"
viewBox="0 0 18 18"
fill="none">
fill="none"
>
<path
d="M9 5.25C8.5875 5.25 8.25 5.5875 8.25 6V8.25H6C5.5875 8.25 5.25 8.5875 5.25 9C5.25 9.4125 5.5875 9.75 6 9.75H8.25V12C8.25 12.4125 8.5875 12.75 9 12.75C9.4125 12.75 9.75 12.4125 9.75 12V9.75H12C12.4125 9.75 12.75 9.4125 12.75 9C12.75 8.5875 12.4125 8.25 12 8.25H9.75V6C9.75 5.5875 9.4125 5.25 9 5.25ZM9 1.5C4.8675 1.5 1.5 4.8675 1.5 9C1.5 13.1325 4.8675 16.5 9 16.5C13.1325 16.5 16.5 13.1325 16.5 9C16.5 4.8675 13.1325 1.5 9 1.5ZM9 15C5.6925 15 3 12.3075 3 9C3 5.6925 5.6925 3 9 3C12.3075 3 15 5.6925 15 9C15 12.3075 12.3075 15 9 15Z"
fill="#878799"
@ -101,33 +105,31 @@ const QuizQuestion = ({
</div>
</div>
</div>
<p className="text-base md:text-xl text-textDarkt italic md:text-start text-center">
«{question}»
</p>
</div>
{finished === 'closed' ? (
{finished === "closed" ? (
<QuizAccordion finished={finished} questionId={questionId} />
) : null}
</div>
) : null,
) : null
)
) : (
<div className="flex flex-col gap-[20px]">
<div className="flex flex-col gap-[30px] max-w-[700px]">
<div className="flex flex-col gap-[30px]">
<div className="flex flex-col gap-[20px] md:gap-[10px]">
<h2 className="text-[24px] md:text-[32px] leading-[100%] font-semibold text-textBlack text-center md:text-left">
{numbers.map((number, id) => (id === questionNumber ? number : ''))}:
<h2 className="text-[18px] md:text-[32px] leading-[120%] font-semibold text-textBlack">
{question}
</h2>
<div className="flex flex-wrap justify-center md:justify-start md:gap-5 gap-[10px]">
<div className="flex gap-2 items-center">
<div
className={`w-[18px] h-[18px] md:w-4 md:h-4 ${
finished === 'closed' ? 'bg-fillRed' : 'bg-fillGreen'
} rounded-full`}></div>
finished === "closed" ? "bg-fillRed" : "bg-fillGreen"
} rounded-full`}
></div>
<span className="text-textLight text-sm md:text-base">
{finished === 'closed' ? 'ýapyk' : 'açyk'}
{finished === "closed" ? "ýapyk" : "açyk"}
</span>
</div>
<div className="flex gap-2 items-center">
@ -136,14 +138,16 @@ const QuizQuestion = ({
width="16"
height="16"
viewBox="0 0 16 16"
fill="none">
fill="none"
>
<path
d="M7.99967 13.3334C10.933 13.3334 13.333 10.9334 13.333 8.00004C13.333 5.06671 10.933 2.66671 7.99967 2.66671C5.06634 2.66671 2.66634 5.06671 2.66634 8.00004C2.66634 10.9334 5.06634 13.3334 7.99967 13.3334ZM7.99967 1.33337C11.6663 1.33337 14.6663 4.33337 14.6663 8.00004C14.6663 11.6667 11.6663 14.6667 7.99967 14.6667C4.33301 14.6667 1.33301 11.6667 1.33301 8.00004C1.33301 4.33337 4.33301 1.33337 7.99967 1.33337ZM10.1997 10.8L9.33301 11.3334L7.33301 7.86671V4.66671H8.33301V7.60004L10.1997 10.8Z"
fill="#878799"
/>
</svg>
<span className="text-textLight text-sm md:text-base">
{Validator.parseDate(startsAt)}-den {Validator.parseDate(endsAt)}-e çenli
{Validator.parseDate(startsAt)}-den{" "}
{Validator.parseDate(endsAt)}-e çenli
</span>
</div>
<div className="flex items-center gap-[5px]">
@ -152,7 +156,8 @@ const QuizQuestion = ({
width="18"
height="18"
viewBox="0 0 18 18"
fill="none">
fill="none"
>
<path
d="M9 5.25C8.5875 5.25 8.25 5.5875 8.25 6V8.25H6C5.5875 8.25 5.25 8.5875 5.25 9C5.25 9.4125 5.5875 9.75 6 9.75H8.25V12C8.25 12.4125 8.5875 12.75 9 12.75C9.4125 12.75 9.75 12.4125 9.75 12V9.75H12C12.4125 9.75 12.75 9.4125 12.75 9C12.75 8.5875 12.4125 8.25 12 8.25H9.75V6C9.75 5.5875 9.4125 5.25 9 5.25ZM9 1.5C4.8675 1.5 1.5 4.8675 1.5 9C1.5 13.1325 4.8675 16.5 9 16.5C13.1325 16.5 16.5 13.1325 16.5 9C16.5 4.8675 13.1325 1.5 9 1.5ZM9 15C5.6925 15 3 12.3075 3 9C3 5.6925 5.6925 3 9 3C12.3075 3 15 5.6925 15 9C15 12.3075 12.3075 15 9 15Z"
fill="#878799"
@ -164,12 +169,11 @@ const QuizQuestion = ({
</div>
</div>
</div>
<p className="text-base md:text-xl text-textDarkt italic md:text-start text-center">
«{question}»
</p>
</div>
{finished === 'closed' ? <QuizAccordion finished={finished} questionId={questionId} /> : null}
{finished === "closed" ? (
<QuizAccordion finished={finished} questionId={questionId} />
) : null}
</div>
);
};

View File

@ -1,10 +1,11 @@
"use client";
import { useQuizResults, useResultsLoading } from "@/store/store";
import { X } from "lucide-react";
import React, { useState } from "react";
const QuizResultsSearch = ({ id }: { id: string }) => {
const [phone, setPhone] = useState<string>("");
const { setError, setResultData } = useQuizResults();
const { setError, setResultData, error, resultData } = useQuizResults();
const { setLoading } = useResultsLoading();
const handleSearchSubmit = async (event: any) => {
@ -43,7 +44,7 @@ const QuizResultsSearch = ({ id }: { id: string }) => {
<h1 className="text-[28px] leading-[120%] font-semibold text-textBlack text-center md:text-left">
Öz jogaplaryňyzy görüň
</h1>
<div className="flex items-center gap-[14px] relative md:w-1/2">
<div className="flex items-center gap-[14px] relative md:w-1/2 w-full">
<svg
width="20"
height="20"
@ -70,6 +71,20 @@ const QuizResultsSearch = ({ id }: { id: string }) => {
maxLength={8}
minLength={8}
/>
{phone && (
<button
className="absolute right-4"
onClick={() => {
setPhone("");
if (resultData.length || error) {
setResultData([]);
setError("");
}
}}
>
<X />
</button>
)}
</div>
</div>
);

View File

@ -0,0 +1,47 @@
import { Question } from "@/models/quizQuestions.model";
import React from "react";
interface IQuizTabProps {
steps: {
tapgyr: number;
questions: Question[];
}[];
setStep: React.Dispatch<React.SetStateAction<number | string>>;
tab: number | string;
}
function QuizResultsTabs({ steps, setStep, tab }: IQuizTabProps) {
return (
<div className="flex gap-[10px] w-full md:w-1/2 self-center">
{steps.map((item) => (
<button
onClick={() => {
setStep(item.tapgyr - 1);
}}
key={item.tapgyr}
className={`flex-1 py-[5px] rounded-lg transition-all duration-300 ${
tab === item.tapgyr - 1
? "bg-lightPrimary text-white"
: "bg-lightPrimaryContainer text-textLight"
}`}
>
{item.tapgyr}
</button>
))}
<button
onClick={() => {
setStep("results");
}}
className={`flex-1 py-[5px] rounded-lg transition-all duration-300 ${
tab === "results"
? "bg-lightPrimary text-white"
: "bg-lightPrimaryContainer text-textLight"
}`}
>
Netije
</button>
</div>
);
}
export default QuizResultsTabs;

View File

@ -73,8 +73,8 @@ const QuizTapgyrResults = ({ id, steps }: { id: string; steps: string[] }) => {
</h1>
</header>
{data.length > 0 && !loading && !searchLoading ? (
// Table Head
<div className="flex flex-col bg-[#F6F2FA] rounded-[12px] overflow-hidden">
<div className="flex flex-col bg-[#F6F2FA] rounded-[12px] overflow-hidden max-w-[700px] self-center w-full">
{/* Table Head */}
<div className="flex justify-between bg-[#EAE7EF] p-[20px]">
{((data[0] as Datum).client?.id ||
(data[0] as ISearchNetije).place) && (
@ -87,15 +87,6 @@ const QuizTapgyrResults = ({ id, steps }: { id: string; steps: string[] }) => {
Telefon beligisi
</span>
)}
{data[0]?.tapgyr_breakdown &&
steps.map((item) => (
<span
key={item}
className={`hidden md:inline-block min-w-[60px] max-w-[150px] w-full text-center ${padding}`}
>
Tapgyr {item}
</span>
))}
{((data[0] as Datum).correct_answers_time ||
(data[0] as ISearchNetije).total_nobat) && (
<span
@ -114,14 +105,14 @@ const QuizTapgyrResults = ({ id, steps }: { id: string; steps: string[] }) => {
)}
</div>
{/* Table body */}
<div className=" text-[#46464F]">
<div className="text-[#46464F]">
{data.map((winner, id) => (
<React.Fragment key={id}>
<div
key={id}
className={`${
id !== data.length - 1 && "md:border-b md:border-[#C7C5D0]"
} flex justify-between px-[20px]`}
id !== data.length - 1 && "border-b border-[#C7C5D0]"
} flex justify-between items-center px-[20px]`}
>
<span
className={`${padding} max-w-[20px] w-full text-center`}
@ -134,10 +125,7 @@ const QuizTapgyrResults = ({ id, steps }: { id: string; steps: string[] }) => {
? (winner as ISearchNetije).place
: id + 1}
</span>
<div
className={`${padding} max-w-[150px] w-full text-center flex flex-col gap-[7px]`}
>
<span className="h-[36px]">
<span>
+
{(winner as Datum).client?.phone
? (winner as Datum).client?.phone
@ -145,45 +133,6 @@ const QuizTapgyrResults = ({ id, steps }: { id: string; steps: string[] }) => {
? (winner as ISearchNetije).phone
: "-"}
</span>
{steps.map((step, i) => (
<span
key={i}
className="py-[5px] text-center col-span-2 md:hidden"
>
Tapgyr {step}
</span>
))}
</div>
{steps.map((step, i) => {
const tapgyr = winner.tapgyr_breakdown?.find(
(i) => i.tapgyr === Number(step)
);
return (
<div
key={i}
className="min-w-[60px] max-w-[150px] w-full justify-center hidden md:flex"
>
<div className="flex justify-center items-center text-base text-textBlack leading-[125%] max-w-[50px] w-[100%]">
<span className="border border-[#2C7CDA] text-[#2C7CDA] rounded-full w-[36px] h-[36px] flex justify-center items-center text-base leading-[125%] ">
{tapgyr && "tapgyr_correct_time" in tapgyr
? tapgyr.tapgyr_correct_time
: tapgyr && "tapgyr_total_nobat" in tapgyr
? tapgyr.tapgyr_total_nobat
: "-"}
</span>
</div>
<div className="flex justify-center items-center text-base text-textBlack leading-[125%] max-w-[50px] w-[100%]">
<span className="bg-fillOrange rounded-full w-[36px] h-[36px] flex justify-center items-center text-base leading-[125%] text-white">
{tapgyr && "tapgyr_score" in tapgyr
? tapgyr.tapgyr_score
: tapgyr && "tapgyr_total_score" in tapgyr
? tapgyr.tapgyr_total_score
: "-"}
</span>
</div>
</div>
);
})}
<div
className={`min-w-[50px] md:min-w-[115px] max-w-[150px] w-full flex flex-col md:flex-row justify-center ${padding}`}
>
@ -196,24 +145,6 @@ const QuizTapgyrResults = ({ id, steps }: { id: string; steps: string[] }) => {
: "-"}
</span>
</div>
{steps.map((step, i) => {
const tapgyr = winner.tapgyr_breakdown?.find(
(i) => i.tapgyr === Number(step)
);
return (
<div key={i} className="py-[5px] md:hidden">
<div className="flex justify-center items-center text-base text-textBlack leading-[125%] max-w-[180px] w-[100%]">
<span className="border border-[#2C7CDA] text-[#2C7CDA] rounded-full w-[30px] h-[30px] flex justify-center items-center text-base leading-[125%] ">
{tapgyr && "tapgyr_correct_time" in tapgyr
? tapgyr.tapgyr_correct_time
: tapgyr && "tapgyr_total_nobat" in tapgyr
? tapgyr.tapgyr_total_nobat
: "-"}
</span>
</div>
</div>
);
})}
</div>
<div
className={`min-w-[50px] md:min-w-[115px] max-w-[150px] w-full flex flex-col md:flex-row justify-center ${padding}`}
@ -227,57 +158,8 @@ const QuizTapgyrResults = ({ id, steps }: { id: string; steps: string[] }) => {
: "-"}
</span>
</div>
{steps.map((step, i) => {
const tapgyr = winner.tapgyr_breakdown?.find(
(i) => i.tapgyr === Number(step)
);
return (
<div key={i} className="py-[5px] md:hidden">
<div className="flex justify-center items-center text-base text-textBlack leading-[125%] max-w-[180px] w-[100%]">
<span className="bg-fillOrange rounded-full w-[30px] h-[30px] flex justify-center items-center text-base leading-[125%] text-white">
{tapgyr && "tapgyr_correct_time" in tapgyr
? tapgyr.tapgyr_correct_time
: tapgyr && "tapgyr_total_nobat" in tapgyr
? tapgyr.tapgyr_total_nobat
: "-"}
</span>
</div>
</div>
);
})}
</div>
</div>
{/* {steps.map((step, i) => {
const tapgyr = winner.tapgyr_breakdown?.find(
(i) => i.tapgyr === Number(step)
);
return (
<div
key={i}
className={`${
i === steps.length - 1 && "border-b border-[#C7C5D0]"
} md:border-none md:hidden grid grid-cols-4`}
>
<span className="py-[5px] text-center col-span-2">
Tapgyr {step}
</span>
<div className="py-[5px]">
<div className="flex justify-center items-center text-base text-textBlack leading-[125%] max-w-[180px] w-[100%]">
<span className="border border-[#2C7CDA] text-[#2C7CDA] rounded-full w-[30px] h-[30px] flex justify-center items-center text-base leading-[125%] ">
{tapgyr ? tapgyr.tapgyr_correct_time : "-"}
</span>
</div>
</div>
<div className="py-[5px]">
<div className="flex justify-center items-center text-base text-textBlack leading-[125%] max-w-[180px] w-[100%]">
<span className="bg-fillOrange rounded-full w-[30px] h-[30px] flex justify-center items-center text-base leading-[125%] text-white">
{tapgyr ? tapgyr.tapgyr_correct_time : "-"}
</span>
</div>
</div>
</div>
);
})} */}
</React.Fragment>
))}
</div>