diff --git a/app/globals.css b/app/globals.css index 00190b0..ecd4cd3 100644 --- a/app/globals.css +++ b/app/globals.css @@ -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; diff --git a/components/common/AnimatedText.tsx b/components/common/AnimatedText.tsx index 3967d4a..c9c0a9c 100644 --- a/components/common/AnimatedText.tsx +++ b/components/common/AnimatedText.tsx @@ -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} ))} diff --git a/components/lottery/LotteryWinnersSection.tsx b/components/lottery/LotteryWinnersSection.tsx index 17d6e35..15042ee 100644 --- a/components/lottery/LotteryWinnersSection.tsx +++ b/components/lottery/LotteryWinnersSection.tsx @@ -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(null); // Refs @@ -34,6 +37,7 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) => // Add new state for display text const [displayText, setDisplayText] = useState('...'); + const [winnerText, setWinnerText] = useState(''); // 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. )} - {isConfettiActive && (
- -
+ {winnerSelectingStatus === 'not-selected' || winnerSelectingStatus === 'is-selecting' ? ( + + ) : ( +
+ + {winnerText && ( + + )} +
+ )} + +
-
+
diff --git a/components/lottery/slotCounter/LotterySlotCounter.tsx b/components/lottery/slotCounter/LotterySlotCounter.tsx index e598790..2c2354d 100644 --- a/components/lottery/slotCounter/LotterySlotCounter.tsx +++ b/components/lottery/slotCounter/LotterySlotCounter.tsx @@ -77,13 +77,14 @@ const LotterySlotCounter = ({ numberString, isAnimating }: LotterySlotCounterPro
diff --git a/tailwind.config.js b/tailwind.config.js index d0ef3ba..859b7da 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -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', + }, + }, }, }, }; diff --git a/tailwind.config.ts b/tailwind.config.ts index 84287e8..78cca54 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -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 \ No newline at end of file +export default config;