lottery page

This commit is contained in:
Kakabay 2025-01-03 17:45:58 +05:00
parent fb6090d9fd
commit e46670628f
6 changed files with 125 additions and 68 deletions

View File

@ -93,8 +93,7 @@ html {
}
.text-stroke {
text-shadow: -1px -1px 0 white, 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;
}
.big-swiper .swiper-pagination-bullet {
@ -122,20 +121,20 @@ html {
.small-swiper .swiper-button-next:after {
color: white;
content: url("/arrow-right-small.svg");
content: url('/arrow-right-small.svg');
}
.small-swiper .swiper-button-prev:after {
color: white;
content: url("/arrow-left-small.svg");
content: url('/arrow-left-small.svg');
}
.big-swiper .swiper-button-next:after {
color: white;
content: url("/arrow-right-big.svg");
content: url('/arrow-right-big.svg');
}
.big-swiper .swiper-button-prev:after {
color: white;
content: url("/arrow-left-big.svg");
content: url('/arrow-left-big.svg');
}
video {
@ -188,15 +187,15 @@ big {
@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;
}
.calendar [aria-label="Go to previous month"] {
.calendar [aria-label='Go to previous month'] {
@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;
}
@ -262,7 +261,7 @@ big {
}
.wheel-circle {
background: url("/wheel-circle.svg");
background: url('/wheel-circle.svg');
background-position: center;
background-size: cover;
background-repeat: no-repeat;
@ -296,7 +295,7 @@ big {
} */
.slot-seperator {
content: url("/dash.svg");
content: url('/dash.svg');
background-position: center;
background-size: cover;
background-repeat: no-repeat;
@ -315,7 +314,7 @@ big {
@media (max-width: 1024px) {
.slot-seperator {
content: url("/dash-md.svg");
content: url('/dash-md.svg');
@apply mx-2;
}
}
@ -340,7 +339,7 @@ big {
@media (max-width: 426px) {
.slot-seperator {
content: url("/dash-sm.svg");
content: url('/dash-sm.svg');
@apply mx-2;
}
}
@ -353,14 +352,27 @@ big {
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 {
0% {
opacity: 1;
transform: translateX(0) translateY(0) rotate(0deg);
}
50% {
transform: translateX(calc(var(--end-x) / 2)) translateY(-30vh)
rotate(180deg);
transform: translateX(calc(var(--end-x) / 2)) translateY(-30vh) rotate(180deg);
}
100% {
opacity: 0;

View File

@ -1,3 +1,4 @@
import { cn } from '@/lib/utils';
import { motion } from 'framer-motion';
interface AnimatedTextProps {
@ -37,7 +38,9 @@ const AnimatedText = ({
delay: i * wordDelay,
ease: 'easeOut',
}}
className={`inline-block mx-2 ${wordClassName}`}>
className={cn(`inline-block mx-2`, wordClassName, {
'animate-dotsFlash': text === '...',
})}>
{word}
</motion.span>
))}

View File

@ -25,6 +25,9 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
const { width, height } = useWindowSize();
const { lotteryData } = useLotteryAuth();
const [isSlotCounterAnimating, setIsSlotCounterAnimating] = useState(false);
const [winnerSelectingStatus, setWinnerSelectingStatus] = useState<
'not-selected' | 'is-selecting' | 'selected'
>('not-selected');
const [pendingWinner, setPendingWinner] = useState<LotteryWinnerDataSimplified | null>(null);
// Refs
@ -34,6 +37,7 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
// Add new state for display text
const [displayText, setDisplayText] = useState<string>('...');
const [winnerText, setWinnerText] = useState<string>('');
// Initialize winners from lottery data
useEffect(() => {
@ -85,7 +89,7 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
setDisplayText(`${winnerData.winner_no}-nji ýeňiji saýlanýar`);
// Start the sequence
setIsSlotCounterAnimating(true);
setWinnerSelectingStatus('is-selecting');
setPendingWinner(winnerData);
setCurrentNumber(winnerData.ticket);
@ -93,7 +97,9 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
await new Promise((resolve) => setTimeout(resolve, SLOT_COUNTER_DURATION));
// Update text to show winner's phone
setDisplayText(winnerData.client);
setDisplayText('The winner is');
setWinnerText(winnerData.client);
setWinnerSelectingStatus('selected');
setIsConfettiActive(true);
setWinners((prev) => [...prev, winnerData]);
@ -102,9 +108,11 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
setTimeout(() => {
if (mountedRef.current) {
setIsConfettiActive(false);
setIsSlotCounterAnimating(false);
// setIsSlotCounterAnimating(false);
setWinnerSelectingStatus('not-selected');
setPendingWinner(null);
setDisplayText('...'); // Reset text
setWinnerText('');
}
}, 5000);
} catch (error) {
@ -157,7 +165,6 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
Connection error. Please refresh the page.
</div>
)}
{isConfettiActive && (
<div className="fixed top-0 left-0 z-50">
<ReactConfetti
@ -181,16 +188,32 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
<div className="container">
<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%)' }}>
{winnerSelectingStatus === 'not-selected' || winnerSelectingStatus === 'is-selecting' ? (
<AnimatedText
text={displayText}
className="text-center text-[100px] leading-[108px] text-[#E65E19]"
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} />
</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} />
</div>
</div>

View File

@ -77,13 +77,14 @@ const LotterySlotCounter = ({ numberString, isAnimating }: LotterySlotCounterPro
<SlotCounter
value={formattedNumber}
startValue={formattedNumber}
startValueOnce
charClassName="rolling-number"
separatorClassName="slot-seperator"
duration={2}
speed={2}
startFromLastDigit
delay={2}
animateUnchanged={false}
startFromLastDigit
animateOnVisible={false}
autoAnimationStart={false}
/>
</div>

View File

@ -204,6 +204,7 @@ export const theme = {
animation: {
buble: 'buble 7s infinite',
dotsFlash: 'dotsFlash 1.5s infinite',
},
keyframes: {
@ -221,6 +222,17 @@ export const theme = {
transform: 'scale(1) translate(0%, 0%)',
},
},
dotsFlash: {
'0%': {
opacity: '1',
},
'50%': {
opacity: '0',
},
'100%': {
opacity: '1',
},
},
},
},
};

View File

@ -1,80 +1,86 @@
import type { Config } from "tailwindcss"
import type { Config } from 'tailwindcss';
const config = {
darkMode: ["class"],
darkMode: ['class'],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
prefix: "",
prefix: '',
theme: {
container: {
center: true,
padding: "2rem",
padding: '2rem',
screens: {
"2xl": "1400px",
'2xl': '1400px',
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--radix-accordion-content-height)' },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: '0' },
},
'dots-flash': {
from: { opacity: '0' },
to: { opacity: '1' },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
'dots-flash': 'dots-flash 1s infinite;',
},
},
},
plugins: [require("tailwindcss-animate")],
} satisfies Config
plugins: [require('tailwindcss-animate')],
} satisfies Config;
export default config
export default config;