lottery page
This commit is contained in:
parent
fb6090d9fd
commit
e46670628f
|
|
@ -93,8 +93,7 @@ html {
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-stroke {
|
.text-stroke {
|
||||||
text-shadow: -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white,
|
text-shadow: -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white, 1px 1px 0 white;
|
||||||
1px 1px 0 white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.big-swiper .swiper-pagination-bullet {
|
.big-swiper .swiper-pagination-bullet {
|
||||||
|
|
@ -122,20 +121,20 @@ html {
|
||||||
|
|
||||||
.small-swiper .swiper-button-next:after {
|
.small-swiper .swiper-button-next:after {
|
||||||
color: white;
|
color: white;
|
||||||
content: url("/arrow-right-small.svg");
|
content: url('/arrow-right-small.svg');
|
||||||
}
|
}
|
||||||
.small-swiper .swiper-button-prev:after {
|
.small-swiper .swiper-button-prev:after {
|
||||||
color: white;
|
color: white;
|
||||||
content: url("/arrow-left-small.svg");
|
content: url('/arrow-left-small.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.big-swiper .swiper-button-next:after {
|
.big-swiper .swiper-button-next:after {
|
||||||
color: white;
|
color: white;
|
||||||
content: url("/arrow-right-big.svg");
|
content: url('/arrow-right-big.svg');
|
||||||
}
|
}
|
||||||
.big-swiper .swiper-button-prev:after {
|
.big-swiper .swiper-button-prev:after {
|
||||||
color: white;
|
color: white;
|
||||||
content: url("/arrow-left-big.svg");
|
content: url('/arrow-left-big.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
video {
|
video {
|
||||||
|
|
@ -188,15 +187,15 @@ big {
|
||||||
@apply bg-[#E6E6FA] rounded-xl py-3 px-4 placeholder:text-[#BCBCD6] outline-none;
|
@apply bg-[#E6E6FA] rounded-xl py-3 px-4 placeholder:text-[#BCBCD6] outline-none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar [aria-label="Go to next month"] {
|
.calendar [aria-label='Go to next month'] {
|
||||||
@apply shadow-sm transition-all;
|
@apply shadow-sm transition-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar [aria-label="Go to previous month"] {
|
.calendar [aria-label='Go to previous month'] {
|
||||||
@apply shadow-sm transition-all;
|
@apply shadow-sm transition-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.day-styles [name="day"] {
|
.day-styles [name='day'] {
|
||||||
@apply p-4 text-textDarkt leading-[140%] bg-purple-600 rounded-full;
|
@apply p-4 text-textDarkt leading-[140%] bg-purple-600 rounded-full;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,7 +261,7 @@ big {
|
||||||
}
|
}
|
||||||
|
|
||||||
.wheel-circle {
|
.wheel-circle {
|
||||||
background: url("/wheel-circle.svg");
|
background: url('/wheel-circle.svg');
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
|
@ -296,7 +295,7 @@ big {
|
||||||
} */
|
} */
|
||||||
|
|
||||||
.slot-seperator {
|
.slot-seperator {
|
||||||
content: url("/dash.svg");
|
content: url('/dash.svg');
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
|
@ -315,7 +314,7 @@ big {
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
.slot-seperator {
|
.slot-seperator {
|
||||||
content: url("/dash-md.svg");
|
content: url('/dash-md.svg');
|
||||||
@apply mx-2;
|
@apply mx-2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -340,7 +339,7 @@ big {
|
||||||
|
|
||||||
@media (max-width: 426px) {
|
@media (max-width: 426px) {
|
||||||
.slot-seperator {
|
.slot-seperator {
|
||||||
content: url("/dash-sm.svg");
|
content: url('/dash-sm.svg');
|
||||||
@apply mx-2;
|
@apply mx-2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -353,14 +352,27 @@ 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 {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes confetti-move {
|
@keyframes confetti-move {
|
||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateX(0) translateY(0) rotate(0deg);
|
transform: translateX(0) translateY(0) rotate(0deg);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
transform: translateX(calc(var(--end-x) / 2)) translateY(-30vh)
|
transform: translateX(calc(var(--end-x) / 2)) translateY(-30vh) rotate(180deg);
|
||||||
rotate(180deg);
|
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
interface AnimatedTextProps {
|
interface AnimatedTextProps {
|
||||||
|
|
@ -37,7 +38,9 @@ const AnimatedText = ({
|
||||||
delay: i * wordDelay,
|
delay: i * wordDelay,
|
||||||
ease: 'easeOut',
|
ease: 'easeOut',
|
||||||
}}
|
}}
|
||||||
className={`inline-block mx-2 ${wordClassName}`}>
|
className={cn(`inline-block mx-2`, wordClassName, {
|
||||||
|
'animate-dotsFlash': text === '...',
|
||||||
|
})}>
|
||||||
{word}
|
{word}
|
||||||
</motion.span>
|
</motion.span>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
|
||||||
const { width, height } = useWindowSize();
|
const { width, height } = useWindowSize();
|
||||||
const { lotteryData } = useLotteryAuth();
|
const { lotteryData } = useLotteryAuth();
|
||||||
const [isSlotCounterAnimating, setIsSlotCounterAnimating] = useState(false);
|
const [isSlotCounterAnimating, setIsSlotCounterAnimating] = useState(false);
|
||||||
|
const [winnerSelectingStatus, setWinnerSelectingStatus] = useState<
|
||||||
|
'not-selected' | 'is-selecting' | 'selected'
|
||||||
|
>('not-selected');
|
||||||
const [pendingWinner, setPendingWinner] = useState<LotteryWinnerDataSimplified | null>(null);
|
const [pendingWinner, setPendingWinner] = useState<LotteryWinnerDataSimplified | null>(null);
|
||||||
|
|
||||||
// Refs
|
// Refs
|
||||||
|
|
@ -34,6 +37,7 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
|
||||||
|
|
||||||
// Add new state for display text
|
// Add new state for display text
|
||||||
const [displayText, setDisplayText] = useState<string>('...');
|
const [displayText, setDisplayText] = useState<string>('...');
|
||||||
|
const [winnerText, setWinnerText] = useState<string>('');
|
||||||
|
|
||||||
// Initialize winners from lottery data
|
// Initialize winners from lottery data
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -85,7 +89,7 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
|
||||||
setDisplayText(`${winnerData.winner_no}-nji ýeňiji saýlanýar`);
|
setDisplayText(`${winnerData.winner_no}-nji ýeňiji saýlanýar`);
|
||||||
|
|
||||||
// Start the sequence
|
// Start the sequence
|
||||||
setIsSlotCounterAnimating(true);
|
setWinnerSelectingStatus('is-selecting');
|
||||||
setPendingWinner(winnerData);
|
setPendingWinner(winnerData);
|
||||||
setCurrentNumber(winnerData.ticket);
|
setCurrentNumber(winnerData.ticket);
|
||||||
|
|
||||||
|
|
@ -93,7 +97,9 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
|
||||||
await new Promise((resolve) => setTimeout(resolve, SLOT_COUNTER_DURATION));
|
await new Promise((resolve) => setTimeout(resolve, SLOT_COUNTER_DURATION));
|
||||||
|
|
||||||
// Update text to show winner's phone
|
// Update text to show winner's phone
|
||||||
setDisplayText(winnerData.client);
|
setDisplayText('The winner is');
|
||||||
|
setWinnerText(winnerData.client);
|
||||||
|
setWinnerSelectingStatus('selected');
|
||||||
|
|
||||||
setIsConfettiActive(true);
|
setIsConfettiActive(true);
|
||||||
setWinners((prev) => [...prev, winnerData]);
|
setWinners((prev) => [...prev, winnerData]);
|
||||||
|
|
@ -102,9 +108,11 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (mountedRef.current) {
|
if (mountedRef.current) {
|
||||||
setIsConfettiActive(false);
|
setIsConfettiActive(false);
|
||||||
setIsSlotCounterAnimating(false);
|
// setIsSlotCounterAnimating(false);
|
||||||
|
setWinnerSelectingStatus('not-selected');
|
||||||
setPendingWinner(null);
|
setPendingWinner(null);
|
||||||
setDisplayText('...'); // Reset text
|
setDisplayText('...'); // Reset text
|
||||||
|
setWinnerText('');
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -157,7 +165,6 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
|
||||||
Connection error. Please refresh the page.
|
Connection error. Please refresh the page.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isConfettiActive && (
|
{isConfettiActive && (
|
||||||
<div className="fixed top-0 left-0 z-50">
|
<div className="fixed top-0 left-0 z-50">
|
||||||
<ReactConfetti
|
<ReactConfetti
|
||||||
|
|
@ -181,16 +188,32 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
|
||||||
|
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div
|
<div
|
||||||
className="flex flex-col items-center rounded-[32px] pt-[40px]"
|
className="flex flex-col items-center rounded-[32px] gap-[40px] pt-[40px]"
|
||||||
style={{ background: 'linear-gradient(180deg, #F0ECF4 0%, #E1E0FF 43.5%)' }}>
|
style={{ background: 'linear-gradient(180deg, #F0ECF4 0%, #E1E0FF 43.5%)' }}>
|
||||||
<AnimatedText
|
{winnerSelectingStatus === 'not-selected' || winnerSelectingStatus === 'is-selecting' ? (
|
||||||
text={displayText}
|
<AnimatedText
|
||||||
className="text-center text-[100px] leading-[108px] text-[#E65E19]"
|
text={displayText}
|
||||||
/>
|
className="text-center flex items-center justify-center text-[100px] leading-[108px] text-[#E65E19]"
|
||||||
<div className="translate-y-1/2 z-10">
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col items-center justify-center">
|
||||||
|
<AnimatedText
|
||||||
|
text={displayText}
|
||||||
|
className="text-center text-[56px] leading-[64px] text-[#E65E19]"
|
||||||
|
/>
|
||||||
|
{winnerText && (
|
||||||
|
<AnimatedText
|
||||||
|
text={winnerText}
|
||||||
|
className="text-center text-[80px] leading-[88px] text-[#E65E19]"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="z-10">
|
||||||
<LotterySlotCounter numberString={currentNumber} isAnimating={isSlotCounterAnimating} />
|
<LotterySlotCounter numberString={currentNumber} isAnimating={isSlotCounterAnimating} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-6 rounded-[12px] flex-1 w-full items-center justify-center md:pt-[122px] sm:pt-[90px] pt-[40px] 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">
|
||||||
<LotteryWinnersList winners={winners} />
|
<LotteryWinnersList winners={winners} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -77,13 +77,14 @@ const LotterySlotCounter = ({ numberString, isAnimating }: LotterySlotCounterPro
|
||||||
<SlotCounter
|
<SlotCounter
|
||||||
value={formattedNumber}
|
value={formattedNumber}
|
||||||
startValue={formattedNumber}
|
startValue={formattedNumber}
|
||||||
|
startValueOnce
|
||||||
charClassName="rolling-number"
|
charClassName="rolling-number"
|
||||||
separatorClassName="slot-seperator"
|
separatorClassName="slot-seperator"
|
||||||
duration={2}
|
duration={2}
|
||||||
speed={2}
|
speed={2}
|
||||||
startFromLastDigit
|
|
||||||
delay={2}
|
delay={2}
|
||||||
animateUnchanged={false}
|
startFromLastDigit
|
||||||
|
animateOnVisible={false}
|
||||||
autoAnimationStart={false}
|
autoAnimationStart={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,7 @@ export const theme = {
|
||||||
|
|
||||||
animation: {
|
animation: {
|
||||||
buble: 'buble 7s infinite',
|
buble: 'buble 7s infinite',
|
||||||
|
dotsFlash: 'dotsFlash 1.5s infinite',
|
||||||
},
|
},
|
||||||
|
|
||||||
keyframes: {
|
keyframes: {
|
||||||
|
|
@ -221,6 +222,17 @@ export const theme = {
|
||||||
transform: 'scale(1) translate(0%, 0%)',
|
transform: 'scale(1) translate(0%, 0%)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dotsFlash: {
|
||||||
|
'0%': {
|
||||||
|
opacity: '1',
|
||||||
|
},
|
||||||
|
'50%': {
|
||||||
|
opacity: '0',
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
opacity: '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,86 @@
|
||||||
import type { Config } from "tailwindcss"
|
import type { Config } from 'tailwindcss';
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
darkMode: ["class"],
|
darkMode: ['class'],
|
||||||
content: [
|
content: [
|
||||||
'./pages/**/*.{ts,tsx}',
|
'./pages/**/*.{ts,tsx}',
|
||||||
'./components/**/*.{ts,tsx}',
|
'./components/**/*.{ts,tsx}',
|
||||||
'./app/**/*.{ts,tsx}',
|
'./app/**/*.{ts,tsx}',
|
||||||
'./src/**/*.{ts,tsx}',
|
'./src/**/*.{ts,tsx}',
|
||||||
],
|
],
|
||||||
prefix: "",
|
prefix: '',
|
||||||
theme: {
|
theme: {
|
||||||
container: {
|
container: {
|
||||||
center: true,
|
center: true,
|
||||||
padding: "2rem",
|
padding: '2rem',
|
||||||
screens: {
|
screens: {
|
||||||
"2xl": "1400px",
|
'2xl': '1400px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
border: "hsl(var(--border))",
|
border: 'hsl(var(--border))',
|
||||||
input: "hsl(var(--input))",
|
input: 'hsl(var(--input))',
|
||||||
ring: "hsl(var(--ring))",
|
ring: 'hsl(var(--ring))',
|
||||||
background: "hsl(var(--background))",
|
background: 'hsl(var(--background))',
|
||||||
foreground: "hsl(var(--foreground))",
|
foreground: 'hsl(var(--foreground))',
|
||||||
primary: {
|
primary: {
|
||||||
DEFAULT: "hsl(var(--primary))",
|
DEFAULT: 'hsl(var(--primary))',
|
||||||
foreground: "hsl(var(--primary-foreground))",
|
foreground: 'hsl(var(--primary-foreground))',
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
DEFAULT: "hsl(var(--secondary))",
|
DEFAULT: 'hsl(var(--secondary))',
|
||||||
foreground: "hsl(var(--secondary-foreground))",
|
foreground: 'hsl(var(--secondary-foreground))',
|
||||||
},
|
},
|
||||||
destructive: {
|
destructive: {
|
||||||
DEFAULT: "hsl(var(--destructive))",
|
DEFAULT: 'hsl(var(--destructive))',
|
||||||
foreground: "hsl(var(--destructive-foreground))",
|
foreground: 'hsl(var(--destructive-foreground))',
|
||||||
},
|
},
|
||||||
muted: {
|
muted: {
|
||||||
DEFAULT: "hsl(var(--muted))",
|
DEFAULT: 'hsl(var(--muted))',
|
||||||
foreground: "hsl(var(--muted-foreground))",
|
foreground: 'hsl(var(--muted-foreground))',
|
||||||
},
|
},
|
||||||
accent: {
|
accent: {
|
||||||
DEFAULT: "hsl(var(--accent))",
|
DEFAULT: 'hsl(var(--accent))',
|
||||||
foreground: "hsl(var(--accent-foreground))",
|
foreground: 'hsl(var(--accent-foreground))',
|
||||||
},
|
},
|
||||||
popover: {
|
popover: {
|
||||||
DEFAULT: "hsl(var(--popover))",
|
DEFAULT: 'hsl(var(--popover))',
|
||||||
foreground: "hsl(var(--popover-foreground))",
|
foreground: 'hsl(var(--popover-foreground))',
|
||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
DEFAULT: "hsl(var(--card))",
|
DEFAULT: 'hsl(var(--card))',
|
||||||
foreground: "hsl(var(--card-foreground))",
|
foreground: 'hsl(var(--card-foreground))',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
lg: "var(--radius)",
|
lg: 'var(--radius)',
|
||||||
md: "calc(var(--radius) - 2px)",
|
md: 'calc(var(--radius) - 2px)',
|
||||||
sm: "calc(var(--radius) - 4px)",
|
sm: 'calc(var(--radius) - 4px)',
|
||||||
},
|
},
|
||||||
keyframes: {
|
keyframes: {
|
||||||
"accordion-down": {
|
'accordion-down': {
|
||||||
from: { height: "0" },
|
from: { height: '0' },
|
||||||
to: { height: "var(--radix-accordion-content-height)" },
|
to: { height: 'var(--radix-accordion-content-height)' },
|
||||||
},
|
},
|
||||||
"accordion-up": {
|
'accordion-up': {
|
||||||
from: { height: "var(--radix-accordion-content-height)" },
|
from: { height: 'var(--radix-accordion-content-height)' },
|
||||||
to: { height: "0" },
|
to: { height: '0' },
|
||||||
|
},
|
||||||
|
|
||||||
|
'dots-flash': {
|
||||||
|
from: { opacity: '0' },
|
||||||
|
to: { opacity: '1' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
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;',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require("tailwindcss-animate")],
|
plugins: [require('tailwindcss-animate')],
|
||||||
} satisfies Config
|
} satisfies Config;
|
||||||
|
|
||||||
export default config
|
export default config;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue