fix quiz/active mobile version table & new logic in lottery route
This commit is contained in:
parent
aba13f527f
commit
fd6a0daf8e
|
|
@ -0,0 +1,34 @@
|
||||||
|
"use server";
|
||||||
|
import baseUrl from "@/baseUrl";
|
||||||
|
import routes from "@/routes";
|
||||||
|
import { cookies } from "next/headers";
|
||||||
|
|
||||||
|
export async function authenticateLottery(
|
||||||
|
phone: string,
|
||||||
|
code: string,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${baseUrl.QUIZ_SRC}${routes.lotteryActive}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
phone: phone,
|
||||||
|
key: code,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
console.log("Authentication failed");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await res.json();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ import { VideoModel } from "@/models/video.model";
|
||||||
import { VideosModel } from "@/models/videos.model";
|
import { VideosModel } from "@/models/videos.model";
|
||||||
import { IVote } from "@/models/vote.model";
|
import { IVote } from "@/models/vote.model";
|
||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
import { CloudFog } from "lucide-react";
|
import { cookies } from "next/headers";
|
||||||
|
|
||||||
export class Queries {
|
export class Queries {
|
||||||
public static async getNews(
|
public static async getNews(
|
||||||
|
|
@ -259,28 +259,26 @@ export class Queries {
|
||||||
).then((res) => res.json().then((res) => res as MessagesByTvAdmin));
|
).then((res) => res.json().then((res) => res as MessagesByTvAdmin));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lottery ================================================================================
|
|
||||||
|
|
||||||
public static async authenticateLottery(
|
|
||||||
phone: string,
|
|
||||||
code: string
|
|
||||||
): Promise<ILotteryResponse> {
|
|
||||||
return await fetch(`${baseUrl.QUIZ_SRC}${routes.lotteryActive}`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
phone: phone,
|
|
||||||
key: code,
|
|
||||||
}),
|
|
||||||
}).then((res) => {
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error("Authentication failed");
|
|
||||||
}
|
|
||||||
return res.json();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================================
|
// ============================================================================================
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getTossData = async ({
|
||||||
|
type,
|
||||||
|
id,
|
||||||
|
}: {
|
||||||
|
type: string;
|
||||||
|
id: string;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${baseUrl.QUIZ_SRC}${routes.tossId(type, id)}`);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await res.json();
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import Buble from "@/components/Buble";
|
|
||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
import MobileMenu from "@/components/MobileMenu";
|
import MobileMenu from "@/components/MobileMenu";
|
||||||
import Nav from "@/components/Nav";
|
import Nav from "@/components/Nav";
|
||||||
import GlobalContext from "@/context/GlobalContext";
|
|
||||||
import MainProvider from "@/providers/MainProvider";
|
import MainProvider from "@/providers/MainProvider";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
|
@ -13,11 +11,9 @@ const RootLayout = ({ children }: IProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="z-20 relative">
|
<div className="z-20 relative">
|
||||||
<MainProvider>
|
<MainProvider>
|
||||||
{/* <Buble /> */}
|
<div className="bg-white dark:bg-black transition-all min-h-screen flex flex-col">
|
||||||
<div className="bg-white dark:bg-black transition-all h-full">
|
|
||||||
<h1 className="hidden">Turkmen TV</h1>
|
|
||||||
<Nav />
|
<Nav />
|
||||||
<main className="min-h-[50vh]">{children}</main>
|
<main className="flex-1">{children}</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
<MobileMenu />
|
<MobileMenu />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
'use client';
|
import LotteryAuthForm from "@/components/lottery/auth/LotteryAuthForm";
|
||||||
|
|
||||||
import LotteryAuthForm from '@/components/lottery/auth/LotteryAuthForm';
|
|
||||||
|
|
||||||
const LotteryAuthPage = () => {
|
const LotteryAuthPage = () => {
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="flex justify-center items-center min-h-[50vh] py-[200px]">
|
<div className="flex justify-center items-center py-[200px]">
|
||||||
<LotteryAuthForm />
|
<LotteryAuthForm />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,114 +1,16 @@
|
||||||
"use client";
|
import { authenticateLottery } from "@/api";
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { useLotteryAuth } from "@/store/useLotteryAuth";
|
|
||||||
import ProtectedRoute from "@/components/lottery/auth/ProtectedRoute";
|
|
||||||
import LotteryHeader from "@/components/lottery/LotteryHeader";
|
|
||||||
|
|
||||||
import LotteryWinnersSection from "@/components/lottery/LotteryWinnersSection";
|
|
||||||
import LotteryRulesSection from "@/components/lottery/rules/LotteryRulesSection";
|
|
||||||
import LotteryCountDown from "@/components/lottery/countDown/LotteryCountDown";
|
|
||||||
import { Queries } from "@/api/queries";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import Loader from "@/components/Loader";
|
import Loader from "@/components/Loader";
|
||||||
|
import LotteryMain from "@/components/lottery/LotteryMain";
|
||||||
|
import { cookies } from "next/headers";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import React, { Suspense } from "react";
|
||||||
|
|
||||||
const LotteryPage = () => {
|
const Page = async () => {
|
||||||
const { lotteryData, setAuth } = useLotteryAuth();
|
|
||||||
const [status, setStatus] = useState<"not-started" | "started" | "ended">(
|
|
||||||
"not-started"
|
|
||||||
);
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const checkAuth = async () => {
|
|
||||||
// ✅ Check credentials from localStorage
|
|
||||||
const phone = localStorage.getItem("lotteryPhone");
|
|
||||||
const code = localStorage.getItem("lotteryCode");
|
|
||||||
|
|
||||||
if (phone && code) {
|
|
||||||
try {
|
|
||||||
// ✅ Authenticate using stored credentials
|
|
||||||
const response = await Queries.authenticateLottery(phone, code);
|
|
||||||
|
|
||||||
if (response.errorMessage) {
|
|
||||||
// If authentication fails, redirect to the auth page
|
|
||||||
console.log("redirecting form lottery/");
|
|
||||||
|
|
||||||
router.replace("/lottery/auth");
|
|
||||||
} else {
|
|
||||||
// ✅ Set the authenticated state
|
|
||||||
setAuth(response, phone, code);
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Authentication failed:", err);
|
|
||||||
router.replace("/lottery/auth");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Redirect to the auth page if no credentials are found
|
|
||||||
router.replace("/lottery/auth");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
checkAuth();
|
|
||||||
}, [router, setAuth]);
|
|
||||||
|
|
||||||
if (isLoading && !lotteryData?.data) {
|
|
||||||
<div className="flex w-full h-[90vh] justify-center items-center">
|
|
||||||
<Loader />
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProtectedRoute>
|
<Suspense fallback={<Loader />}>
|
||||||
{lotteryData?.data && (
|
<LotteryMain />
|
||||||
<div className="flex flex-col md:gap-[128px] gap-[80px] font-roboto md:pt-[64px] sm:pt-[48px] pt-[40px] ms:pb-[128px] pb-[80px] text-lightOnSurface">
|
</Suspense>
|
||||||
{lotteryData && (
|
|
||||||
<div className="flex flex-col sm:gap-[64px] gap-[40px]">
|
|
||||||
<LotteryHeader
|
|
||||||
title={lotteryData.data.title}
|
|
||||||
description={lotteryData.data.description}
|
|
||||||
image={lotteryData.data.image}
|
|
||||||
smsCode={lotteryData.data.sms_code}
|
|
||||||
startDate={lotteryData.data.start_time}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{status === "not-started" ? (
|
|
||||||
<div className="container">
|
|
||||||
<LotteryCountDown
|
|
||||||
lotteryStatus={status}
|
|
||||||
setLotteryStatus={setStatus}
|
|
||||||
endDate={lotteryData.data.end_time}
|
|
||||||
startDate={lotteryData.data.start_time}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<LotteryRulesSection />
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-10">
|
|
||||||
{lotteryData && (status === "ended" || status === "started") && (
|
|
||||||
<LotteryWinnersSection lotteryStatus={status} />
|
|
||||||
)}
|
|
||||||
<div className="w-full">
|
|
||||||
<div className="container">
|
|
||||||
<Link
|
|
||||||
href="/lottery/auth"
|
|
||||||
className="sm:text-textLarge sm:leading-textLarge text-[16px] rounded-full leading-[24px] sm:py-[12px] py-[8px] w-full flex justify-center items-center border-2 border-lightPrimary hover:bg-lightPrimary font-medium text-lightPrimary hover:text-lightOnPrimary disabled:opacity-50 transition-all duration-300"
|
|
||||||
>
|
|
||||||
Çykmak
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ProtectedRoute>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LotteryPage;
|
export default Page;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import LotteryAuthForm from "@/components/lottery/auth/LotteryAuthForm";
|
||||||
|
|
||||||
|
const LotteryAuthPage = () => {
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
<div className="flex justify-center items-center min-h-[50vh] py-[200px]">
|
||||||
|
{/* <LotteryAuthForm /> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LotteryAuthPage;
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useLotteryAuth } from "@/store/useLotteryAuth";
|
||||||
|
import ProtectedRoute from "@/components/lottery/auth/ProtectedRoute";
|
||||||
|
import LotteryHeader from "@/components/lottery/LotteryHeader";
|
||||||
|
|
||||||
|
import LotteryWinnersSection from "@/components/lottery/LotteryWinnersSection";
|
||||||
|
import LotteryRulesSection from "@/components/lottery/rules/LotteryRulesSection";
|
||||||
|
import LotteryCountDown from "@/components/lottery/countDown/LotteryCountDown";
|
||||||
|
import { Queries } from "@/api/queries";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import Loader from "@/components/Loader";
|
||||||
|
import { authenticateLottery } from "@/api";
|
||||||
|
import { ILotteryResponse } from "@/models/lottery/lottery.model";
|
||||||
|
|
||||||
|
const LotteryPage = () => {
|
||||||
|
const [lotteryData, setData] = useState<ILotteryResponse>();
|
||||||
|
const [status, setStatus] = useState<"not-started" | "started" | "ended">(
|
||||||
|
"not-started"
|
||||||
|
);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkAuth = async () => {
|
||||||
|
const res = await authenticateLottery();
|
||||||
|
setData(res);
|
||||||
|
};
|
||||||
|
|
||||||
|
checkAuth();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (isLoading && !lotteryData?.data) {
|
||||||
|
<div className="flex w-full h-[90vh] justify-center items-center">
|
||||||
|
<Loader />
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProtectedRoute>
|
||||||
|
{lotteryData?.data && (
|
||||||
|
<div className="flex flex-col md:gap-[128px] gap-[80px] font-roboto md:pt-[64px] sm:pt-[48px] pt-[40px] ms:pb-[128px] pb-[80px] text-lightOnSurface">
|
||||||
|
{lotteryData && (
|
||||||
|
<div className="flex flex-col sm:gap-[64px] gap-[40px]">
|
||||||
|
<LotteryHeader
|
||||||
|
title={lotteryData.data.title}
|
||||||
|
description={lotteryData.data.description}
|
||||||
|
image={lotteryData.data.image}
|
||||||
|
smsCode={lotteryData.data.sms_code}
|
||||||
|
startDate={lotteryData.data.start_time}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{status === "not-started" ? (
|
||||||
|
<div className="container">
|
||||||
|
<LotteryCountDown
|
||||||
|
lotteryStatus={status}
|
||||||
|
setLotteryStatus={setStatus}
|
||||||
|
endDate={lotteryData.data.end_time}
|
||||||
|
startDate={lotteryData.data.start_time}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<LotteryRulesSection />
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-10">
|
||||||
|
{lotteryData && (status === "ended" || status === "started") && (
|
||||||
|
<LotteryWinnersSection lotteryStatus={status} />
|
||||||
|
)}
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="container">
|
||||||
|
<Link
|
||||||
|
href="/lottery/auth"
|
||||||
|
className="sm:text-textLarge sm:leading-textLarge text-[16px] rounded-full leading-[24px] sm:py-[12px] py-[8px] w-full flex justify-center items-center border-2 border-lightPrimary hover:bg-lightPrimary font-medium text-lightPrimary hover:text-lightOnPrimary disabled:opacity-50 transition-all duration-300"
|
||||||
|
>
|
||||||
|
Çykmak
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ProtectedRoute>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LotteryPage;
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
import { Queries } from '@/api/queries';
|
import { Queries } from '@/api/queries';
|
||||||
import Loader from '@/components/Loader';
|
|
||||||
import QuizQuestion from '@/components/quiz/QuizQuestion';
|
|
||||||
import QuizQuestionList from '@/components/quiz/QuizQuestionList';
|
import QuizQuestionList from '@/components/quiz/QuizQuestionList';
|
||||||
import QuizSearch from '@/components/quiz/QuizSearch';
|
import QuizSearch from '@/components/quiz/QuizSearch';
|
||||||
import QuizTable from '@/components/quiz/QuizTable';
|
import QuizTable from '@/components/quiz/QuizTable';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import Loader from "@/components/Loader";
|
||||||
|
import TossPage from "@/components/toss";
|
||||||
|
import { notFound } from "next/navigation";
|
||||||
|
import React, { Suspense } from "react";
|
||||||
|
|
||||||
|
type SearchParams = Promise<{ [key: string]: string }>;
|
||||||
|
|
||||||
|
const Page = async (props: { searchParams: SearchParams }) => {
|
||||||
|
const searchParams = await props.searchParams;
|
||||||
|
let type: "bije" | "cekilis";
|
||||||
|
|
||||||
|
if (searchParams?.cekilis) {
|
||||||
|
type = "cekilis";
|
||||||
|
} else if (searchParams?.bije) {
|
||||||
|
type = "bije";
|
||||||
|
} else {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<Loader />}>
|
||||||
|
<TossPage
|
||||||
|
type={type}
|
||||||
|
id={searchParams?.cekilis ? searchParams?.cekilis : searchParams?.bije}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
|
|
@ -5,24 +5,24 @@
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--background: 0 0% 100%;
|
--background: 0 0% 100%;
|
||||||
--foreground: 222.2 84% 4.9%;
|
--foreground: 0 0% 3.9%;
|
||||||
--card: 0 0% 100%;
|
--card: 0 0% 100%;
|
||||||
--card-foreground: 222.2 84% 4.9%;
|
--card-foreground: 0 0% 3.9%;
|
||||||
--popover: 0 0% 100%;
|
--popover: 0 0% 100%;
|
||||||
--popover-foreground: 222.2 84% 4.9%;
|
--popover-foreground: 0 0% 3.9%;
|
||||||
--primary: 222.2 47.4% 11.2%;
|
--primary: 0 0% 9%;
|
||||||
--primary-foreground: 210 40% 98%;
|
--primary-foreground: 0 0% 98%;
|
||||||
--secondary: 210 40% 96.1%;
|
--secondary: 0 0% 96.1%;
|
||||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
--secondary-foreground: 0 0% 9%;
|
||||||
--muted: 210 40% 96.1%;
|
--muted: 0 0% 96.1%;
|
||||||
--muted-foreground: 215.4 16.3% 46.9%;
|
--muted-foreground: 0 0% 45.1%;
|
||||||
--accent: 210 40% 96.1%;
|
--accent: 0 0% 96.1%;
|
||||||
--accent-foreground: 222.2 47.4% 11.2%;
|
--accent-foreground: 0 0% 9%;
|
||||||
--destructive: 0 84.2% 60.2%;
|
--destructive: 0 84.2% 60.2%;
|
||||||
--destructive-foreground: 210 40% 98%;
|
--destructive-foreground: 0 0% 98%;
|
||||||
--border: 214.3 31.8% 91.4%;
|
--border: 0 0% 89.8%;
|
||||||
--input: 214.3 31.8% 91.4%;
|
--input: 0 0% 89.8%;
|
||||||
--ring: 222.2 84% 4.9%;
|
--ring: 0 0% 3.9%;
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
--chart-1: 12 76% 61%;
|
--chart-1: 12 76% 61%;
|
||||||
--chart-2: 173 58% 39%;
|
--chart-2: 173 58% 39%;
|
||||||
|
|
@ -32,25 +32,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: 222.2 84% 4.9%;
|
--background: 0 0% 3.9%;
|
||||||
--foreground: 210 40% 98%;
|
--foreground: 0 0% 98%;
|
||||||
--card: 222.2 84% 4.9%;
|
--card: 0 0% 3.9%;
|
||||||
--card-foreground: 210 40% 98%;
|
--card-foreground: 0 0% 98%;
|
||||||
--popover: 222.2 84% 4.9%;
|
--popover: 0 0% 3.9%;
|
||||||
--popover-foreground: 210 40% 98%;
|
--popover-foreground: 0 0% 98%;
|
||||||
--primary: 210 40% 98%;
|
--primary: 0 0% 98%;
|
||||||
--primary-foreground: 222.2 47.4% 11.2%;
|
--primary-foreground: 0 0% 9%;
|
||||||
--secondary: 217.2 32.6% 17.5%;
|
--secondary: 0 0% 14.9%;
|
||||||
--secondary-foreground: 210 40% 98%;
|
--secondary-foreground: 0 0% 98%;
|
||||||
--muted: 217.2 32.6% 17.5%;
|
--muted: 0 0% 14.9%;
|
||||||
--muted-foreground: 215 20.2% 65.1%;
|
--muted-foreground: 0 0% 63.9%;
|
||||||
--accent: 217.2 32.6% 17.5%;
|
--accent: 0 0% 14.9%;
|
||||||
--accent-foreground: 210 40% 98%;
|
--accent-foreground: 0 0% 98%;
|
||||||
--destructive: 0 62.8% 30.6%;
|
--destructive: 0 62.8% 30.6%;
|
||||||
--destructive-foreground: 210 40% 98%;
|
--destructive-foreground: 0 0% 98%;
|
||||||
--border: 217.2 32.6% 17.5%;
|
--border: 0 0% 14.9%;
|
||||||
--input: 217.2 32.6% 17.5%;
|
--input: 0 0% 14.9%;
|
||||||
--ring: 212.7 26.8% 83.9%;
|
--ring: 0 0% 83.1%;
|
||||||
--chart-1: 220 70% 50%;
|
--chart-1: 220 70% 50%;
|
||||||
--chart-2: 160 60% 45%;
|
--chart-2: 160 60% 45%;
|
||||||
--chart-3: 30 80% 55%;
|
--chart-3: 30 80% 55%;
|
||||||
|
|
@ -59,21 +59,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @layer base {
|
|
||||||
* {
|
|
||||||
@apply border-border;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
@apply bg-background text-foreground;
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
/* <<<<<<< HEAD * {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
=======>>>>>>>2c1eddb85d62e4709470605eb8df7458c2d6c37e body, */
|
|
||||||
|
|
||||||
* {
|
* {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
@ -357,10 +342,6 @@ big {
|
||||||
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.2); /* Add depth */
|
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.2); /* Add depth */
|
||||||
}
|
}
|
||||||
|
|
||||||
.span.flash {
|
|
||||||
animation: flash 1s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes dots-flash {
|
@keyframes dots-flash {
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://ui.shadcn.com/schema.json",
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
"style": "default",
|
"style": "new-york",
|
||||||
"rsc": true,
|
"rsc": true,
|
||||||
"tsx": true,
|
"tsx": true,
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"config": "tailwind.config.ts",
|
"config": "tailwind.config.js",
|
||||||
"css": "app/globals.css",
|
"css": "app/globals.css",
|
||||||
"baseColor": "slate",
|
"baseColor": "neutral",
|
||||||
"cssVariables": true,
|
"cssVariables": true,
|
||||||
"prefix": ""
|
"prefix": ""
|
||||||
},
|
},
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"components": "@/components",
|
"components": "@/components",
|
||||||
"utils": "@/lib/utils"
|
"utils": "@/lib/utils",
|
||||||
}
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide"
|
||||||
}
|
}
|
||||||
|
|
@ -49,14 +49,6 @@ const Nav = () => {
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<ul className="md:flex gap-5 items-center justify-start hidden">
|
<ul className="md:flex gap-5 items-center justify-start hidden">
|
||||||
{/* <li>
|
|
||||||
<Link
|
|
||||||
className="block text-lg text-black transition-all font-roboto font-bold dark:text-white"
|
|
||||||
href={'/news'}
|
|
||||||
style={path.includes('news') ? { color: '#FFAB48' } : {}}>
|
|
||||||
Habarlar
|
|
||||||
</Link>
|
|
||||||
</li> */}
|
|
||||||
<li>
|
<li>
|
||||||
<Link
|
<Link
|
||||||
href={'/treasury'}
|
href={'/treasury'}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
'use client';
|
"use client";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
import { useState, useEffect } from 'react';
|
import ReactConfetti from "react-confetti";
|
||||||
import ReactConfetti from 'react-confetti';
|
import { useWindowSize } from "react-use";
|
||||||
import { useWindowSize } from 'react-use';
|
import { useMediaQuery } from "usehooks-ts";
|
||||||
import { useMediaQuery } from 'usehooks-ts';
|
|
||||||
|
|
||||||
const Confetti = ({
|
const Confetti = ({
|
||||||
numberOfPieces = 200,
|
numberOfPieces = 200,
|
||||||
|
|
@ -12,27 +11,31 @@ const Confetti = ({
|
||||||
numberOfPieces?: number;
|
numberOfPieces?: number;
|
||||||
showConfetti: boolean;
|
showConfetti: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
const [recycle, setRecycle] = useState<boolean>(true);
|
||||||
const { width, height } = useWindowSize();
|
const { width, height } = useWindowSize();
|
||||||
const colors = [
|
const colors = [
|
||||||
'linear-gradient(45deg, #5D5D72, #8589DE)',
|
"linear-gradient(45deg, #5D5D72, #8589DE)",
|
||||||
'linear-gradient(45deg, #E1E0FF, #575992)',
|
"linear-gradient(45deg, #E1E0FF, #575992)",
|
||||||
'#8589DE',
|
"#8589DE",
|
||||||
'#575992',
|
"#575992",
|
||||||
'#E1E0FF',
|
"#E1E0FF",
|
||||||
'#FF3131',
|
"#FF3131",
|
||||||
];
|
];
|
||||||
|
|
||||||
const mobile = useMediaQuery('(max-width: 426px)');
|
const mobile = useMediaQuery("(max-width: 426px)");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => setRecycle(false), 30000);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed top-0 left-0 z-50">
|
<div className="fixed top-0 left-0 z-50">
|
||||||
<ReactConfetti
|
<ReactConfetti
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
recycle={showConfetti}
|
recycle={recycle}
|
||||||
numberOfPieces={mobile ? numberOfPieces / 3 : numberOfPieces}
|
numberOfPieces={mobile ? 200 / 3 : 200}
|
||||||
tweenDuration={500}
|
tweenDuration={500}
|
||||||
// run={true}
|
|
||||||
colors={colors}
|
colors={colors}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
import LotteryHeader from "@/components/lottery/LotteryHeader";
|
||||||
|
import LotteryRulesSection from "@/components/lottery/rules/LotteryRulesSection";
|
||||||
|
import LotteryCountDown from "@/components/lottery/countDown/LotteryCountDown";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { authenticateLottery } from "@/api";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import { cookies } from "next/headers";
|
||||||
|
import { getLotteryStatus } from "@/lib/actions";
|
||||||
|
import LotteryWinners from "./LotteryWinners";
|
||||||
|
|
||||||
|
async function getData() {
|
||||||
|
const cookieStore = await cookies();
|
||||||
|
const phone = cookieStore.get("phoneNumber");
|
||||||
|
const key = cookieStore.get("key");
|
||||||
|
if (phone?.value && key?.value) {
|
||||||
|
const res = await authenticateLottery(phone.value, key.value);
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
redirect("/lottery/auth");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LotteryMain = async () => {
|
||||||
|
const lotteryData = await getData();
|
||||||
|
|
||||||
|
const status = await getLotteryStatus(
|
||||||
|
lotteryData?.data.start_time,
|
||||||
|
lotteryData?.data.end_time
|
||||||
|
);
|
||||||
|
|
||||||
|
return lotteryData?.errorMessage ? (
|
||||||
|
<div className="flex flex-1 w-full justify-center items-center">
|
||||||
|
<h1 className="text-[50px]">{lotteryData.errorMessage}</h1>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col md:gap-[128px] gap-[80px] font-roboto md:pt-[64px] sm:pt-[48px] pt-[40px] ms:pb-[128px] pb-[80px] text-lightOnSurface">
|
||||||
|
<div className="flex flex-col sm:gap-[64px] gap-[40px]">
|
||||||
|
<LotteryHeader
|
||||||
|
title={lotteryData.data.title}
|
||||||
|
description={lotteryData.data.description}
|
||||||
|
image={lotteryData.data.image}
|
||||||
|
smsCode={lotteryData.data.sms_code}
|
||||||
|
startDate={lotteryData.data.start_time}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{status === "Upcoming" && (
|
||||||
|
<div className="container">
|
||||||
|
<LotteryCountDown
|
||||||
|
lotteryStatus={status}
|
||||||
|
endDate={lotteryData.data.end_time}
|
||||||
|
startDate={lotteryData.data.start_time}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<LotteryRulesSection data={lotteryData} />
|
||||||
|
<div className="flex flex-col gap-10">
|
||||||
|
<LotteryWinners data={lotteryData} lotteryStatus={status} />
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="container">
|
||||||
|
<Link
|
||||||
|
href="/lottery/auth"
|
||||||
|
className="sm:text-textLarge sm:leading-textLarge text-[16px] rounded-full leading-[24px] sm:py-[12px] py-[8px] w-full flex justify-center items-center border-2 border-lightPrimary hover:bg-lightPrimary font-medium text-lightPrimary hover:text-lightOnPrimary disabled:opacity-50 transition-all duration-300"
|
||||||
|
>
|
||||||
|
Çykmak
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LotteryMain;
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
"use client";
|
||||||
|
import { useLotteryStatus } from "@/store/store";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import LotteryWinnersSection from "./LotteryWinnersSection";
|
||||||
|
import { ILotteryResponse } from "@/models/lottery/lottery.model";
|
||||||
|
|
||||||
|
const LotteryWinners = ({
|
||||||
|
data,
|
||||||
|
lotteryStatus,
|
||||||
|
}: {
|
||||||
|
data: ILotteryResponse;
|
||||||
|
lotteryStatus: "Upcoming" | "Ongoing" | "Finished";
|
||||||
|
}) => {
|
||||||
|
const { status, setStatus } = useLotteryStatus();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setStatus(lotteryStatus);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
(status === "Finished" || status === "Ongoing") && (
|
||||||
|
<LotteryWinnersSection data={data} />
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LotteryWinners;
|
||||||
|
|
@ -11,16 +11,10 @@ import { AnimatePresence, motion } from "framer-motion";
|
||||||
const WEBSOCKET_URL = "wss://sms.turkmentv.gov.tm/ws/lottery?dst=";
|
const WEBSOCKET_URL = "wss://sms.turkmentv.gov.tm/ws/lottery?dst=";
|
||||||
const SLOT_COUNTER_DURATION = 30000;
|
const SLOT_COUNTER_DURATION = 30000;
|
||||||
|
|
||||||
const LotteryWinnersSection = ({
|
const LotteryWinnersSection = ({ data }: { data: any }) => {
|
||||||
lotteryStatus,
|
|
||||||
}: {
|
|
||||||
lotteryStatus: string;
|
|
||||||
}) => {
|
|
||||||
const [winners, setWinners] = useState<LotteryWinnerDataSimplified[]>([]);
|
const [winners, setWinners] = useState<LotteryWinnerDataSimplified[]>([]);
|
||||||
const [currentNumber, setCurrentNumber] = useState<string>("00-00-00-00-00");
|
const [currentNumber, setCurrentNumber] = useState<string>("00-00-00-00-00");
|
||||||
const [isConfettiActive, setIsConfettiActive] = useState(false);
|
const [isConfettiActive, setIsConfettiActive] = useState(false);
|
||||||
|
|
||||||
const { lotteryData } = useLotteryAuth();
|
|
||||||
const [winnerSelectingStatus, setWinnerSelectingStatus] = useState<
|
const [winnerSelectingStatus, setWinnerSelectingStatus] = useState<
|
||||||
"not-selected" | "is-selecting" | "selected"
|
"not-selected" | "is-selecting" | "selected"
|
||||||
>("not-selected");
|
>("not-selected");
|
||||||
|
|
@ -28,52 +22,19 @@ const LotteryWinnersSection = ({
|
||||||
useState<LotteryWinnerDataSimplified | null>(null);
|
useState<LotteryWinnerDataSimplified | null>(null);
|
||||||
const [topText, setTopText] = useState<string>("Bije az wagtdan başlaýar");
|
const [topText, setTopText] = useState<string>("Bije az wagtdan başlaýar");
|
||||||
const [bottomText, setBottomText] = useState<string>("");
|
const [bottomText, setBottomText] = useState<string>("");
|
||||||
|
|
||||||
const [messageQueue, setMessageQueue] = useState<
|
const [messageQueue, setMessageQueue] = useState<
|
||||||
LotteryWinnerDataSimplified[]
|
LotteryWinnerDataSimplified[]
|
||||||
>([]); // Queue for incoming WebSocket messages
|
>([]); // Queue for incoming WebSocket messages
|
||||||
const [isProcessing, setIsProcessing] = useState<boolean>(false); // Track if a message is being processed
|
const [isProcessing, setIsProcessing] = useState<boolean>(false); // Track if a message is being processed
|
||||||
|
const [startNumber, setStartNumber] = useState("00,00,00,00,00");
|
||||||
const { wsStatus, subscribeToMessages } = useWebsocketLottery(
|
const { wsStatus, subscribeToMessages } = useWebsocketLottery(
|
||||||
`${WEBSOCKET_URL}${lotteryData?.data.sms_number}`
|
`${WEBSOCKET_URL}${data?.data.sms_number}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Simulate WebSocket message for testing
|
|
||||||
const simulateMessage = () => {
|
|
||||||
const dummyWinner: LotteryWinnerDataSimplified = {
|
|
||||||
phone: `9936${Math.floor(10000000 + Math.random() * 90000000)}`, // Generate random client number
|
|
||||||
winner_no: winners.length + 1, // Increment winner number
|
|
||||||
ticket: `${Math.floor(Math.random() * 99)
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}-${Math.floor(Math.random() * 99)
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}-${Math.floor(Math.random() * 99)
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}-${Math.floor(Math.random() * 99)
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}-${Math.floor(Math.random() * 99)
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}`, // Generate random ticket
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("📩 Simulated Message:", dummyWinner); // Log the simulated message
|
|
||||||
setMessageQueue((prevQueue) => [...prevQueue, dummyWinner]);
|
|
||||||
};
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// const interval = setInterval(() => {
|
|
||||||
// simulateMessage();
|
|
||||||
// }, 20000); // Trigger every 10 seconds
|
|
||||||
|
|
||||||
// return () => clearInterval(interval); // Clean up interval on unmount
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// Initialize winners from lottery data
|
// Initialize winners from lottery data
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("🎟️ Lottery Data:", lotteryData);
|
if (data?.data?.winners.length > 0) {
|
||||||
|
const simplifiedWinners = data.data.winners.map((winner: any) => ({
|
||||||
if (lotteryData && lotteryData.data.winners.length > 0) {
|
|
||||||
const simplifiedWinners = lotteryData.data.winners.map((winner) => ({
|
|
||||||
phone: winner.client,
|
phone: winner.client,
|
||||||
winner_no: winner.winner_no,
|
winner_no: winner.winner_no,
|
||||||
ticket: winner.ticket,
|
ticket: winner.ticket,
|
||||||
|
|
@ -84,20 +45,19 @@ const LotteryWinnersSection = ({
|
||||||
setWinnerSelectingStatus("selected");
|
setWinnerSelectingStatus("selected");
|
||||||
setTopText(`${lastWinner.winner_no}-nji ýeňiji`);
|
setTopText(`${lastWinner.winner_no}-nji ýeňiji`);
|
||||||
setBottomText(lastWinner.phone);
|
setBottomText(lastWinner.phone);
|
||||||
|
setStartNumber(lastWinner.ticket.replace(/-/g, ","));
|
||||||
setIsConfettiActive(true);
|
setIsConfettiActive(true);
|
||||||
}
|
}
|
||||||
}, [lotteryData]);
|
}, [data]);
|
||||||
|
|
||||||
// Subscribe to WebSocket messages
|
// Subscribe to WebSocket messages
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = subscribeToMessages((event) => {
|
const unsubscribe = subscribeToMessages((event) => {
|
||||||
try {
|
try {
|
||||||
const newWinner: LotteryWinnerDataSimplified = JSON.parse(event.data);
|
const newWinner: LotteryWinnerDataSimplified = JSON.parse(event.data);
|
||||||
console.log("📩 WebSocket Message Received:", newWinner); // Log the parsed message
|
|
||||||
|
|
||||||
// Add new message to the queue
|
// Add new message to the queue
|
||||||
setMessageQueue((prevQueue) => {
|
setMessageQueue((prevQueue) => {
|
||||||
console.log("📥 Adding to Queue:", newWinner);
|
|
||||||
return [...prevQueue, newWinner];
|
return [...prevQueue, newWinner];
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -110,8 +70,6 @@ const LotteryWinnersSection = ({
|
||||||
|
|
||||||
// Process queue when a new message is added
|
// Process queue when a new message is added
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("📋 Current Message Queue:", messageQueue);
|
|
||||||
|
|
||||||
if (!isProcessing && messageQueue.length > 0) {
|
if (!isProcessing && messageQueue.length > 0) {
|
||||||
processQueue();
|
processQueue();
|
||||||
}
|
}
|
||||||
|
|
@ -124,10 +82,8 @@ const LotteryWinnersSection = ({
|
||||||
setIsProcessing(true); // Lock processing
|
setIsProcessing(true); // Lock processing
|
||||||
const message = messageQueue[0]; // Get the first message in the queue
|
const message = messageQueue[0]; // Get the first message in the queue
|
||||||
|
|
||||||
console.log("⚙️ Processing Message:", message); // Debug Log 4: Log the message being processed
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await handleMessage(message); // Process the message
|
await handleMessage(message);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error processing message:", error);
|
console.error("Error processing message:", error);
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +94,7 @@ const LotteryWinnersSection = ({
|
||||||
|
|
||||||
// Handle the logic for processing a single WebSocket message
|
// Handle the logic for processing a single WebSocket message
|
||||||
const handleMessage = async (winner: LotteryWinnerDataSimplified) => {
|
const handleMessage = async (winner: LotteryWinnerDataSimplified) => {
|
||||||
console.log("⬇️ Updating Top and Bottom Text:", winner); // Debug Log 5: Log winner data before setting states
|
setStartNumber("00,00,00,00,00");
|
||||||
setIsConfettiActive(false);
|
setIsConfettiActive(false);
|
||||||
setTopText(`${winner.winner_no}-nji ýeňiji saýlanýar`);
|
setTopText(`${winner.winner_no}-nji ýeňiji saýlanýar`);
|
||||||
setBottomText("...");
|
setBottomText("...");
|
||||||
|
|
@ -152,7 +108,6 @@ const LotteryWinnersSection = ({
|
||||||
// Finalize winner selection
|
// Finalize winner selection
|
||||||
setTopText(`${winner.winner_no}-nji ýeňiji`);
|
setTopText(`${winner.winner_no}-nji ýeňiji`);
|
||||||
setBottomText(winner.phone);
|
setBottomText(winner.phone);
|
||||||
console.log("⬇️ Finalized Bottom Text:", winner.phone); // Debug Log 6: Log the final bottomText update
|
|
||||||
|
|
||||||
setWinnerSelectingStatus("selected");
|
setWinnerSelectingStatus("selected");
|
||||||
setIsConfettiActive(true);
|
setIsConfettiActive(true);
|
||||||
|
|
@ -172,16 +127,6 @@ const LotteryWinnersSection = ({
|
||||||
)}
|
)}
|
||||||
<Confetti showConfetti={isConfettiActive} numberOfPieces={300} />
|
<Confetti showConfetti={isConfettiActive} numberOfPieces={300} />
|
||||||
|
|
||||||
{/* Simmulation Button */}
|
|
||||||
{/* <div className="w-full flex justify-center py-4">
|
|
||||||
<button
|
|
||||||
onClick={simulateMessage}
|
|
||||||
className="px-4 py-2 bg-blue-500 text-white rounded"
|
|
||||||
>
|
|
||||||
Simulate Message
|
|
||||||
</button>
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div
|
<div
|
||||||
className="flex flex-col items-center rounded-[32px] gap-[40px]"
|
className="flex flex-col items-center rounded-[32px] gap-[40px]"
|
||||||
|
|
@ -234,7 +179,10 @@ const LotteryWinnersSection = ({
|
||||||
|
|
||||||
<div className="z-10">
|
<div className="z-10">
|
||||||
{currentNumber && (
|
{currentNumber && (
|
||||||
<LotterySlotCounter numberString={currentNumber} />
|
<LotterySlotCounter
|
||||||
|
numberString={currentNumber}
|
||||||
|
startNumber={startNumber}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-6 rounded-[12px] flex-1 w-full items-center justify-center sm:pb-[62px] pb-[32px] px-4">
|
<div className="flex gap-6 rounded-[12px] flex-1 w-full items-center justify-center sm:pb-[62px] pb-[32px] px-4">
|
||||||
|
|
|
||||||
|
|
@ -1,140 +1,115 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Queries } from "@/api/queries";
|
import { useState } from "react";
|
||||||
import { useState, FormEvent } from "react";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useLotteryAuth } from "@/store/useLotteryAuth";
|
import { useForm } from "react-hook-form";
|
||||||
|
import z from "zod";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { Form, FormField, FormItem, FormMessage } from "@/components/ui/form";
|
||||||
|
import { FormControl, FormLabel } from "@mui/material";
|
||||||
|
import { authenticateLottery } from "@/api";
|
||||||
|
|
||||||
|
const lotteryAuthSchema = z.object({
|
||||||
|
phoneNumber: z.string().regex(/^993\d{8}$/, {
|
||||||
|
message: "Dogry telefon belgisini girizin",
|
||||||
|
}),
|
||||||
|
key: z.string().regex(/^.+-\d{10}$/, {
|
||||||
|
message: "Dogry acar sozuni girizin",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
const LotteryAuthForm = () => {
|
const LotteryAuthForm = () => {
|
||||||
const [phone, setPhone] = useState("");
|
const form = useForm<z.infer<typeof lotteryAuthSchema>>({
|
||||||
const [code, setCode] = useState("");
|
resolver: zodResolver(lotteryAuthSchema),
|
||||||
|
});
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const setAuth = useLotteryAuth((state) => state.setAuth);
|
|
||||||
|
|
||||||
const validatePhone = (value: string) => {
|
|
||||||
const phoneRegex = /^993\d{8}$/;
|
|
||||||
const isValid = phoneRegex.test(value);
|
|
||||||
|
|
||||||
return isValid;
|
|
||||||
};
|
|
||||||
|
|
||||||
const validateCode = (value: string) => {
|
|
||||||
const codeRegex = /^.+-\d{10}$/; // Any characters before "-", exactly 10 digits after
|
|
||||||
const isValid = codeRegex.test(value);
|
|
||||||
|
|
||||||
return isValid;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async (e: FormEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setError(null);
|
|
||||||
|
|
||||||
if (!validatePhone(phone)) {
|
|
||||||
setError("Telefon belgisi nädogry");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validateCode(code)) {
|
|
||||||
setError("Açar nädogry");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
async function onSubmit(values: z.infer<typeof lotteryAuthSchema>) {
|
||||||
|
const { phoneNumber, key } = values;
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await Queries.authenticateLottery(phone, code);
|
const response = await authenticateLottery(phoneNumber, key);
|
||||||
|
|
||||||
if (response.errorMessage) {
|
if (response.errorMessage) {
|
||||||
setError(response.errorMessage);
|
setError(response.errorMessage);
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem("lotteryPhone", phone);
|
console.log(response);
|
||||||
localStorage.setItem("lotteryCode", code);
|
document.cookie = `phoneNumber=${phoneNumber};path=/`;
|
||||||
|
document.cookie = `key=${key};path=/`;
|
||||||
setAuth(response, phone, code);
|
|
||||||
router.replace("/lottery");
|
router.replace("/lottery");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Authentication error:", err);
|
|
||||||
setError("Telefon belgisi ýa-da açar nädogry");
|
setError("Telefon belgisi ýa-da açar nädogry");
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handlePhoneChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const value = e.target.value.replace(/\\D/g, "");
|
|
||||||
if (value.length <= 11) {
|
|
||||||
setPhone(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCodeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
setCode(value);
|
|
||||||
// if (value.length <= 12) {
|
|
||||||
// }
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<Form {...form}>
|
||||||
onSubmit={handleSubmit}
|
<form
|
||||||
className="bg-lightSurfaceContainer rounded-[24px] md:p-[40px] sm:p-[24px] p-[16px] w-[530px] flex flex-col gap-[24px]"
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
>
|
className="bg-lightSurfaceContainer rounded-[24px] md:p-[40px] sm:p-[24px] p-[16px] w-[530px] flex flex-col gap-[24px]"
|
||||||
<h1 className="md:text-display3 sm:text-[32px] text-[24px] font-[500] md:leading-display3">
|
|
||||||
Giriş
|
|
||||||
</h1>
|
|
||||||
<div className="flex flex-col gap-[16px]">
|
|
||||||
<div className="flex flex-col gap-[8px]">
|
|
||||||
<label
|
|
||||||
htmlFor="phone"
|
|
||||||
className="font-base-medium text-lightOnSurface cursor-pointer"
|
|
||||||
>
|
|
||||||
Telefon
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
value={phone}
|
|
||||||
onChange={handlePhoneChange}
|
|
||||||
className="px-[16px] py-[12px] bg-lightPrimaryContainer rounded-[12px] outline-none text-lightOnSurfaceVariant text-textSmall leading-textSmall"
|
|
||||||
placeholder="99363XXXXXX"
|
|
||||||
required
|
|
||||||
id="phone"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-[8px]">
|
|
||||||
<label
|
|
||||||
htmlFor="code"
|
|
||||||
className="font-base-medium text-lightOnSurface cursor-pointer"
|
|
||||||
>
|
|
||||||
Açar
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={code}
|
|
||||||
onChange={handleCodeChange}
|
|
||||||
className="px-[16px] py-[12px] bg-lightPrimaryContainer rounded-[12px] outline-none text-lightOnSurfaceVariant text-textSmall leading-textSmall"
|
|
||||||
placeholder="C5-0105639808"
|
|
||||||
required
|
|
||||||
id="code"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{error && (
|
|
||||||
<p className="text-lightError text-textSmall leading-textSmall">
|
|
||||||
{error}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={isLoading || !phone || !code}
|
|
||||||
className="sm:text-textLarge sm:leading-textLarge text-[16px] leading-[24px] sm:py-[12px] py-[8px] w-full flex justify-center items-center rounded-[12px] bg-lightPrimary font-medium text-lightOnPrimary disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
{isLoading ? "Ýüklenilýär..." : "Giriş"}
|
<h1 className="md:text-display3 sm:text-[32px] text-[24px] font-[500] md:leading-display3">
|
||||||
</button>
|
Giriş
|
||||||
</form>
|
</h1>
|
||||||
|
<div className="flex flex-col gap-[16px]">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="phoneNumber"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-col gap-[8px]">
|
||||||
|
<FormLabel className="font-base-medium text-lightOnSurface">
|
||||||
|
Telefon
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<input
|
||||||
|
{...field}
|
||||||
|
className="px-[16px] py-[12px] bg-lightPrimaryContainer rounded-[12px] outline-none text-lightOnSurfaceVariant text-textSmall leading-textSmall"
|
||||||
|
placeholder="99363XXXXXX"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage className="text-red-500" />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="key"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-col gap-[8px]">
|
||||||
|
<FormLabel className="font-base-medium text-lightOnSurface">
|
||||||
|
Açar
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<input
|
||||||
|
{...field}
|
||||||
|
className="px-[16px] py-[12px] bg-lightPrimaryContainer rounded-[12px] outline-none text-lightOnSurfaceVariant text-textSmall leading-textSmall"
|
||||||
|
placeholder="C5-0105639808"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage className="text-red-500" />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{error && (
|
||||||
|
<p className="text-lightError text-textSmall leading-textSmall">
|
||||||
|
{error}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="sm:text-textLarge sm:leading-textLarge text-[16px] leading-[24px] sm:py-[12px] py-[8px] w-full flex justify-center items-center rounded-[12px] bg-lightPrimary font-medium text-lightOnPrimary disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{isLoading ? "Ýüklenilýär..." : "Giriş"}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,31 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { calculateTimeLeft } from "@/lib/hooks/useCalculateTimeLeft";
|
import { calculateTimeLeft } from "@/lib/hooks/useCalculateTimeLeft";
|
||||||
|
import { useLotteryStatus } from "@/store/store";
|
||||||
import React, { useState, useEffect, Dispatch, SetStateAction } from "react";
|
import React, { useState, useEffect, Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
interface LotteryCountDownProps {
|
interface LotteryCountDownProps {
|
||||||
startDate: string; // Event start date in "YYYY-MM-DD HH:mm:ss" format
|
startDate: string; // Event start date in "YYYY-MM-DD HH:mm:ss" format
|
||||||
endDate: string; // Event end date in "YYYY-MM-DD HH:mm:ss" format
|
endDate: string; // Event end date in "YYYY-MM-DD HH:mm:ss" format
|
||||||
lotteryStatus: string;
|
lotteryStatus: "Upcoming" | "Ongoing" | "Finished";
|
||||||
setLotteryStatus: Dispatch<
|
|
||||||
SetStateAction<"not-started" | "started" | "ended">
|
|
||||||
>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const LotteryCountDown: React.FC<LotteryCountDownProps> = ({
|
const LotteryCountDown: React.FC<LotteryCountDownProps> = ({
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
lotteryStatus,
|
lotteryStatus,
|
||||||
setLotteryStatus,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [timeLeft, setTimeLeft] = useState({
|
const [timeLeft, setTimeLeft] = useState({
|
||||||
hours: 0,
|
hours: 0,
|
||||||
minutes: 0,
|
minutes: 0,
|
||||||
seconds: 0,
|
seconds: 0,
|
||||||
});
|
});
|
||||||
|
const { status, setStatus } = useLotteryStatus();
|
||||||
console.log(lotteryStatus);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
setStatus(lotteryStatus);
|
||||||
|
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
if (lotteryStatus === "not-started") {
|
if (lotteryStatus === "Upcoming") {
|
||||||
const timeToStart = calculateTimeLeft(startDate);
|
const timeToStart = calculateTimeLeft(startDate);
|
||||||
setTimeLeft(timeToStart);
|
setTimeLeft(timeToStart);
|
||||||
|
|
||||||
|
|
@ -37,9 +34,9 @@ const LotteryCountDown: React.FC<LotteryCountDownProps> = ({
|
||||||
timeToStart.minutes === 0 &&
|
timeToStart.minutes === 0 &&
|
||||||
timeToStart.seconds === 0
|
timeToStart.seconds === 0
|
||||||
) {
|
) {
|
||||||
setLotteryStatus("started"); // Update status to "started"
|
setStatus("Ongoing"); // Update status to "started"
|
||||||
}
|
}
|
||||||
} else if (lotteryStatus === "started") {
|
} else if (lotteryStatus === "Ongoing") {
|
||||||
const timeToEnd = calculateTimeLeft(endDate);
|
const timeToEnd = calculateTimeLeft(endDate);
|
||||||
setTimeLeft(timeToEnd);
|
setTimeLeft(timeToEnd);
|
||||||
|
|
||||||
|
|
@ -48,32 +45,32 @@ const LotteryCountDown: React.FC<LotteryCountDownProps> = ({
|
||||||
timeToEnd.minutes === 0 &&
|
timeToEnd.minutes === 0 &&
|
||||||
timeToEnd.seconds === 0
|
timeToEnd.seconds === 0
|
||||||
) {
|
) {
|
||||||
setLotteryStatus("ended"); // Update status to "finished"
|
setStatus("Finished"); // Update status to "finished"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
return () => clearInterval(timer); // Clean up interval on component unmount
|
return () => clearInterval(timer); // Clean up interval on component unmount
|
||||||
}, [startDate, endDate, lotteryStatus, setLotteryStatus]);
|
}, [startDate, endDate, lotteryStatus]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-lightPrimaryContainer sm:p-6 py-3 flex flex-col w-full md:gap-2 rounded-[12px] sm:gap-3 gap-0 text-lightOnPrimaryContainer">
|
<div className="bg-lightPrimaryContainer sm:p-6 py-3 flex flex-col w-full md:gap-2 rounded-[12px] sm:gap-3 gap-0 text-lightOnPrimaryContainer">
|
||||||
<h3 className="text-center md:font-heading-1-regular sm:text-[32px] sm:leading-[40px] text-[20px] leading-[28px] text-lightOnSurface">
|
<h3 className="text-center md:font-heading-1-regular sm:text-[32px] sm:leading-[40px] text-[20px] leading-[28px] text-lightOnSurface">
|
||||||
{lotteryStatus === "started"
|
{status === "Ongoing"
|
||||||
? "Bije dowam edýär"
|
? "Bije dowam edýär"
|
||||||
: lotteryStatus === "ended"
|
: status === "Finished"
|
||||||
? "Bije tamamlandy"
|
? "Bije tamamlandy"
|
||||||
: "Bije"}
|
: "Bije"}
|
||||||
</h3>
|
</h3>
|
||||||
{/* LotteryCountDown */}
|
{/* LotteryCountDown */}
|
||||||
{lotteryStatus === "not-started" && (
|
{status === "Upcoming" && (
|
||||||
<div className="flex items-center sm:gap-6 gap-2 justify-between">
|
<div className="flex items-center sm:gap-6 gap-2 justify-between">
|
||||||
<div className="flex flex-col items-center justify-center flex-1 sm:p-6 p-4 sm:pb-3">
|
<div className="flex flex-col items-center justify-center flex-1 sm:p-6 p-4 sm:pb-3">
|
||||||
<h3 className="md:text-[80px] sm:text-[56px] text-[28px] md:leading-[88px] sm:leading-[64px] leading-[36px] -tracking-[1%]">
|
<h3 className="md:text-[80px] sm:text-[56px] text-[28px] md:leading-[88px] sm:leading-[64px] leading-[36px] -tracking-[1%]">
|
||||||
{timeLeft.hours}
|
{timeLeft.hours}
|
||||||
</h3>
|
</h3>
|
||||||
<h4 className="font-medium md:text-[20px] sm:text-[18px] text-[14px] sm:leading-[28px] leading-[20px] -tracking-[1%] text-lightOnSurfaceVariant">
|
<h4 className="font-medium md:text-[20px] sm:text-[18px] text-[14px] sm:leading-[28px] leading-[20px] -tracking-[1%] text-lightOnSurfaceVariant">
|
||||||
hours
|
sagat
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -88,7 +85,7 @@ const LotteryCountDown: React.FC<LotteryCountDownProps> = ({
|
||||||
{timeLeft.minutes}
|
{timeLeft.minutes}
|
||||||
</h3>
|
</h3>
|
||||||
<h4 className="font-medium md:text-[20px] sm:text-[18px] text-[14px] sm:leading-[28px] leading-[20px] -tracking-[1%] text-lightOnSurfaceVariant">
|
<h4 className="font-medium md:text-[20px] sm:text-[18px] text-[14px] sm:leading-[28px] leading-[20px] -tracking-[1%] text-lightOnSurfaceVariant">
|
||||||
minutes
|
minut
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -103,7 +100,7 @@ const LotteryCountDown: React.FC<LotteryCountDownProps> = ({
|
||||||
{timeLeft.seconds}
|
{timeLeft.seconds}
|
||||||
</h3>
|
</h3>
|
||||||
<h4 className="font-medium md:text-[20px] sm:text-[18px] text-[14px] sm:leading-[28px] leading-[20px] -tracking-[1%] text-lightOnSurfaceVariant">
|
<h4 className="font-medium md:text-[20px] sm:text-[18px] text-[14px] sm:leading-[28px] leading-[20px] -tracking-[1%] text-lightOnSurfaceVariant">
|
||||||
seconds
|
sekunt
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -111,9 +108,9 @@ const LotteryCountDown: React.FC<LotteryCountDownProps> = ({
|
||||||
|
|
||||||
<div className="flex items-center justify-center text-lightOnSurfaceVariant md:font-heading-1-regular md:text-[20px] sm:text-[18px] sm:leading-[28px] text-[14px] leading-[20px]">
|
<div className="flex items-center justify-center text-lightOnSurfaceVariant md:font-heading-1-regular md:text-[20px] sm:text-[18px] sm:leading-[28px] text-[14px] leading-[20px]">
|
||||||
<span>
|
<span>
|
||||||
{lotteryStatus === "not-started"
|
{status === "Upcoming"
|
||||||
? "- den başlar"
|
? "- den başlar"
|
||||||
: lotteryStatus === "started"
|
: status === "Ongoing"
|
||||||
? "girmek üçin aşakda kodyňyzy giriziň"
|
? "girmek üçin aşakda kodyňyzy giriziň"
|
||||||
: "netijeleri görmek üçin aşakda kodyňyzy giriziň"}
|
: "netijeleri görmek üçin aşakda kodyňyzy giriziň"}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,62 @@
|
||||||
import { useLotteryAuth } from "@/store/useLotteryAuth";
|
"use client";
|
||||||
|
import { useWebsocketLottery } from "@/hooks/useWebSocketLottery";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
title: string;
|
show?: boolean;
|
||||||
content: string;
|
data?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LotteryRulesSection = () => {
|
const WEBSOCKET_URL = "wss://sms.turkmentv.gov.tm/ws/lottery?dst=";
|
||||||
const { lotteryData } = useLotteryAuth();
|
|
||||||
|
const LotteryRulesSection = ({ show = true, data }: IProps) => {
|
||||||
|
const [messageQueue, setMessageQueue] = useState<any[]>([]); // Queue for incoming WebSocket messages
|
||||||
|
const [isProcessing, setIsProcessing] = useState<boolean>(false); // Track if a message is being processed
|
||||||
|
const { subscribeToMessages } = useWebsocketLottery(
|
||||||
|
`${WEBSOCKET_URL}${data?.data.sms_number}`
|
||||||
|
);
|
||||||
|
const [totalParticipants, setTotalParticipants] = useState<number>(
|
||||||
|
data ? data?.data?.bije_count : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!data.errorMessage) {
|
||||||
|
document.cookie = "phoneNumber=;path=/";
|
||||||
|
document.cookie = "key=;path=/";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to WebSocket messages
|
||||||
|
useEffect(() => {
|
||||||
|
const unsubscribe = subscribeToMessages((event) => {
|
||||||
|
// Add new message to the queue
|
||||||
|
setMessageQueue((prevQueue) => {
|
||||||
|
return [...prevQueue, JSON.parse(event.data)];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => unsubscribe();
|
||||||
|
}, [subscribeToMessages]);
|
||||||
|
|
||||||
|
// Process queue when a new message is added
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isProcessing && messageQueue.length > 0) {
|
||||||
|
processQueue();
|
||||||
|
}
|
||||||
|
}, [messageQueue, isProcessing]);
|
||||||
|
|
||||||
|
// Process a single message from the queue
|
||||||
|
const processQueue = async () => {
|
||||||
|
if (isProcessing || messageQueue.length === 0) return;
|
||||||
|
|
||||||
|
setIsProcessing(true); // Lock processing
|
||||||
|
const message = messageQueue[0]; // Get the first message in the queue
|
||||||
|
|
||||||
|
if (!message?.winner_no) {
|
||||||
|
setTotalParticipants(totalParticipants + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
setMessageQueue((prevQueue) => prevQueue.slice(1)); // Remove the processed message from the queue
|
||||||
|
setIsProcessing(false); // Unlock processing
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
|
|
@ -16,10 +66,10 @@ const LotteryRulesSection = () => {
|
||||||
Düzgünleri:
|
Düzgünleri:
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="flex sm:flex-row flex-col gap-6">
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
<div className="flex flex-col bg-lightSurfaceContainer sm:py-4 md:px-8 sm:px-6 py-3 px-4 rounded-[12px] w-full">
|
<div className="flex flex-col bg-lightSurfaceContainer sm:py-4 md:px-8 sm:px-6 py-3 px-4 rounded-[12px] w-full">
|
||||||
<ul className="list-disc flex flex-col md:gap-4 gap-2 pl-[16px]">
|
<ul className="list-disc flex flex-col md:gap-4 gap-2 pl-[16px]">
|
||||||
{lotteryData?.data.rules?.map((item, i) => (
|
{data?.data.rules?.map((item: any, i: number) => (
|
||||||
<li className="font-small-regular" key={i}>
|
<li className="font-small-regular" key={i}>
|
||||||
{item.title}
|
{item.title}
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -27,19 +77,31 @@ const LotteryRulesSection = () => {
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-lightSurfaceContainer flex flex-col gap-4 px-4 py-[12px] rounded-[12px]">
|
||||||
|
<h1 className="md:font-heading-5-regular sm:text-[20px] text-[18px] sm:leading-[24px] leading-[28px]">
|
||||||
|
Umumy gatnaşyjylaryň sany:
|
||||||
|
</h1>
|
||||||
|
<p className="text-[24px]">{totalParticipants}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{show && (
|
||||||
<div className="flex flex-col md:gap-4 sm:gap-2 gap-4 bg-lightSurfaceContainer sm:py-4 md:px-8 sm:px-6 py-3 px-4 rounded-[12px] w-full">
|
<div className="flex flex-col md:gap-4 sm:gap-2 gap-4 bg-lightSurfaceContainer sm:py-4 md:px-8 sm:px-6 py-3 px-4 rounded-[12px] w-full">
|
||||||
<h3 className="md:font-heading-5-regular sm:text-[20px] text-[18px] sm:leading-[24px] leading-[28px]">
|
<h3 className="md:font-heading-5-regular sm:text-[20px] text-[18px] sm:leading-[24px] leading-[28px]">
|
||||||
Siziň kodlaryňyz:
|
Siziň bijeli sanynyz:
|
||||||
</h3>
|
</h3>
|
||||||
<ul className="list-disc flex flex-col md:gap-4 gap-2 pl-[16px]">
|
<ul className="flex flex-col items-center md:gap-4 gap-2">
|
||||||
{lotteryData?.user_lottery_numbers.map((item, i) => (
|
{data?.user_lottery_numbers.map((item: any, i: number) => (
|
||||||
<li className="font-small-regular" key={i}>
|
<li
|
||||||
|
className="text-[24px] text-[#46464F] md:text-[48px] lg:text-[80px] list-none"
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
{item}
|
{item}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,26 @@
|
||||||
'use client';
|
"use client";
|
||||||
import Image from 'next/image';
|
import Image from "next/image";
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import SlotCounter, { SlotCounterRef } from 'react-slot-counter';
|
import SlotCounter, { SlotCounterRef } from "react-slot-counter";
|
||||||
import { useMediaQuery } from 'usehooks-ts';
|
import { useMediaQuery } from "usehooks-ts";
|
||||||
|
|
||||||
interface LotterySlotCounterProps {
|
interface LotterySlotCounterProps {
|
||||||
numberString: string;
|
numberString: string;
|
||||||
|
startNumber: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LotterySlotCounter = ({ numberString }: LotterySlotCounterProps) => {
|
const LotterySlotCounter = ({
|
||||||
const [formattedNumber, setFormattedNumber] = useState(numberString);
|
numberString,
|
||||||
|
startNumber,
|
||||||
|
}: LotterySlotCounterProps) => {
|
||||||
|
const [formattedNumber, setFormattedNumber] = useState("00,00,00,00,00");
|
||||||
const slotCounterRef = useRef<SlotCounterRef>(null); // Ref for manual control
|
const slotCounterRef = useRef<SlotCounterRef>(null); // Ref for manual control
|
||||||
const isFirstRender = useRef(true); // Ref to track the initial render
|
const isFirstRender = useRef(true); // Ref to track the initial render
|
||||||
|
|
||||||
const tablet = useMediaQuery('(max-width: 769px)');
|
const mobile = useMediaQuery("(max-width: 426px)");
|
||||||
const mobile = useMediaQuery('(max-width: 426px)');
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const formatted = numberString.replace(/-/g, ',');
|
const formatted = numberString.replace(/-/g, ",");
|
||||||
setFormattedNumber(formatted);
|
setFormattedNumber(formatted);
|
||||||
|
|
||||||
// Skip animation on the first render
|
// Skip animation on the first render
|
||||||
|
|
@ -74,22 +77,24 @@ const LotterySlotCounter = ({ numberString }: LotterySlotCounterProps) => {
|
||||||
className="flex items-center h-fit md:max-w-[1132px] sm:max-w-[640px] max-w-[400px] w-full justify-center text-white min-[1025px]:py-4 md:px-6 rounded-full overflow-y-hidden overflow-x-visible relative border-4 border-lightPrimary"
|
className="flex items-center h-fit md:max-w-[1132px] sm:max-w-[640px] max-w-[400px] w-full justify-center text-white min-[1025px]:py-4 md:px-6 rounded-full overflow-y-hidden overflow-x-visible relative border-4 border-lightPrimary"
|
||||||
style={{
|
style={{
|
||||||
background:
|
background:
|
||||||
'linear-gradient(180deg, #454673 0%, #575992 10.5%, #575992 90%, #454673 100%)',
|
"linear-gradient(180deg, #454673 0%, #575992 10.5%, #575992 90%, #454673 100%)",
|
||||||
boxShadow: '0px 4px 4px 0px #00000040',
|
boxShadow: "0px 4px 4px 0px #00000040",
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{/* Highlight */}
|
{/* Highlight */}
|
||||||
<div
|
<div
|
||||||
className="absolute top-[50%] -translate-y-1/2 left-0 w-full h-full"
|
className="absolute top-[50%] -translate-y-1/2 left-0 w-full h-full"
|
||||||
style={{
|
style={{
|
||||||
background:
|
background:
|
||||||
'linear-gradient(180deg, rgba(87, 89, 146, 0) 0%, #7274AB 50%, rgba(87, 89, 146, 0) 100%)',
|
"linear-gradient(180deg, rgba(87, 89, 146, 0) 0%, #7274AB 50%, rgba(87, 89, 146, 0) 100%)",
|
||||||
}}></div>
|
}}
|
||||||
|
></div>
|
||||||
|
|
||||||
<div className="z-10">
|
<div className="z-10">
|
||||||
<SlotCounter
|
<SlotCounter
|
||||||
ref={slotCounterRef}
|
ref={slotCounterRef}
|
||||||
value={formattedNumber}
|
value={formattedNumber}
|
||||||
startValue={'00,00,00,00,00'}
|
startValue={startNumber}
|
||||||
charClassName="rolling-number"
|
charClassName="rolling-number"
|
||||||
separatorClassName="slot-seperator"
|
separatorClassName="slot-seperator"
|
||||||
duration={3}
|
duration={3}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import Image from 'next/image';
|
import Image from "next/image";
|
||||||
import { Dispatch, SetStateAction, useState } from 'react';
|
import { Dispatch, SetStateAction, useState } from "react";
|
||||||
import Confetti from 'react-confetti';
|
import Confetti from "react-confetti";
|
||||||
import { useWindowSize } from 'react-use';
|
import { useWindowSize } from "react-use";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
setWinners: Dispatch<SetStateAction<number[]>>;
|
setWinners: Dispatch<SetStateAction<number[]>>;
|
||||||
|
|
@ -72,12 +72,12 @@ const SpinWheel = ({ setWinners }: IProps) => {
|
||||||
tweenDuration={10000}
|
tweenDuration={10000}
|
||||||
run={true}
|
run={true}
|
||||||
colors={[
|
colors={[
|
||||||
'linear-gradient(45deg, #5D5D72, #8589DE)',
|
"linear-gradient(45deg, #5D5D72, #8589DE)",
|
||||||
'linear-gradient(45deg, #E1E0FF, #575992)',
|
"linear-gradient(45deg, #E1E0FF, #575992)",
|
||||||
'#8589DE',
|
"#8589DE",
|
||||||
'#575992',
|
"#575992",
|
||||||
'#E1E0FF',
|
"#E1E0FF",
|
||||||
'#BA1A1A',
|
"#BA1A1A",
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -85,7 +85,7 @@ const SpinWheel = ({ setWinners }: IProps) => {
|
||||||
<div className="relative rounded-full md:max-w-[554px] md:max-h-[554px] w-[276px] h-[276px] md:w-full md:h-full">
|
<div className="relative rounded-full md:max-w-[554px] md:max-h-[554px] w-[276px] h-[276px] md:w-full md:h-full">
|
||||||
{/* Wheel triangle */}
|
{/* Wheel triangle */}
|
||||||
<Image
|
<Image
|
||||||
src={'/wheel-triangle.svg'}
|
src={"/wheel-triangle.svg"}
|
||||||
alt="wheel"
|
alt="wheel"
|
||||||
height={34}
|
height={34}
|
||||||
width={35}
|
width={35}
|
||||||
|
|
@ -96,12 +96,13 @@ const SpinWheel = ({ setWinners }: IProps) => {
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
transform: `rotate(${rotation}deg)`,
|
transform: `rotate(${rotation}deg)`,
|
||||||
transition: isSpinning ? 'transform 5s ease-out' : '',
|
transition: isSpinning ? "transform 5s ease-out" : "",
|
||||||
}}
|
}}
|
||||||
className="lg:p-3 p-2 bg-[#5D5D72] rounded-full overflow-hidden">
|
className="lg:p-3 p-2 bg-[#5D5D72] rounded-full overflow-hidden"
|
||||||
|
>
|
||||||
<div className="lg:p-[15px] p-[10px] bg-[#8589DE] rounded-full ">
|
<div className="lg:p-[15px] p-[10px] bg-[#8589DE] rounded-full ">
|
||||||
<Image
|
<Image
|
||||||
src={'/wheel-circle-inner.png'}
|
src={"/wheel-circle-inner.png"}
|
||||||
alt="wheel"
|
alt="wheel"
|
||||||
height={530}
|
height={530}
|
||||||
width={530}
|
width={530}
|
||||||
|
|
@ -124,14 +125,15 @@ const SpinWheel = ({ setWinners }: IProps) => {
|
||||||
disabled={isSpinning || isCountingDown}
|
disabled={isSpinning || isCountingDown}
|
||||||
className={`mt-6 px-6 py-3 rounded-full text-white font-bold ${
|
className={`mt-6 px-6 py-3 rounded-full text-white font-bold ${
|
||||||
isSpinning || isCountingDown
|
isSpinning || isCountingDown
|
||||||
? 'bg-gray-400 cursor-not-allowed'
|
? "bg-gray-400 cursor-not-allowed"
|
||||||
: 'bg-blue-500 hover:bg-blue-700'
|
: "bg-blue-500 hover:bg-blue-700"
|
||||||
}`}>
|
}`}
|
||||||
|
>
|
||||||
{isCountingDown
|
{isCountingDown
|
||||||
? `Starting in ${countdown}...`
|
? `Starting in ${countdown}...`
|
||||||
: isSpinning
|
: isSpinning
|
||||||
? 'Spinning...'
|
? "Spinning..."
|
||||||
: 'Spin the Wheel'}
|
: "Spin the Wheel"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import {
|
import {
|
||||||
LotteryWinnerData,
|
|
||||||
LotteryWinnerDataSimplified,
|
LotteryWinnerDataSimplified,
|
||||||
} from "@/typings/lottery/lottery.types";
|
} from "@/typings/lottery/lottery.types";
|
||||||
import LotteryWinner from "./LotteryWinner";
|
import LotteryWinner from "./LotteryWinner";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
|
|
||||||
const LotteryWinnersList = ({
|
const LotteryWinnersList = ({
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
'use client';
|
"use client";
|
||||||
import { Queries } from '@/api/queries';
|
import { Queries } from "@/api/queries";
|
||||||
|
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from "uuid";
|
||||||
import { useState, useEffect, useContext, Dispatch, SetStateAction } from 'react';
|
import { useState, useEffect, useContext } from "react";
|
||||||
import { Answer, 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 {
|
interface Message {
|
||||||
answer: string;
|
answer: string;
|
||||||
|
|
@ -133,11 +132,11 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
// Function to handle incoming WebSocket message and update winnersData
|
// Function to handle incoming WebSocket message and update winnersData
|
||||||
const handleOnMessage = (message: Message) => {
|
const handleOnMessage = (message: Message) => {
|
||||||
if (!winnersData) {
|
if (!winnersData) {
|
||||||
console.error('winnersData is undefined');
|
console.error("winnersData is undefined");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('updating winnersData');
|
console.log("updating winnersData");
|
||||||
|
|
||||||
// Update the winnersData by matching phone with starred_src from the message
|
// Update the winnersData by matching phone with starred_src from the message
|
||||||
setWinnersData((prevWinnersData) => {
|
setWinnersData((prevWinnersData) => {
|
||||||
|
|
@ -162,7 +161,10 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
|
|
||||||
// Calculate the new correct_answers_time by summing serial_number_for_correct
|
// Calculate the new correct_answers_time by summing serial_number_for_correct
|
||||||
const updatedCorrectAnswersTime = updatedAnswers
|
const updatedCorrectAnswersTime = updatedAnswers
|
||||||
.reduce((sum, answer) => sum + answer.serial_number_for_correct, 0)
|
.reduce(
|
||||||
|
(sum, answer) => sum + answer.serial_number_for_correct,
|
||||||
|
0
|
||||||
|
)
|
||||||
.toString();
|
.toString();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -182,7 +184,7 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('winnersData is updated');
|
console.log("winnersData is updated");
|
||||||
};
|
};
|
||||||
|
|
||||||
return winnersData?.data.length !== 0 ? (
|
return winnersData?.data.length !== 0 ? (
|
||||||
|
|
@ -231,9 +233,10 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
{winnersData?.data.map((winner, id) => (
|
{winnersData?.data.map((winner, id) => (
|
||||||
<div
|
<div
|
||||||
className={`flex border-b border-fillTableStrokeTableRow ${
|
className={`flex border-b border-fillTableStrokeTableRow ${
|
||||||
id % 2 === 0 ? 'bg-fillTableRow' : 'bg-fillTableRow2'
|
id % 2 === 0 ? "bg-fillTableRow" : "bg-fillTableRow2"
|
||||||
}`}
|
}`}
|
||||||
key={v4()}>
|
key={v4()}
|
||||||
|
>
|
||||||
<div className="flex justify-center items-center text-base text-textBlack leading-[125%] max-w-[54px] w-[100%] pl-6 pr-3 py-5">
|
<div className="flex justify-center items-center text-base text-textBlack leading-[125%] max-w-[54px] w-[100%] pl-6 pr-3 py-5">
|
||||||
<span>{id + 1}</span>
|
<span>{id + 1}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -246,46 +249,35 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
<div className="flex justify-center items-center gap-6 text-base text-textGray leading-[125%] w-[100%] px-3 py-5">
|
<div className="flex justify-center items-center gap-6 text-base text-textGray leading-[125%] w-[100%] px-3 py-5">
|
||||||
{questionsData
|
{questionsData
|
||||||
? questionsData.map((question) => {
|
? questionsData.map((question) => {
|
||||||
// const matchingAnswer = winner.client.answers.find(
|
|
||||||
// (answer) => answer.question_id === question.id,
|
|
||||||
// );
|
|
||||||
|
|
||||||
const matchingAnswer =
|
const matchingAnswer =
|
||||||
winner.client.answers.find(
|
winner.client.answers.find(
|
||||||
(answer) => answer.question_id === question.id && answer.score > 0,
|
(answer) =>
|
||||||
|
answer.question_id === question.id &&
|
||||||
|
answer.score > 0
|
||||||
) ||
|
) ||
|
||||||
winner.client.answers.find(
|
winner.client.answers.find(
|
||||||
(answer) => answer.question_id === question.id,
|
(answer) => answer.question_id === question.id
|
||||||
);
|
);
|
||||||
|
|
||||||
// const matchingAnswer = () => {
|
|
||||||
// const duplicateAnswers: Answer[] = [];
|
|
||||||
|
|
||||||
// winner.client.answers.map((answer) =>
|
|
||||||
// answer.question_id === question.id
|
|
||||||
// ? duplicateAnswers.push(answer)
|
|
||||||
// : null,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(duplicateAnswers);
|
|
||||||
// };
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
key={v4()}
|
key={v4()}
|
||||||
className={`text-sm font-semibold leading-[125%] ${
|
className={`text-sm font-semibold leading-[125%] ${
|
||||||
matchingAnswer && matchingAnswer.serial_number_for_correct !== 0
|
matchingAnswer &&
|
||||||
? 'text-fillGreen'
|
matchingAnswer.serial_number_for_correct !== 0
|
||||||
|
? "text-fillGreen"
|
||||||
: matchingAnswer &&
|
: matchingAnswer &&
|
||||||
matchingAnswer?.serial_number_for_correct === 0
|
matchingAnswer?.serial_number_for_correct ===
|
||||||
? 'text-fillRed'
|
0
|
||||||
: 'text-textLight'
|
? "text-fillRed"
|
||||||
}`}>
|
: "text-textLight"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{matchingAnswer && matchingAnswer.score !== 0
|
{matchingAnswer && matchingAnswer.score !== 0
|
||||||
? matchingAnswer.serial_number_for_correct
|
? matchingAnswer.serial_number_for_correct
|
||||||
: matchingAnswer && matchingAnswer?.score === 0
|
: matchingAnswer && matchingAnswer?.score === 0
|
||||||
? 'X'
|
? "X"
|
||||||
: '0'}
|
: "0"}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -345,9 +337,10 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
{winnersData?.data.map((winner, id) => (
|
{winnersData?.data.map((winner, id) => (
|
||||||
<div
|
<div
|
||||||
className={`flex border-b border-fillTableStrokeTableRow items-center p-[8px] gap-[8px] ${
|
className={`flex border-b border-fillTableStrokeTableRow items-center p-[8px] gap-[8px] ${
|
||||||
id % 2 === 0 ? 'bg-fillTableRow' : 'bg-fillTableRow2'
|
id % 2 === 0 ? "bg-fillTableRow" : "bg-fillTableRow2"
|
||||||
}`}
|
}`}
|
||||||
key={v4()}>
|
key={v4()}
|
||||||
|
>
|
||||||
<div className="flex items-center text-base text-textBlack leading-[125%] max-w-[14px] w-[100%] ">
|
<div className="flex items-center text-base text-textBlack leading-[125%] max-w-[14px] w-[100%] ">
|
||||||
<span>{id + 1}</span>
|
<span>{id + 1}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -385,26 +378,36 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
<div className="flex justify-center items-center gap-[4px] text-xs text-textGray leading-[125%] w-fit">
|
<div className="flex justify-center items-center gap-[4px] text-xs text-textGray leading-[125%] w-fit">
|
||||||
{questionsData
|
{questionsData
|
||||||
? questionsData.map((question) => {
|
? questionsData.map((question) => {
|
||||||
const matchingAnswer = winner.client.answers.find(
|
const matchingAnswer =
|
||||||
(answer) => answer.question_id === question.id,
|
winner.client.answers.find(
|
||||||
);
|
(answer) =>
|
||||||
|
answer.question_id === question.id &&
|
||||||
|
answer.score > 0
|
||||||
|
) ||
|
||||||
|
winner.client.answers.find(
|
||||||
|
(answer) => answer.question_id === question.id
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
key={v4()}
|
key={v4()}
|
||||||
className={`text-sm font-semibold leading-[125%] ${
|
className={`text-sm font-semibold leading-[125%] ${
|
||||||
matchingAnswer && matchingAnswer.serial_number_for_correct !== 0
|
matchingAnswer &&
|
||||||
? 'text-fillGreen'
|
matchingAnswer.serial_number_for_correct !==
|
||||||
|
0
|
||||||
|
? "text-fillGreen"
|
||||||
: matchingAnswer &&
|
: matchingAnswer &&
|
||||||
matchingAnswer.serial_number_for_correct === 0
|
matchingAnswer.serial_number_for_correct ===
|
||||||
? 'text-fillRed'
|
0
|
||||||
: 'text-textLight'
|
? "text-fillRed"
|
||||||
}`}>
|
: "text-textLight"
|
||||||
{matchingAnswer && matchingAnswer.serial_number_for_correct !== 0
|
}`}
|
||||||
|
>
|
||||||
|
{matchingAnswer && matchingAnswer.score !== 0
|
||||||
? matchingAnswer.serial_number_for_correct
|
? matchingAnswer.serial_number_for_correct
|
||||||
: matchingAnswer &&
|
: matchingAnswer &&
|
||||||
matchingAnswer.serial_number_for_correct === 0
|
matchingAnswer?.score === 0
|
||||||
? 'X'
|
? "X"
|
||||||
: '0'}
|
: "0"}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -436,7 +439,9 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-[10px] items-center ">
|
<div className="flex gap-[10px] items-center ">
|
||||||
<div className="border border-fillBlue rounded-full min-w-[32px] h-[32px] flex justify-center items-center">
|
<div className="border border-fillBlue rounded-full min-w-[32px] h-[32px] flex justify-center items-center">
|
||||||
<span className="text-fillBlue text-sm leading-[125%] ">100</span>
|
<span className="text-fillBlue text-sm leading-[125%] ">
|
||||||
|
100
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span className="text-base leading-[120%] text-textLight w-full">
|
<span className="text-base leading-[120%] text-textLight w-full">
|
||||||
|
|
@ -445,7 +450,9 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-[10px] items-center">
|
<div className="flex gap-[10px] items-center">
|
||||||
<div className="flex justify-center items-center min-w-[32px]">
|
<div className="flex justify-center items-center min-w-[32px]">
|
||||||
<span className="text-xl font-semibold leading-[120%] text-fillGreen ">1</span>
|
<span className="text-xl font-semibold leading-[120%] text-fillGreen ">
|
||||||
|
1
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-base leading-[120%] text-textLight">
|
<span className="text-base leading-[120%] text-textLight">
|
||||||
Dogry jogaplara näçinji bolup jogap berdi
|
Dogry jogaplara näçinji bolup jogap berdi
|
||||||
|
|
@ -453,7 +460,9 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-[10px] items-center ">
|
<div className="flex gap-[10px] items-center ">
|
||||||
<div className="flex justify-center items-center min-w-[32px]">
|
<div className="flex justify-center items-center min-w-[32px]">
|
||||||
<span className="text-xl font-semibold leading-[120%] text-fillRed">X</span>
|
<span className="text-xl font-semibold leading-[120%] text-fillRed">
|
||||||
|
X
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-base leading-[120%] text-textLight">
|
<span className="text-base leading-[120%] text-textLight">
|
||||||
Soraga nädogry jogap berdi
|
Soraga nädogry jogap berdi
|
||||||
|
|
@ -462,9 +471,13 @@ const QuizWinnerTable = ({ quizId, quizFinished, smsNumber }: IProps) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-[10px] items-center ">
|
<div className="flex gap-[10px] items-center ">
|
||||||
<div className="flex justify-center items-center min-w-[32px]">
|
<div className="flex justify-center items-center min-w-[32px]">
|
||||||
<span className="text-xl font-semibold leading-[120%] text-textLight">0</span>
|
<span className="text-xl font-semibold leading-[120%] text-textLight">
|
||||||
|
0
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-base leading-[120%] text-textLight">Soraga jogap ugratmady</span>
|
<span className="text-base leading-[120%] text-textLight">
|
||||||
|
Soraga jogap ugratmady
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
import LotteryHeader from "@/components/lottery/LotteryHeader";
|
||||||
|
import LotteryRulesSection from "@/components/lottery/rules/LotteryRulesSection";
|
||||||
|
import LotteryCountDown from "@/components/lottery/countDown/LotteryCountDown";
|
||||||
|
import { getTossData } from "@/api/queries";
|
||||||
|
import { getLotteryStatus } from "@/lib/actions";
|
||||||
|
import LotteryWinners from "../lottery/LotteryWinners";
|
||||||
|
|
||||||
|
const TossPage = async ({
|
||||||
|
type,
|
||||||
|
id,
|
||||||
|
}: {
|
||||||
|
type: "bije" | "cekilis";
|
||||||
|
id: string;
|
||||||
|
}) => {
|
||||||
|
const tossData = await getTossData({ type, id });
|
||||||
|
|
||||||
|
const status = await getLotteryStatus(
|
||||||
|
tossData?.data?.start_time,
|
||||||
|
tossData?.data?.end_time
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{tossData?.data ? (
|
||||||
|
<div className="flex flex-col md:gap-[128px] gap-[80px] font-roboto md:pt-[64px] sm:pt-[48px] pt-[40px] ms:pb-[128px] pb-[80px] text-lightOnSurface">
|
||||||
|
{tossData && (
|
||||||
|
<div className="flex flex-col sm:gap-[64px] gap-[40px]">
|
||||||
|
<LotteryHeader
|
||||||
|
title={tossData.data.title}
|
||||||
|
description={tossData.data.description}
|
||||||
|
image={tossData.data.image}
|
||||||
|
smsCode={tossData.data.sms_code}
|
||||||
|
startDate={tossData.data.start_time}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{status === "Upcoming" && (
|
||||||
|
<div className="container">
|
||||||
|
<LotteryCountDown
|
||||||
|
lotteryStatus={status}
|
||||||
|
endDate={tossData.data.end_time}
|
||||||
|
startDate={tossData.data.start_time}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<LotteryRulesSection show={false} data={tossData} />
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-10">
|
||||||
|
<LotteryWinners data={tossData} lotteryStatus={status} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col items-center md:gap-[128px] gap-[80px] font-roboto md:pt-[64px] sm:pt-[48px] pt-[40px] ms:pb-[128px] pb-[80px] text-lightOnSurface">
|
||||||
|
<h1 className="text-[22px]">{tossData?.errorMessage}</h1>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TossPage;
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
ControllerProps,
|
||||||
|
FieldPath,
|
||||||
|
FieldValues,
|
||||||
|
FormProvider,
|
||||||
|
useFormContext,
|
||||||
|
} from "react-hook-form"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Label } from "@/components/ui/label"
|
||||||
|
|
||||||
|
const Form = FormProvider
|
||||||
|
|
||||||
|
type FormFieldContextValue<
|
||||||
|
TFieldValues extends FieldValues = FieldValues,
|
||||||
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||||
|
> = {
|
||||||
|
name: TName
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||||
|
{} as FormFieldContextValue
|
||||||
|
)
|
||||||
|
|
||||||
|
const FormField = <
|
||||||
|
TFieldValues extends FieldValues = FieldValues,
|
||||||
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||||
|
>({
|
||||||
|
...props
|
||||||
|
}: ControllerProps<TFieldValues, TName>) => {
|
||||||
|
return (
|
||||||
|
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||||
|
<Controller {...props} />
|
||||||
|
</FormFieldContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const useFormField = () => {
|
||||||
|
const fieldContext = React.useContext(FormFieldContext)
|
||||||
|
const itemContext = React.useContext(FormItemContext)
|
||||||
|
const { getFieldState, formState } = useFormContext()
|
||||||
|
|
||||||
|
const fieldState = getFieldState(fieldContext.name, formState)
|
||||||
|
|
||||||
|
if (!fieldContext) {
|
||||||
|
throw new Error("useFormField should be used within <FormField>")
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = itemContext
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: fieldContext.name,
|
||||||
|
formItemId: `${id}-form-item`,
|
||||||
|
formDescriptionId: `${id}-form-item-description`,
|
||||||
|
formMessageId: `${id}-form-item-message`,
|
||||||
|
...fieldState,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FormItemContextValue = {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormItemContext = React.createContext<FormItemContextValue>(
|
||||||
|
{} as FormItemContextValue
|
||||||
|
)
|
||||||
|
|
||||||
|
const FormItem = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
const id = React.useId()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItemContext.Provider value={{ id }}>
|
||||||
|
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
||||||
|
</FormItemContext.Provider>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
FormItem.displayName = "FormItem"
|
||||||
|
|
||||||
|
const FormLabel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
const { error, formItemId } = useFormField()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label
|
||||||
|
ref={ref}
|
||||||
|
className={cn(error && "text-destructive", className)}
|
||||||
|
htmlFor={formItemId}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
FormLabel.displayName = "FormLabel"
|
||||||
|
|
||||||
|
const FormControl = React.forwardRef<
|
||||||
|
React.ElementRef<typeof Slot>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof Slot>
|
||||||
|
>(({ ...props }, ref) => {
|
||||||
|
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slot
|
||||||
|
ref={ref}
|
||||||
|
id={formItemId}
|
||||||
|
aria-describedby={
|
||||||
|
!error
|
||||||
|
? `${formDescriptionId}`
|
||||||
|
: `${formDescriptionId} ${formMessageId}`
|
||||||
|
}
|
||||||
|
aria-invalid={!!error}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
FormControl.displayName = "FormControl"
|
||||||
|
|
||||||
|
const FormDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
const { formDescriptionId } = useFormField()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
ref={ref}
|
||||||
|
id={formDescriptionId}
|
||||||
|
className={cn("text-[0.8rem] text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
FormDescription.displayName = "FormDescription"
|
||||||
|
|
||||||
|
const FormMessage = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, children, ...props }, ref) => {
|
||||||
|
const { error, formMessageId } = useFormField()
|
||||||
|
const body = error ? String(error?.message) : children
|
||||||
|
|
||||||
|
if (!body) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
ref={ref}
|
||||||
|
id={formMessageId}
|
||||||
|
className={cn("text-[0.8rem] font-medium text-destructive", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{body}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
FormMessage.displayName = "FormMessage"
|
||||||
|
|
||||||
|
export {
|
||||||
|
useFormField,
|
||||||
|
Form,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormMessage,
|
||||||
|
FormField,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const labelVariants = cva(
|
||||||
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Label = React.forwardRef<
|
||||||
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
|
VariantProps<typeof labelVariants>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(labelVariants(), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Label.displayName = LabelPrimitive.Root.displayName
|
||||||
|
|
||||||
|
export { Label }
|
||||||
|
|
@ -14,15 +14,12 @@ export const useWebsocketLottery = (url: string) => {
|
||||||
const setupWebSocket = () => {
|
const setupWebSocket = () => {
|
||||||
if (!isMounted) return;
|
if (!isMounted) return;
|
||||||
|
|
||||||
console.log("🔄 [WebSocket] Connecting...");
|
|
||||||
const socket = new WebSocket(url);
|
const socket = new WebSocket(url);
|
||||||
wsRef.current = socket;
|
wsRef.current = socket;
|
||||||
|
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
if (!isMounted) return;
|
if (!isMounted) return;
|
||||||
|
|
||||||
console.log("✅ [WebSocket] Connected");
|
|
||||||
console.log("🔗 [WebSocket URL]:", url);
|
|
||||||
setWsStatus("connected");
|
setWsStatus("connected");
|
||||||
if (reconnectTimeoutRef.current)
|
if (reconnectTimeoutRef.current)
|
||||||
clearTimeout(reconnectTimeoutRef.current);
|
clearTimeout(reconnectTimeoutRef.current);
|
||||||
|
|
@ -31,21 +28,18 @@ export const useWebsocketLottery = (url: string) => {
|
||||||
socket.onmessage = (event) => {
|
socket.onmessage = (event) => {
|
||||||
if (!isMounted) return;
|
if (!isMounted) return;
|
||||||
|
|
||||||
console.log("📩 [WebSocket] Message received:", event.data);
|
|
||||||
messageListeners.current.forEach((listener) => listener(event));
|
messageListeners.current.forEach((listener) => listener(event));
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.onerror = () => {
|
socket.onerror = () => {
|
||||||
if (!isMounted) return;
|
if (!isMounted) return;
|
||||||
|
|
||||||
console.error("❌ [WebSocket] Error occurred");
|
|
||||||
setWsStatus("error");
|
setWsStatus("error");
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.onclose = () => {
|
socket.onclose = () => {
|
||||||
if (!isMounted) return;
|
if (!isMounted) return;
|
||||||
|
|
||||||
console.log("❌ [WebSocket] Closed");
|
|
||||||
setWsStatus("closed");
|
setWsStatus("closed");
|
||||||
reconnectWebSocket();
|
reconnectWebSocket();
|
||||||
};
|
};
|
||||||
|
|
@ -54,7 +48,6 @@ export const useWebsocketLottery = (url: string) => {
|
||||||
const reconnectWebSocket = () => {
|
const reconnectWebSocket = () => {
|
||||||
if (!isMounted) return;
|
if (!isMounted) return;
|
||||||
|
|
||||||
console.log("🔄 [WebSocket] Reconnecting in 5 seconds...");
|
|
||||||
reconnectTimeoutRef.current = setTimeout(() => {
|
reconnectTimeoutRef.current = setTimeout(() => {
|
||||||
setupWebSocket();
|
setupWebSocket();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
@ -63,7 +56,6 @@ export const useWebsocketLottery = (url: string) => {
|
||||||
setupWebSocket();
|
setupWebSocket();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
console.log("🔌 [WebSocket] Cleaning up...");
|
|
||||||
isMounted = false;
|
isMounted = false;
|
||||||
|
|
||||||
if (wsRef.current) {
|
if (wsRef.current) {
|
||||||
|
|
@ -79,17 +71,14 @@ export const useWebsocketLottery = (url: string) => {
|
||||||
|
|
||||||
const sendPing = useCallback(() => {
|
const sendPing = useCallback(() => {
|
||||||
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
||||||
console.log("📤 [WebSocket] Sending ping");
|
|
||||||
wsRef.current.send(JSON.stringify({ type: "ping" }));
|
wsRef.current.send(JSON.stringify({ type: "ping" }));
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const subscribeToMessages = useCallback(
|
const subscribeToMessages = useCallback(
|
||||||
(listener: (event: MessageEvent) => void) => {
|
(listener: (event: MessageEvent) => void) => {
|
||||||
console.log("👂 [WebSocket] Subscribing to messages");
|
|
||||||
messageListeners.current.push(listener);
|
messageListeners.current.push(listener);
|
||||||
return () => {
|
return () => {
|
||||||
console.log("❌ [WebSocket] Unsubscribing from messages");
|
|
||||||
messageListeners.current = messageListeners.current.filter(
|
messageListeners.current = messageListeners.current.filter(
|
||||||
(l) => l !== listener
|
(l) => l !== listener
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
"use server";
|
||||||
|
|
||||||
|
export async function getLotteryStatus(startTime: string, endTime: string) {
|
||||||
|
const now = new Date();
|
||||||
|
const start = new Date(startTime);
|
||||||
|
const end = new Date(endTime);
|
||||||
|
|
||||||
|
if (now < start) {
|
||||||
|
return "Upcoming";
|
||||||
|
} else if (now >= start && now <= end) {
|
||||||
|
return "Ongoing";
|
||||||
|
} else {
|
||||||
|
return "Finished";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { type ClassValue, clsx } from "clsx"
|
import { clsx, type ClassValue } from "clsx";
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ export interface ILotteryData {
|
||||||
sms_number: string;
|
sms_number: string;
|
||||||
winners: ILotteryWinner[];
|
winners: ILotteryWinner[];
|
||||||
rules: ILotteryRule[] | null;
|
rules: ILotteryRule[] | null;
|
||||||
|
bije_count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILotteryResponse {
|
export interface ILotteryResponse {
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,14 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
"@emotion/styled": "^11.10.6",
|
"@emotion/styled": "^11.10.6",
|
||||||
|
"@hookform/resolvers": "^3.10.0",
|
||||||
"@mui/material": "^5.12.1",
|
"@mui/material": "^5.12.1",
|
||||||
"@mui/x-date-pickers": "^6.5.0",
|
"@mui/x-date-pickers": "^6.5.0",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.2",
|
"@radix-ui/react-alert-dialog": "^1.1.2",
|
||||||
"@radix-ui/react-dialog": "^1.1.2",
|
"@radix-ui/react-dialog": "^1.1.2",
|
||||||
|
"@radix-ui/react-label": "^2.1.2",
|
||||||
"@radix-ui/react-popover": "^1.1.1",
|
"@radix-ui/react-popover": "^1.1.1",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@tanstack/react-query": "^4.32.0",
|
"@tanstack/react-query": "^4.32.0",
|
||||||
"@tanstack/react-query-devtools": "^4.32.0",
|
"@tanstack/react-query-devtools": "^4.32.0",
|
||||||
"@types/node": "18.15.13",
|
"@types/node": "18.15.13",
|
||||||
|
|
@ -24,7 +26,7 @@
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
"autoprefixer": "10.4.14",
|
"autoprefixer": "10.4.14",
|
||||||
"axios": "^1.5.1",
|
"axios": "^1.5.1",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
|
|
@ -38,19 +40,20 @@
|
||||||
"react-day-picker": "^8.10.1",
|
"react-day-picker": "^8.10.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-fast-marquee": "^1.3.5",
|
"react-fast-marquee": "^1.3.5",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-icons": "^4.8.0",
|
"react-icons": "^4.8.0",
|
||||||
"react-player": "^2.12.0",
|
"react-player": "^2.12.0",
|
||||||
"react-slot-counter": "^3.0.4",
|
"react-slot-counter": "^3.0.4",
|
||||||
"react-use": "^17.5.1",
|
"react-use": "^17.5.1",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
"swiper": "^9.2.4",
|
"swiper": "^9.2.4",
|
||||||
"tailwind-merge": "^2.4.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss": "3.3.1",
|
"tailwindcss": "3.3.1",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"usehooks-ts": "^2.9.1",
|
"usehooks-ts": "^2.9.1",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
|
"zod": "^3.24.1",
|
||||||
"zustand": "^5.0.1"
|
"zustand": "^5.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -375,6 +378,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.5.tgz",
|
||||||
"integrity": "sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ=="
|
"integrity": "sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@hookform/resolvers": {
|
||||||
|
"version": "3.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz",
|
||||||
|
"integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react-hook-form": "^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.11.11",
|
"version": "0.11.11",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
|
||||||
|
|
@ -1447,6 +1459,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-arrow": {
|
"node_modules/@radix-ui/react-arrow": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
|
||||||
|
|
@ -1632,6 +1662,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-dialog/node_modules/react-remove-scroll": {
|
"node_modules/@radix-ui/react-dialog/node_modules/react-remove-scroll": {
|
||||||
"version": "2.6.0",
|
"version": "2.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz",
|
||||||
|
|
@ -1737,6 +1785,52 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-label": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-popover": {
|
"node_modules/@radix-ui/react-popover": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz",
|
||||||
|
|
@ -1773,6 +1867,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-popper": {
|
"node_modules/@radix-ui/react-popper": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
|
||||||
|
|
@ -1872,10 +1984,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-compose-refs": "1.1.0"
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
},
|
},
|
||||||
|
|
@ -1889,6 +2002,39 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||||
|
|
@ -2785,22 +2931,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/class-variance-authority": {
|
"node_modules/class-variance-authority": {
|
||||||
"version": "0.7.0",
|
"version": "0.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||||
"integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==",
|
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"clsx": "2.0.0"
|
"clsx": "^2.1.1"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://joebell.co.uk"
|
"url": "https://polar.sh/cva"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/class-variance-authority/node_modules/clsx": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/client-only": {
|
"node_modules/client-only": {
|
||||||
|
|
@ -2812,6 +2951,7 @@
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
|
|
@ -4998,6 +5138,7 @@
|
||||||
"version": "0.408.0",
|
"version": "0.408.0",
|
||||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.408.0.tgz",
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.408.0.tgz",
|
||||||
"integrity": "sha512-8kETAAeWmOvtGIr7HPHm51DXoxlfkNncQ5FZWXR+abX8saQwMYXANWIkUstaYtcKSo/imOe/q+tVFA8ANzdSVA==",
|
"integrity": "sha512-8kETAAeWmOvtGIr7HPHm51DXoxlfkNncQ5FZWXR+abX8saQwMYXANWIkUstaYtcKSo/imOe/q+tVFA8ANzdSVA==",
|
||||||
|
"license": "ISC",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -5803,18 +5944,19 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-hook-form": {
|
"node_modules/react-hook-form": {
|
||||||
"version": "7.45.2",
|
"version": "7.54.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.45.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz",
|
||||||
"integrity": "sha512-9s45OdTaKN+4NSTbXVqeDITd/nwIg++nxJGL8+OD5uf1DxvhsXQ641kaYHk5K28cpIOTYm71O/fYk7rFaygb3A==",
|
"integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.22.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/react-hook-form"
|
"url": "https://opencollective.com/react-hook-form"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.0 || ^17 || ^18"
|
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-icons": {
|
"node_modules/react-icons": {
|
||||||
|
|
@ -6666,9 +6808,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tailwind-merge": {
|
"node_modules/tailwind-merge": {
|
||||||
"version": "2.4.0",
|
"version": "2.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz",
|
||||||
"integrity": "sha512-49AwoOQNKdqKPd9CViyH5wJoSKsCDjUlzL8DxuGp3P1FsGY36NJDAa18jLZcaHAUUuTj+JB8IAo8zWgBNvBF7A==",
|
"integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==",
|
||||||
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/dcastil"
|
"url": "https://github.com/sponsors/dcastil"
|
||||||
|
|
@ -6719,6 +6862,7 @@
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz",
|
||||||
"integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==",
|
"integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==",
|
||||||
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"tailwindcss": ">=3.0.0 || insiders"
|
"tailwindcss": ">=3.0.0 || insiders"
|
||||||
}
|
}
|
||||||
|
|
@ -7322,6 +7466,15 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "3.24.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
|
||||||
|
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/zustand": {
|
"node_modules/zustand": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz",
|
||||||
|
|
@ -7604,6 +7757,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.5.tgz",
|
||||||
"integrity": "sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ=="
|
"integrity": "sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ=="
|
||||||
},
|
},
|
||||||
|
"@hookform/resolvers": {
|
||||||
|
"version": "3.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz",
|
||||||
|
"integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"@humanwhocodes/config-array": {
|
"@humanwhocodes/config-array": {
|
||||||
"version": "0.11.11",
|
"version": "0.11.11",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
|
||||||
|
|
@ -8133,6 +8292,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
|
||||||
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
|
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
|
},
|
||||||
|
"@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"requires": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -8219,6 +8386,14 @@
|
||||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"requires": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-remove-scroll": {
|
"react-remove-scroll": {
|
||||||
"version": "2.6.0",
|
"version": "2.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz",
|
||||||
|
|
@ -8269,6 +8444,24 @@
|
||||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@radix-ui/react-label": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==",
|
||||||
|
"requires": {
|
||||||
|
"@radix-ui/react-primitive": "2.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||||
|
"requires": {
|
||||||
|
"@radix-ui/react-slot": "1.1.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@radix-ui/react-popover": {
|
"@radix-ui/react-popover": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz",
|
||||||
|
|
@ -8289,6 +8482,16 @@
|
||||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||||
"aria-hidden": "^1.1.1",
|
"aria-hidden": "^1.1.1",
|
||||||
"react-remove-scroll": "2.5.7"
|
"react-remove-scroll": "2.5.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"requires": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@radix-ui/react-popper": {
|
"@radix-ui/react-popper": {
|
||||||
|
|
@ -8332,14 +8535,32 @@
|
||||||
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@radix-ui/react-slot": "1.1.0"
|
"@radix-ui/react-slot": "1.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"requires": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@radix-ui/react-slot": {
|
"@radix-ui/react-slot": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||||
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
"integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@radix-ui/react-compose-refs": "1.1.0"
|
"@radix-ui/react-compose-refs": "1.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||||
|
"requires": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@radix-ui/react-use-callback-ref": {
|
"@radix-ui/react-use-callback-ref": {
|
||||||
|
|
@ -8938,18 +9159,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"class-variance-authority": {
|
"class-variance-authority": {
|
||||||
"version": "0.7.0",
|
"version": "0.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||||
"integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==",
|
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"clsx": "2.0.0"
|
"clsx": "^2.1.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"clsx": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"client-only": {
|
"client-only": {
|
||||||
|
|
@ -11111,9 +11325,9 @@
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"react-hook-form": {
|
"react-hook-form": {
|
||||||
"version": "7.45.2",
|
"version": "7.54.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.45.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz",
|
||||||
"integrity": "sha512-9s45OdTaKN+4NSTbXVqeDITd/nwIg++nxJGL8+OD5uf1DxvhsXQ641kaYHk5K28cpIOTYm71O/fYk7rFaygb3A==",
|
"integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"react-icons": {
|
"react-icons": {
|
||||||
|
|
@ -11691,9 +11905,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tailwind-merge": {
|
"tailwind-merge": {
|
||||||
"version": "2.4.0",
|
"version": "2.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz",
|
||||||
"integrity": "sha512-49AwoOQNKdqKPd9CViyH5wJoSKsCDjUlzL8DxuGp3P1FsGY36NJDAa18jLZcaHAUUuTj+JB8IAo8zWgBNvBF7A=="
|
"integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="
|
||||||
},
|
},
|
||||||
"tailwindcss": {
|
"tailwindcss": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
|
|
@ -12141,6 +12355,11 @@
|
||||||
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"zod": {
|
||||||
|
"version": "3.24.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
|
||||||
|
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="
|
||||||
|
},
|
||||||
"zustand": {
|
"zustand": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz",
|
||||||
|
|
|
||||||
11
package.json
11
package.json
|
|
@ -11,12 +11,14 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
"@emotion/styled": "^11.10.6",
|
"@emotion/styled": "^11.10.6",
|
||||||
|
"@hookform/resolvers": "^3.10.0",
|
||||||
"@mui/material": "^5.12.1",
|
"@mui/material": "^5.12.1",
|
||||||
"@mui/x-date-pickers": "^6.5.0",
|
"@mui/x-date-pickers": "^6.5.0",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.2",
|
"@radix-ui/react-alert-dialog": "^1.1.2",
|
||||||
"@radix-ui/react-dialog": "^1.1.2",
|
"@radix-ui/react-dialog": "^1.1.2",
|
||||||
|
"@radix-ui/react-label": "^2.1.2",
|
||||||
"@radix-ui/react-popover": "^1.1.1",
|
"@radix-ui/react-popover": "^1.1.1",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@tanstack/react-query": "^4.32.0",
|
"@tanstack/react-query": "^4.32.0",
|
||||||
"@tanstack/react-query-devtools": "^4.32.0",
|
"@tanstack/react-query-devtools": "^4.32.0",
|
||||||
"@types/node": "18.15.13",
|
"@types/node": "18.15.13",
|
||||||
|
|
@ -25,7 +27,7 @@
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
"autoprefixer": "10.4.14",
|
"autoprefixer": "10.4.14",
|
||||||
"axios": "^1.5.1",
|
"axios": "^1.5.1",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
|
|
@ -39,19 +41,20 @@
|
||||||
"react-day-picker": "^8.10.1",
|
"react-day-picker": "^8.10.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-fast-marquee": "^1.3.5",
|
"react-fast-marquee": "^1.3.5",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-icons": "^4.8.0",
|
"react-icons": "^4.8.0",
|
||||||
"react-player": "^2.12.0",
|
"react-player": "^2.12.0",
|
||||||
"react-slot-counter": "^3.0.4",
|
"react-slot-counter": "^3.0.4",
|
||||||
"react-use": "^17.5.1",
|
"react-use": "^17.5.1",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
"swiper": "^9.2.4",
|
"swiper": "^9.2.4",
|
||||||
"tailwind-merge": "^2.4.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss": "3.3.1",
|
"tailwindcss": "3.3.1",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"usehooks-ts": "^2.9.1",
|
"usehooks-ts": "^2.9.1",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
|
"zod": "^3.24.1",
|
||||||
"zustand": "^5.0.1"
|
"zustand": "^5.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import { useState, useMemo, useEffect, ReactNode } from 'react';
|
||||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||||
import { IBurger } from '@/typings/burger.type';
|
import { IBurger } from '@/typings/burger.type';
|
||||||
import { IAdvertisment, IAdvertismentContext } from '@/typings/advertisment.type';
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
|
|
||||||
37
routes.ts
37
routes.ts
|
|
@ -1,7 +1,8 @@
|
||||||
export default {
|
export default {
|
||||||
newsItem: (id: string) => `/posts/${id}`,
|
newsItem: (id: string) => `/posts/${id}`,
|
||||||
pageItem: (id: string) => `/pages/${id}`,
|
pageItem: (id: string) => `/pages/${id}`,
|
||||||
channelItem: (channel: number) => `/timetable?on_channel=1&channel=${channel}`,
|
channelItem: (channel: number) =>
|
||||||
|
`/timetable?on_channel=1&channel=${channel}`,
|
||||||
smallSwiper: (type: string) => `/slider?type=${type}`,
|
smallSwiper: (type: string) => `/slider?type=${type}`,
|
||||||
videos: (search: string) => `/materials${search}`,
|
videos: (search: string) => `/materials${search}`,
|
||||||
video: (video_id: number) => `/material/${video_id}`,
|
video: (video_id: number) => `/material/${video_id}`,
|
||||||
|
|
@ -15,35 +16,35 @@ export default {
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
// Votes ================================================================
|
// Votes ================================================================
|
||||||
allVotes: '/voting/show_on_site',
|
allVotes: "/voting/show_on_site",
|
||||||
vote: (vote_id: string) => `/voting/${vote_id}`,
|
vote: (vote_id: string) => `/voting/${vote_id}`,
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
|
|
||||||
// Lottery ================================================================
|
// Lottery ================================================================
|
||||||
lotteryActive: '/lottery/active',
|
lotteryActive: "/lottery/active",
|
||||||
lotteryId: (lottery_id: string) => `/lottery/${lottery_id}`,
|
tossId: (type: string, id: string) => `/${type}/${id}`,
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
|
|
||||||
addPost: '/mahabat/order',
|
addPost: "/mahabat/order",
|
||||||
news: '/pagination/new/posts',
|
news: "/pagination/new/posts",
|
||||||
lastVideos: '/materials?per_page=30',
|
lastVideos: "/materials?per_page=30",
|
||||||
categories: '/categories',
|
categories: "/categories",
|
||||||
channels: '/channels',
|
channels: "/channels",
|
||||||
banner: '/mahabatlar',
|
banner: "/mahabatlar",
|
||||||
|
|
||||||
// home: '/mahabatlar?type=home',
|
// home: '/mahabatlar?type=home',
|
||||||
home: '/slider?type=big',
|
home: "/slider?type=big",
|
||||||
marquee: '/timetable',
|
marquee: "/timetable",
|
||||||
homeSmallSlider_1: '/slider?type=small',
|
homeSmallSlider_1: "/slider?type=small",
|
||||||
homeSmallSlider_2: '/slider?type=small2',
|
homeSmallSlider_2: "/slider?type=small2",
|
||||||
homeSmallSlider_3: '/slider?type=hazyna',
|
homeSmallSlider_3: "/slider?type=hazyna",
|
||||||
homeSmallSlider_4: '/slider?type=mahabat',
|
homeSmallSlider_4: "/slider?type=mahabat",
|
||||||
|
|
||||||
properties: '/mahabat/property-types',
|
properties: "/mahabat/property-types",
|
||||||
|
|
||||||
addViews: (video_id: string) => `material/${video_id}/views/increment`,
|
addViews: (video_id: string) => `material/${video_id}/views/increment`,
|
||||||
|
|
||||||
// Sms ========================================================================
|
// Sms ========================================================================
|
||||||
myTvAdmins: '/my-tv-admins',
|
myTvAdmins: "/my-tv-admins",
|
||||||
messagesByTvAdmin: (id: number) => `/messages-by-tv-admin/${id}`,
|
messagesByTvAdmin: (id: number) => `/messages-by-tv-admin/${id}`,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
interface ILotteryStatus {
|
||||||
|
status: "Upcoming" | "Finished" | "Ongoing";
|
||||||
|
setStatus: (value: "Upcoming" | "Finished" | "Ongoing") => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useLotteryStatus = create<ILotteryStatus>((set) => ({
|
||||||
|
status: "Upcoming",
|
||||||
|
setStatus: (value: "Upcoming" | "Finished" | "Ongoing") =>
|
||||||
|
set({ status: value }),
|
||||||
|
}));
|
||||||
|
|
@ -67,7 +67,6 @@ const config = {
|
||||||
from: { height: 'var(--radix-accordion-content-height)' },
|
from: { height: 'var(--radix-accordion-content-height)' },
|
||||||
to: { height: '0' },
|
to: { height: '0' },
|
||||||
},
|
},
|
||||||
|
|
||||||
'dots-flash': {
|
'dots-flash': {
|
||||||
from: { opacity: '0' },
|
from: { opacity: '0' },
|
||||||
to: { opacity: '1' },
|
to: { opacity: '1' },
|
||||||
|
|
@ -76,7 +75,6 @@ const config = {
|
||||||
animation: {
|
animation: {
|
||||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||||
'accordion-up': 'accordion-up 0.2s ease-out',
|
'accordion-up': 'accordion-up 0.2s ease-out',
|
||||||
'dots-flash': 'dots-flash 1s infinite;',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue