From bc1900d3f71167b6c66e7d002234d45ccf7f2ebb Mon Sep 17 00:00:00 2001 From: Aditya Siregar Date: Sat, 16 Aug 2025 01:51:16 +0700 Subject: [PATCH] update UI result --- app/globals.css | 1127 ++++++++++++++++++++++++++++++++++++++++++ app/results/page.tsx | 735 ++++++++++++++++++--------- 2 files changed, 1642 insertions(+), 220 deletions(-) diff --git a/app/globals.css b/app/globals.css index 9c65a0f..b9cd56e 100644 --- a/app/globals.css +++ b/app/globals.css @@ -173,6 +173,1133 @@ transition: transform 0.2s ease-out; } +/* Normal card-header styles to preserve positioning */ +[data-fullscreen-target="event-overview"]:not(:fullscreen) .card-header { + padding: 1.5rem; + display: block; + position: relative; +} + +/* Fullscreen styles for event overview */ +[data-fullscreen-target="event-overview"]:fullscreen { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + padding: 4rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 100vh; + position: relative; + color: white; +} + +[data-fullscreen-target="event-overview"]:fullscreen .card-header { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + margin-bottom: 3rem; + padding: 3rem !important; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border-radius: 2rem; + border: 1px solid rgba(255, 255, 255, 0.2); + position: relative; +} + +[data-fullscreen-target="event-overview"]:fullscreen .card-header h3 { + font-size: 6rem !important; + font-weight: 900 !important; + margin-bottom: 2rem !important; + color: white !important; + text-shadow: 0 8px 16px rgba(0, 0, 0, 0.5); + letter-spacing: -0.03em; + text-align: center !important; + width: 100% !important; + display: block !important; +} + + +[data-fullscreen-target="event-overview"]:fullscreen .card-header button { + position: absolute !important; + top: 1rem !important; + right: 1rem !important; + font-size: 1rem !important; + padding: 0.75rem 1.5rem !important; + background: rgba(255, 255, 255, 0.2) !important; + border: 1px solid rgba(255, 255, 255, 0.3) !important; + color: white !important; + backdrop-filter: blur(10px); + z-index: 10; +} + +[data-fullscreen-target="event-overview"]:fullscreen .card-content { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + max-width: 1200px; + width: 100%; +} + +[data-fullscreen-target="event-overview"]:fullscreen .grid { + grid-template-columns: repeat(3, 1fr) !important; + gap: 3rem !important; + margin-bottom: 4rem !important; + width: 100%; + max-width: 1400px; +} + +[data-fullscreen-target="event-overview"]:fullscreen .text-2xl { + font-size: 4.5rem !important; + font-weight: 900 !important; + margin-bottom: 1rem !important; + text-shadow: 0 6px 12px rgba(0, 0, 0, 0.4) !important; + color: white !important; + letter-spacing: -0.02em; +} + +[data-fullscreen-target="event-overview"]:fullscreen .text-sm { + font-size: 1.5rem !important; + font-weight: 600 !important; + line-height: 1.4 !important; + color: rgba(255, 255, 255, 0.95) !important; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); +} + +[data-fullscreen-target="event-overview"]:fullscreen .h-8 { + height: 5rem !important; + width: 5rem !important; + margin-bottom: 2rem !important; + filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3)); + color: white !important; +} + +[data-fullscreen-target="event-overview"]:fullscreen .card-header .h-5 { + height: 3rem !important; + width: 3rem !important; + color: white !important; + filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.4)); + margin-right: 1rem !important; +} + +[data-fullscreen-target="event-overview"]:fullscreen .p-4 { + padding: 2.5rem !important; +} + +[data-fullscreen-target="event-overview"]:fullscreen .mt-6 { + margin-top: 4rem !important; +} + +[data-fullscreen-target="event-overview"]:fullscreen .text-3xl { + font-size: 4rem; +} + +[data-fullscreen-target="event-overview"]:fullscreen .text-xs { + font-size: 1.125rem !important; + font-weight: 500 !important; +} + +/* Final fullscreen enhancements */ +[data-fullscreen-target="event-overview"]:fullscreen .card-content > div:first-child { + margin-bottom: 4rem !important; +} + +[data-fullscreen-target="event-overview"]:fullscreen .card-content > div:last-child { + margin-top: 3rem !important; +} + +/* Smooth entrance animations */ +[data-fullscreen-target="event-overview"]:fullscreen .card-header { + animation: slideDown 0.8s ease-out; +} + +[data-fullscreen-target="event-overview"]:fullscreen .grid { + animation: fadeInUp 1s ease-out 0.2s both; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-gradient-to-r { + animation: fadeInUp 1s ease-out 0.4s both; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Additional fullscreen enhancements */ +[data-fullscreen-target="event-overview"]:fullscreen { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-blue-50, +[data-fullscreen-target="event-overview"]:fullscreen .bg-green-50, +[data-fullscreen-target="event-overview"]:fullscreen .bg-orange-50 { + padding: 3.5rem !important; + border-radius: 2rem !important; + background: rgba(255, 255, 255, 0.15) !important; + backdrop-filter: blur(20px) !important; + border: 1px solid rgba(255, 255, 255, 0.3) !important; + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25) !important; + transition: all 0.3s ease !important; + position: relative; + overflow: hidden; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-blue-50::before, +[data-fullscreen-target="event-overview"]:fullscreen .bg-green-50::before, +[data-fullscreen-target="event-overview"]:fullscreen .bg-orange-50::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05)); + pointer-events: none; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-blue-50 { + border-color: rgba(59, 130, 246, 0.5) !important; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-green-50 { + border-color: rgba(16, 185, 129, 0.5) !important; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-orange-50 { + border-color: rgba(245, 158, 11, 0.5) !important; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-blue-50:hover, +[data-fullscreen-target="event-overview"]:fullscreen .bg-green-50:hover, +[data-fullscreen-target="event-overview"]:fullscreen .bg-orange-50:hover { + transform: translateY(-10px) scale(1.02) !important; + box-shadow: 0 35px 70px rgba(0, 0, 0, 0.35) !important; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-gradient-to-r { + padding: 5rem !important; + border-radius: 3rem !important; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.1)) !important; + backdrop-filter: blur(30px) !important; + border: 2px solid rgba(255, 255, 255, 0.4) !important; + box-shadow: 0 30px 60px rgba(0, 0, 0, 0.4) !important; + position: relative; + overflow: hidden; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-gradient-to-r::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: conic-gradient(from 0deg, transparent, rgba(255, 255, 255, 0.1), transparent); + animation: rotate 10s linear infinite; + pointer-events: none; +} + +@keyframes rotate { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-gradient-to-r .text-3xl { + font-size: 7rem !important; + font-weight: 900 !important; + text-shadow: 0 8px 16px rgba(0, 0, 0, 0.5) !important; + color: white !important; + letter-spacing: -0.02em; + position: relative; + z-index: 1; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-gradient-to-r .text-gray-700 { + font-size: 2rem !important; + font-weight: 700 !important; + color: white !important; + text-shadow: 0 3px 6px rgba(0, 0, 0, 0.4); + position: relative; + z-index: 1; +} + +[data-fullscreen-target="event-overview"]:fullscreen .bg-gradient-to-r .text-gray-600 { + font-size: 1.25rem !important; + line-height: 1.6 !important; + color: rgba(255, 255, 255, 0.9) !important; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + position: relative; + z-index: 1; +} + +/* Fullscreen styles for Ready to Reveal Results */ +[data-fullscreen-target="reveal-results"]:fullscreen { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100vh; + position: relative; + padding: 1rem; + overflow-y: auto; + overflow-x: hidden; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .reveal-content { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + max-width: 1200px; + width: 100%; + height: 100vh; + padding: 1rem !important; + position: relative; + overflow-y: auto; +} + +/* Hide normal fullscreen button in fullscreen mode */ +[data-fullscreen-target="reveal-results"]:fullscreen .fullscreen-toggle-btn { + display: none !important; +} + +/* Show exit fullscreen button in fullscreen mode */ +[data-fullscreen-target="reveal-results"]:fullscreen .exit-fullscreen-btn { + display: flex !important; + position: absolute !important; + top: 2rem !important; + right: 2rem !important; + background: rgba(255, 255, 255, 0.2) !important; + border: 1px solid rgba(255, 255, 255, 0.3) !important; + color: white !important; + backdrop-filter: blur(10px); + padding: 1rem 2rem !important; + font-size: 1.2rem !important; + border-radius: 1rem !important; + transition: all 0.3s ease !important; + z-index: 100; + align-items: center; + gap: 0.5rem; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .exit-fullscreen-btn:hover { + background: rgba(255, 255, 255, 0.3) !important; + transform: scale(1.05) !important; +} + +/* Show event details in fullscreen */ +[data-fullscreen-target="reveal-results"]:fullscreen .event-details-fullscreen { + display: block !important; + width: 100%; + margin-bottom: 1.5rem; + animation: fadeInUp 0.8s ease-out; + position: relative; + z-index: 10; +} + +/* Ensure all content is above background effects */ +[data-fullscreen-target="reveal-results"]:fullscreen > * { + position: relative; + z-index: 10; +} + +/* Event stats grid - more compact */ +[data-fullscreen-target="reveal-results"]:fullscreen .event-stats-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + margin-bottom: 1.5rem; + width: 100%; + max-width: 900px; + margin-left: auto; + margin-right: auto; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .stat-card { + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(20px); + border-radius: 1rem; + padding: 1.5rem; + text-align: center; + border: 1px solid rgba(255, 255, 255, 0.3); + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .stat-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05)); + pointer-events: none; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .stat-card:hover { + transform: translateY(-10px) scale(1.02); + box-shadow: 0 30px 50px rgba(0, 0, 0, 0.3); +} + +[data-fullscreen-target="reveal-results"]:fullscreen .stat-icon { + height: 3rem !important; + width: 3rem !important; + color: white !important; + margin: 0 auto 0.75rem !important; + filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3)); +} + +[data-fullscreen-target="reveal-results"]:fullscreen .stat-value { + font-size: 2.5rem !important; + font-weight: 900 !important; + color: white !important; + text-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); + margin-bottom: 0.25rem; + letter-spacing: -0.02em; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .stat-label { + font-size: 1rem !important; + font-weight: 600 !important; + color: rgba(255, 255, 255, 0.9) !important; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); +} + +/* Participation rate card - more compact */ +[data-fullscreen-target="reveal-results"]:fullscreen .participation-rate-card { + background: linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.1)); + backdrop-filter: blur(30px); + border-radius: 1.5rem; + padding: 2rem; + text-align: center; + border: 2px solid rgba(255, 255, 255, 0.4); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4); + margin-bottom: 2rem; + position: relative; + overflow: hidden; + max-width: 600px; + margin-left: auto; + margin-right: auto; + width: 100%; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .rate-value { + font-size: 4rem !important; + font-weight: 900 !important; + color: white !important; + text-shadow: 0 6px 12px rgba(0, 0, 0, 0.5); + margin-bottom: 0.5rem; + background: linear-gradient(135deg, #FFD700, #FFA500); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .rate-label { + font-size: 1.25rem !important; + font-weight: 700 !important; + color: white !important; + text-shadow: 0 3px 6px rgba(0, 0, 0, 0.4); + margin-bottom: 0.25rem; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .rate-description { + font-size: 0.9rem !important; + color: rgba(255, 255, 255, 0.9) !important; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); +} + +/* Main reveal content styling - compact for fitting in viewport */ +[data-fullscreen-target="reveal-results"]:fullscreen .reveal-content > div:last-of-type { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(20px); + border-radius: 2rem; + padding: 2rem; + border: 2px solid rgba(255, 255, 255, 0.3); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); + position: relative; + max-width: 800px; + margin: 1rem auto; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .trophy-icon { + height: 5rem !important; + width: 5rem !important; + color: #FFD700 !important; + filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.4)); + animation: pulse 2s ease-in-out infinite, float 3s ease-in-out infinite; + margin-bottom: 1rem !important; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .reveal-title { + font-size: 2.5rem !important; + font-weight: 900 !important; + color: white !important; + text-shadow: 0 6px 12px rgba(0, 0, 0, 0.5); + letter-spacing: -0.02em; + margin-bottom: 1rem !important; + text-align: center !important; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .reveal-description { + font-size: 1.25rem !important; + color: rgba(255, 255, 255, 0.9) !important; + text-shadow: 0 3px 6px rgba(0, 0, 0, 0.4); + margin-bottom: 2rem !important; + line-height: 1.4 !important; + text-align: center !important; + max-width: 600px; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .reveal-button { + font-size: 1.25rem !important; + padding: 1rem 2.5rem !important; + border-radius: 1rem !important; + background: linear-gradient(135deg, #3B82F6, #8B5CF6) !important; + box-shadow: 0 10px 20px rgba(59, 130, 246, 0.4) !important; + transition: all 0.3s ease !important; + text-transform: uppercase; + letter-spacing: 0.1em; + font-weight: 700 !important; + color: white !important; + border: 2px solid rgba(255, 255, 255, 0.3) !important; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .reveal-button { + position: relative; + overflow: hidden; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .reveal-button::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient( + 45deg, + transparent 30%, + rgba(255, 255, 255, 0.3) 50%, + transparent 70% + ); + animation: shimmerButton 3s infinite; + pointer-events: none; +} + +@keyframes shimmerButton { + 0% { + transform: translateX(-100%) translateY(-100%) rotate(45deg); + } + 100% { + transform: translateX(100%) translateY(100%) rotate(45deg); + } +} + +[data-fullscreen-target="reveal-results"]:fullscreen .reveal-button:hover { + transform: translateY(-5px) scale(1.05) !important; + box-shadow: + 0 20px 40px rgba(59, 130, 246, 0.6), + 0 0 60px rgba(139, 92, 246, 0.4) !important; + background: linear-gradient(135deg, #2563EB, #7C3AED) !important; + border-color: rgba(255, 255, 255, 0.5) !important; +} + +[data-fullscreen-target="reveal-results"]:fullscreen .reveal-button svg { + height: 1.75rem !important; + width: 1.75rem !important; + margin-right: 0.75rem !important; +} + +@keyframes pulse { + 0%, 100% { + transform: scale(1); + } + 50% { + transform: scale(1.1); + } +} + +/* Add floating animation and sparkles for trophy */ +[data-fullscreen-target="reveal-results"]:fullscreen .trophy-icon { + animation: pulse 2s ease-in-out infinite, float 3s ease-in-out infinite; + position: relative; +} + + +@keyframes float { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-20px); + } +} + +/* Add shimmer effect to the container */ +[data-fullscreen-target="reveal-results"]:fullscreen .reveal-content::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent); + animation: shimmer 3s infinite; +} + +@keyframes shimmer { + 0% { + left: -100%; + } + 100% { + left: 100%; + } +} + +/* Fullscreen Results Container Styles */ +[data-fullscreen-target="results-container"]:fullscreen { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + position: relative; + padding: 2rem; + overflow-y: auto; +} + +/* Multiple Fireworks */ +[data-fullscreen-target="results-container"]:fullscreen::before { + content: ''; + position: fixed; + bottom: 0; + left: 20%; + width: 6px; + height: 6px; + background: gold; + border-radius: 50%; + box-shadow: 0 0 10px gold; + animation: firework1 4s ease-out infinite; + pointer-events: none; + z-index: 1; +} + +[data-fullscreen-target="results-container"]:fullscreen::after { + content: ''; + position: fixed; + bottom: 0; + right: 30%; + width: 6px; + height: 6px; + background: #FF6B6B; + border-radius: 50%; + box-shadow: 0 0 10px #FF6B6B; + animation: firework2 4s ease-out infinite 1.5s; + pointer-events: none; + z-index: 1; +} + +/* Additional fireworks using child elements */ +[data-fullscreen-target="results-container"]:fullscreen .results-display::before { + content: ''; + position: fixed; + bottom: 0; + left: 60%; + width: 5px; + height: 5px; + background: #10B981; + border-radius: 50%; + box-shadow: 0 0 10px #10B981; + animation: firework3 4s ease-out infinite 2.5s; + pointer-events: none; + z-index: 1; +} + +[data-fullscreen-target="results-container"]:fullscreen .results-display::after { + content: ''; + position: fixed; + bottom: 0; + right: 10%; + width: 5px; + height: 5px; + background: #8B5CF6; + border-radius: 50%; + box-shadow: 0 0 10px #8B5CF6; + animation: firework4 4s ease-out infinite 3.2s; + pointer-events: none; + z-index: 1; +} + +@keyframes firework3 { + 0% { + transform: translateY(0) scale(1); + opacity: 1; + bottom: 0; + } + 50% { + transform: translateY(-450px) scale(1); + opacity: 1; + } + 51% { + transform: translateY(-450px) scale(1); + opacity: 1; + box-shadow: + 0 -20px 0 #10B981, + 14px -14px 0 #10B981, + 20px 0 0 #10B981, + 14px 14px 0 #10B981, + 0 20px 0 #10B981, + -14px 14px 0 #10B981, + -20px 0 0 #10B981, + -14px -14px 0 #10B981; + } + 100% { + transform: translateY(-450px) scale(0); + opacity: 0; + box-shadow: + 0 -45px 0 transparent, + 35px -35px 0 transparent, + 45px 0 0 transparent, + 35px 35px 0 transparent, + 0 45px 0 transparent, + -35px 35px 0 transparent, + -45px 0 0 transparent, + -35px -35px 0 transparent; + } +} + +@keyframes firework4 { + 0% { + transform: translateY(0) scale(1); + opacity: 1; + bottom: 0; + } + 50% { + transform: translateY(-380px) scale(1); + opacity: 1; + } + 51% { + transform: translateY(-380px) scale(1); + opacity: 1; + box-shadow: + 0 -18px 0 #8B5CF6, + 18px -12px 0 #8B5CF6, + 18px 12px 0 #8B5CF6, + 0 18px 0 #8B5CF6, + -18px 12px 0 #8B5CF6, + -18px -12px 0 #8B5CF6; + } + 100% { + transform: translateY(-380px) scale(0); + opacity: 0; + box-shadow: + 0 -50px 0 transparent, + 50px -35px 0 transparent, + 50px 35px 0 transparent, + 0 50px 0 transparent, + -50px 35px 0 transparent, + -50px -35px 0 transparent; + } +} + +@keyframes firework1 { + 0% { + transform: translateY(0) scale(1); + opacity: 1; + bottom: 0; + } + 50% { + transform: translateY(-400px) scale(1); + opacity: 1; + } + 51% { + transform: translateY(-400px) scale(1); + opacity: 1; + box-shadow: + 0 -20px 0 gold, + 20px -15px 0 gold, + 20px 15px 0 gold, + 0 20px 0 gold, + -20px 15px 0 gold, + -20px -15px 0 gold, + 30px 0 0 gold, + -30px 0 0 gold; + } + 100% { + transform: translateY(-400px) scale(0); + opacity: 0; + box-shadow: + 0 -50px 0 transparent, + 50px -35px 0 transparent, + 50px 35px 0 transparent, + 0 50px 0 transparent, + -50px 35px 0 transparent, + -50px -35px 0 transparent, + 70px 0 0 transparent, + -70px 0 0 transparent; + } +} + +@keyframes firework2 { + 0% { + transform: translateY(0) scale(1); + opacity: 1; + bottom: 0; + } + 50% { + transform: translateY(-350px) scale(1); + opacity: 1; + } + 51% { + transform: translateY(-350px) scale(1); + opacity: 1; + box-shadow: + 0 -25px 0 #FF6B6B, + 25px -18px 0 #FF6B6B, + 25px 18px 0 #FF6B6B, + 0 25px 0 #FF6B6B, + -25px 18px 0 #FF6B6B, + -25px -18px 0 #FF6B6B, + 35px 0 0 #FF6B6B, + -35px 0 0 #FF6B6B; + } + 100% { + transform: translateY(-350px) scale(0); + opacity: 0; + box-shadow: + 0 -60px 0 transparent, + 60px -45px 0 transparent, + 60px 45px 0 transparent, + 0 60px 0 transparent, + -60px 45px 0 transparent, + -60px -45px 0 transparent, + 80px 0 0 transparent, + -80px 0 0 transparent; + } +} + +[data-fullscreen-target="results-container"]:fullscreen .countdown-card { + background: transparent !important; + border: none !important; + box-shadow: none !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .results-display { + padding: 3rem; +} + +/* Fullscreen Countdown Styles */ +[data-fullscreen-target="results-container"]:fullscreen .countdown-content { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 100vh; +} + +/* Old standalone countdown styles - keep for backwards compatibility */ +[data-fullscreen-target="countdown"]:fullscreen { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 100vh; + position: relative; +} + +[data-fullscreen-target="countdown"]:fullscreen .countdown-content { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; +} + +[data-fullscreen-target="results-container"]:fullscreen .fullscreen-toggle-btn { + display: none !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .exit-fullscreen-btn { + display: flex !important; + position: absolute !important; + top: 2rem !important; + right: 2rem !important; + background: rgba(255, 255, 255, 0.2) !important; + border: 1px solid rgba(255, 255, 255, 0.3) !important; + color: white !important; + backdrop-filter: blur(10px); + padding: 1rem 2rem !important; + font-size: 1.2rem !important; + border-radius: 1rem !important; + z-index: 100; +} + +[data-fullscreen-target="results-container"]:fullscreen .countdown-wrapper { + transform: scale(2); + margin: 8rem 0; +} + +[data-fullscreen-target="results-container"]:fullscreen .countdown-number { + font-size: 12rem !important; + color: white !important; + text-shadow: + 0 10px 20px rgba(0, 0, 0, 0.5), + 0 0 60px rgba(255, 255, 255, 0.5), + 0 0 100px rgba(255, 255, 255, 0.3); + position: relative; +} + + +[data-fullscreen-target="results-container"]:fullscreen .countdown-number.text-red-500 { + color: #FF6B6B !important; + animation: pulse 0.5s ease-in-out infinite; + text-shadow: + 0 10px 20px rgba(255, 0, 0, 0.5), + 0 0 60px rgba(255, 100, 100, 0.8), + 0 0 100px rgba(255, 100, 100, 0.5); +} + +[data-fullscreen-target="results-container"]:fullscreen .countdown-circle { + width: 20rem !important; + height: 20rem !important; + border-width: 8px !important; + border-color: rgba(255, 255, 255, 0.3) !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .countdown-circle.border-red-200 { + border-color: rgba(255, 107, 107, 0.5) !important; + box-shadow: 0 0 100px rgba(255, 107, 107, 0.5); +} + +[data-fullscreen-target="results-container"]:fullscreen .countdown-text { + font-size: 3rem !important; + color: white !important; + text-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.2em; +} + +/* Fullscreen Results Display Styles within Container */ +[data-fullscreen-target="results-container"]:fullscreen .fullscreen-results-btn { + position: fixed !important; + top: 2rem !important; + right: 2rem !important; + background: rgba(255, 255, 255, 0.2) !important; + border: 1px solid rgba(255, 255, 255, 0.3) !important; + color: white !important; + backdrop-filter: blur(10px); + padding: 1rem 2rem !important; + z-index: 100; +} + +[data-fullscreen-target="results-container"]:fullscreen .maximize-icon { + display: none !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .minimize-icon { + display: block !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .btn-text::after { + content: "Exit Fullscreen" !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .btn-text { + font-size: 0 !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .btn-text::after { + font-size: 1rem !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .results-card { + background: rgba(255, 255, 255, 0.1) !important; + backdrop-filter: blur(20px) !important; + border: 2px solid rgba(255, 255, 255, 0.3) !important; + max-width: 1400px; + margin: 0 auto; +} + +[data-fullscreen-target="results-container"]:fullscreen .results-title { + font-size: 4rem !important; + color: white !important; + text-shadow: 0 6px 12px rgba(0, 0, 0, 0.5); + font-weight: 900 !important; + margin-bottom: 1rem; +} + +[data-fullscreen-target="results-container"]:fullscreen .results-subtitle { + font-size: 1.5rem !important; + color: rgba(255, 255, 255, 0.9) !important; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); +} + +[data-fullscreen-target="results-container"]:fullscreen .profile-image-container { + transform: scale(1.3); + margin: 0 1rem; +} + +[data-fullscreen-target="results-container"]:fullscreen h3 { + font-size: 2rem !important; + color: white !important; + text-shadow: 0 3px 6px rgba(0, 0, 0, 0.4); +} + +[data-fullscreen-target="results-container"]:fullscreen p { + color: rgba(255, 255, 255, 0.9) !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .text-3xl { + font-size: 4rem !important; + color: white !important; + text-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); +} + +[data-fullscreen-target="results-container"]:fullscreen .bg-white { + background: rgba(255, 255, 255, 0.15) !important; + backdrop-filter: blur(10px) !important; + border: 1px solid rgba(255, 255, 255, 0.3) !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .text-gray-900, +[data-fullscreen-target="results-container"]:fullscreen .text-gray-600, +[data-fullscreen-target="results-container"]:fullscreen .text-gray-500, +[data-fullscreen-target="results-container"]:fullscreen .text-gray-700 { + color: white !important; +} + +[data-fullscreen-target="results-container"]:fullscreen .bg-gradient-to-r.from-blue-50 { + background: linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.1)) !important; + backdrop-filter: blur(20px) !important; + border: 2px solid rgba(255, 255, 255, 0.4) !important; +} + +/* Vertical Bar Chart Styles */ +.vertical-chart-container { + padding: 2rem 0; +} + +.vertical-bar { + position: relative; + animation: growUp 2s ease-out forwards; + transform-origin: bottom; +} + +@keyframes growUp { + from { + height: 0 !important; + } +} + +/* Enhanced fullscreen vertical chart with sparkles */ +[data-fullscreen-target="results-container"]:fullscreen .vertical-chart-container { + transform: scale(1.2); + margin: 2rem 0; + position: relative; +} + +[data-fullscreen-target="results-container"]:fullscreen .vertical-bar { + box-shadow: + 0 -10px 30px rgba(0, 0, 0, 0.3), + inset 0 0 20px rgba(255, 255, 255, 0.2) !important; + position: relative; + overflow: visible !important; +} + +/* Firework burst for winner bar */ +[data-fullscreen-target="results-container"]:fullscreen .vertical-chart-container > div > div:first-child .vertical-bar::before { + content: ''; + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + width: 6px; + height: 6px; + background: gold; + border-radius: 50%; + animation: winnerFirework 3s ease-out infinite; +} + +@keyframes winnerFirework { + 0% { + transform: translateX(-50%) translateY(0); + opacity: 0; + box-shadow: none; + } + 10% { + transform: translateX(-50%) translateY(-20px); + opacity: 1; + box-shadow: 0 0 10px gold; + } + 15% { + transform: translateX(-50%) translateY(-30px); + opacity: 1; + box-shadow: + 0 -15px 0 gold, + 15px -10px 0 gold, + 15px 10px 0 gold, + 0 15px 0 gold, + -15px 10px 0 gold, + -15px -10px 0 gold; + } + 100% { + transform: translateX(-50%) translateY(-50px); + opacity: 0; + box-shadow: + 0 -40px 0 transparent, + 40px -30px 0 transparent, + 40px 30px 0 transparent, + 0 40px 0 transparent, + -40px 30px 0 transparent, + -40px -30px 0 transparent; + } +} + +/* Special sparkle effect for winner */ +[data-fullscreen-target="results-container"]:fullscreen .vertical-chart-container > div > div:first-child .vertical-bar { + animation: winnerGlow 2s ease-in-out infinite; +} + +@keyframes winnerGlow { + 0%, 100% { + filter: brightness(1) drop-shadow(0 0 20px gold); + } + 50% { + filter: brightness(1.2) drop-shadow(0 0 40px gold); + } +} + +/* Golden glow for winner crown */ +[data-fullscreen-target="results-container"]:fullscreen .text-yellow-500.animate-bounce { + filter: drop-shadow(0 0 15px gold); + position: relative; +} + /* Gradient backgrounds for bars */ .bar-gradient-1 { background: linear-gradient(135deg, #3B82F6, #1D4ED8); } .bar-gradient-2 { background: linear-gradient(135deg, #10B981, #047857); } diff --git a/app/results/page.tsx b/app/results/page.tsx index c32e138..7d004a1 100644 --- a/app/results/page.tsx +++ b/app/results/page.tsx @@ -5,7 +5,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import { Button } from "@/components/ui/button" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Badge } from "@/components/ui/badge" -import { BarChart3, Users, Trophy, TrendingUp, ArrowLeft, RotateCcw, Download, Eye, Maximize2, Minimize2, Table } from "lucide-react" +import { BarChart3, Users, Trophy, TrendingUp, ArrowLeft, RotateCcw, Download, Eye, Maximize2, Minimize2, Table, Clock, RefreshCw } from "lucide-react" import Link from "next/link" import Image from "next/image" import { AuthGuard } from "@/components/auth-guard" @@ -24,6 +24,14 @@ interface VoteEvent { end_date: string is_active: boolean is_voting_open: boolean + results_open: boolean +} + +interface VoteEventDetails { + vote_event: VoteEvent + total_participants: number + total_voted: number + total_not_voted: number } interface Candidate { @@ -56,12 +64,16 @@ function ResultsPageContent() { const [events, setEvents] = useState([]) const [selectedEventId, setSelectedEventId] = useState("") const [results, setResults] = useState(null) + const [eventDetails, setEventDetails] = useState(null) const [loading, setLoading] = useState(false) const [eventsLoading, setEventsLoading] = useState(true) const [isFullPageChart, setIsFullPageChart] = useState(false) const [showCountdown, setShowCountdown] = useState(false) const [countdown, setCountdown] = useState(10) const [showResults, setShowResults] = useState(false) + const [isFullscreen, setIsFullscreen] = useState(false) + const [isRefreshing, setIsRefreshing] = useState(false) + const [lastUpdated, setLastUpdated] = useState(new Date()) // Chart colors const COLORS = ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#06B6D4', '#84CC16', '#F97316'] @@ -74,7 +86,17 @@ function ResultsPageContent() { useEffect(() => { if (selectedEventId) { - fetchResults(selectedEventId) + fetchEventDetails(selectedEventId, false) + fetchResults(selectedEventId, false) + + // Set up interval for live updates every 10 seconds + const interval = setInterval(() => { + fetchEventDetails(selectedEventId, true) + fetchResults(selectedEventId, true) + }, 10000) // 10 seconds + + // Cleanup interval on unmount or when selectedEventId changes + return () => clearInterval(interval) } }, [selectedEventId]) @@ -85,12 +107,34 @@ function ResultsPageContent() { setCountdown(prev => prev - 1) }, 1000) } else if (showCountdown && countdown === 0) { + // Simply transition to results - fullscreen container remains the same setShowCountdown(false) setShowResults(true) } return () => clearInterval(interval) }, [showCountdown, countdown]) + // Fullscreen change listener + useEffect(() => { + const handleFullscreenChange = () => { + const fullscreenElement = document.fullscreenElement + setIsFullscreen(!!fullscreenElement) + + // Add/remove fullscreen styles to the event overview card + const eventOverviewCard = document.querySelector('[data-fullscreen-target="event-overview"]') + if (eventOverviewCard) { + if (fullscreenElement === eventOverviewCard) { + eventOverviewCard.classList.add('fullscreen-active') + } else { + eventOverviewCard.classList.remove('fullscreen-active') + } + } + } + + document.addEventListener('fullscreenchange', handleFullscreenChange) + return () => document.removeEventListener('fullscreenchange', handleFullscreenChange) + }, []) + const fetchEvents = async () => { try { setEventsLoading(true) @@ -115,28 +159,61 @@ function ResultsPageContent() { } } - const fetchResults = async (eventId: string) => { + const fetchEventDetails = async (eventId: string, showRefreshIndicator = false) => { try { - setLoading(true) + if (showRefreshIndicator) setIsRefreshing(true) + const response = await apiClient.get(`/api/v1/vote-events/${eventId}/details`) + if (response.data.success) { + setEventDetails(response.data.data) + setLastUpdated(new Date()) + } + } catch (error) { + console.error('Error fetching event details:', error) + // Only show toast on initial load, not on refresh + if (!showRefreshIndicator) { + toast({ + title: "Error", + description: "Failed to fetch event details", + variant: "destructive" + }) + } + } finally { + if (showRefreshIndicator) { + setTimeout(() => setIsRefreshing(false), 500) // Show indicator for at least 500ms + } + } + } + + const fetchResults = async (eventId: string, showRefreshIndicator = false) => { + try { + if (!showRefreshIndicator) setLoading(true) + if (showRefreshIndicator) setIsRefreshing(true) const response = await apiClient.get(`${API_CONFIG.ENDPOINTS.RESULTS}/${eventId}/results`) if (response.data.success) { setResults(response.data.data) + setLastUpdated(new Date()) } } catch (error) { console.error('Error fetching results:', error) - toast({ - title: "Error", - description: "Failed to fetch voting results", - variant: "destructive" - }) + // Only show toast on initial load, not on refresh + if (!showRefreshIndicator) { + toast({ + title: "Error", + description: "Failed to fetch voting results", + variant: "destructive" + }) + } } finally { - setLoading(false) + if (!showRefreshIndicator) setLoading(false) + if (showRefreshIndicator) { + setTimeout(() => setIsRefreshing(false), 500) // Show indicator for at least 500ms + } } } const getChartData = (): (ChartData & { image_url: string; id: string })[] => { if (!results) return [] - + return results.candidates .sort((a, b) => b.vote_count - a.vote_count) // Sort by vote count descending .map((candidate, index) => ({ @@ -151,7 +228,7 @@ function ResultsPageContent() { const getWinner = (): Candidate | null => { if (!results || results.candidates.length === 0) return null - return results.candidates.reduce((prev, current) => + return results.candidates.reduce((prev, current) => prev.vote_count > current.vote_count ? prev : current ) } @@ -168,11 +245,11 @@ function ResultsPageContent() { const getStatusBadge = (event: VoteEvent) => { const status = getEventStatus(event) - + if (event.is_voting_open && status === "active") { return Live Voting } - + switch (status) { case "active": return Active @@ -194,9 +271,41 @@ function ResultsPageContent() { }) return } + + // Check if we're in fullscreen mode from the reveal button + const isInFullscreen = document.fullscreenElement !== null + setShowCountdown(true) setCountdown(10) setShowResults(false) + + // If we were in fullscreen from reveal, maintain it by making the container fullscreen + if (isInFullscreen) { + setTimeout(() => { + const container = document.querySelector('[data-fullscreen-target="results-container"]') + if (container && !document.fullscreenElement) { + container.requestFullscreen().catch(err => { + console.error('Error maintaining fullscreen:', err) + }) + } + }, 100) + } + } + + const toggleFullscreen = () => { + if (!document.fullscreenElement) { + // Find the event overview card and make it fullscreen + const eventOverviewCard = document.querySelector('[data-fullscreen-target="event-overview"]') + if (eventOverviewCard) { + eventOverviewCard.requestFullscreen().catch(err => { + console.error('Error attempting to enable fullscreen:', err) + }) + setIsFullscreen(true) + } + } else { + document.exitFullscreen() + setIsFullscreen(false) + } } const selectedEvent = events.find(e => e.id === selectedEventId) @@ -214,13 +323,25 @@ function ResultsPageContent() {

Voting Results

View detailed voting results and statistics

+ {selectedEventId && ( +
+
+ + {isRefreshing ? 'Updating...' : 'Live updates every 10s'} +
+ + + Last updated: {lastUpdated.toLocaleTimeString()} + +
+ )}
{results && ( <> - )} +
- + {selectedEvent && (
@@ -303,193 +441,396 @@ function ResultsPageContent() {
)} + {/* Loading Event Details */} + {!loading && selectedEventId && !eventDetails && ( +
+
+

Loading event details...

+
+ )} + {/* Results Content */} {!loading && selectedEventId && results && ( <> - {/* Show Results Button */} + {/* Event Statistics */} {!showCountdown && !showResults && ( - - - -

Ready to Reveal Results?

-

- Click the button below to see the voting results for "{selectedEvent?.title}" -

- -
-
+ <> + {eventDetails ? ( +
+ {/* Event Overview */} + + +
+ {isRefreshing && ( + + + Updating + + )} + +
+ Live +
+
+ + {eventDetails.vote_event.title} + +
+ +
+
+ +
+ {eventDetails.total_participants} +
+

Total Participants

+
+ +
+ +
+ {eventDetails.total_voted} +
+

Total Voted

+
+ +
+ +
+ {eventDetails.total_not_voted} +
+

Not Voted

+
+
+ + {/* Participation Rate */} +
+
+
+ {eventDetails.total_participants > 0 + ? ((eventDetails.total_voted / eventDetails.total_participants) * 100).toFixed(1) + : '0' + }% +
+

Participation Rate

+

+ {eventDetails.total_voted} out of {eventDetails.total_participants} participants have voted +

+
+
+
+
+
+ ) : ( +
+ + + +

Event details not available

+
+
+
+ )} + + {/* Show Results Button */} + + + + + {/* Exit fullscreen button (hidden by default, shown in fullscreen) */} + + + {/* Event Details Section (hidden by default, shown in fullscreen) */} + {eventDetails && ( +
+
+
+ +
{eventDetails.total_participants}
+

Total Participants

+
+ +
+ +
{eventDetails.total_voted}
+

Total Voted

+
+ +
+ +
{eventDetails.total_not_voted}
+

Not Voted

+
+
+ +
+
+ {eventDetails.total_participants > 0 + ? ((eventDetails.total_voted / eventDetails.total_participants) * 100).toFixed(1) + : '0' + }% +
+

Participation Rate

+

+ {eventDetails.total_voted} out of {eventDetails.total_participants} participants have voted +

+
+
+ )} + + +

Ready to Reveal Results?

+

+ Click the button below to see the voting results for "{selectedEvent?.title}" +

+ +
+
+ + )} - {/* Countdown Animation */} - {showCountdown && ( - - -
-
+ {/* Countdown Animation */} + {showCountdown && ( + + + + + + +
+
{countdown}
-
-

- {countdown > 5 ? 'Preparing results...' : +

+ {countdown > 5 ? 'Preparing results...' : countdown > 3 ? 'Almost ready...' : 'Here we go!'}

-
-
- )} - - {/* Animated Results Display */} - {showResults && ( -
- {/* Winner Announcement */} - - -
- -
-

- 🎉 Winner: {winner?.name || "No votes yet"} 🎉 -

-

- {winner ? `${winner.vote_count} votes (${((winner.vote_count / results.total_votes) * 100).toFixed(1)}%)` : "Waiting for votes"} -

+ )} + {/* Animated Results Display */} + {showResults && ( +
+ {/* Fullscreen Results Button */} +
+ +
{/* Animated Bar Chart with Profile Images */} - + - Final Results - Vote distribution by candidate +
+ {isRefreshing && ( + + + Updating + + )} + +
+ Live Results +
+
+ Final Results + + Vote distribution by candidate • Updated: {lastUpdated.toLocaleTimeString()} +
{chartData.length > 0 ? ( -
- {chartData.map((candidate, index) => { - const maxVotes = Math.max(...chartData.map(c => c.votes)) - const barWidth = candidate.votes > 0 ? (candidate.votes / maxVotes) * 100 : 0 - - return ( -
- {/* Candidate Info Row */} -
- {/* Profile Image */} -
- {candidate.image_url ? ( - {candidate.name} - ) : ( -
- -
- )} - {/* Rank Badge */} -
-
- #{index + 1} -
-
- {/* Winner Crown */} - {index === 0 && candidate.votes > 0 && ( -
- -
- )} -
- - {/* Candidate Name and Info */} -
-
-

{candidate.name}

- {index < 3 && ( - - {index === 0 ? '🏆 Winner' : index === 1 ? '🥈 2nd' : '🥉 3rd'} - - )} -
-

{candidate.votes} votes

-
- - {/* Percentage Display */} -
-
- {candidate.percentage.toFixed(1)}% -
-
- of {results.total_votes} votes -
-
-
- - {/* Enhanced Animated Bar */} -
+
+ {/* Vertical Bar Chart */} +
+ {chartData.map((candidate, index) => { + const maxVotes = Math.max(...chartData.map(c => c.votes)) + const barHeight = candidate.votes > 0 ? (candidate.votes / maxVotes) * 100 : 0 + + return ( +
+ {/* Percentage above bar */}
- {/* Shimmer Effect */} -
- - {/* Vote count inside bar */} -
- {barWidth > 20 && ( - - {candidate.name} - - )} - {barWidth > 10 && ( - + {candidate.percentage.toFixed(1)}% +
+ + {/* Vertical Bar Container */} +
+ {/* Background bar */} +
+ + {/* Animated Bar */} +
0 ? '40px' : '0' + }} + > + {/* Shimmer Effect */} +
+ + {/* Vote count inside bar */} +
+ {candidate.votes} - )} +
- - {/* Percentage label outside bar for small bars */} - {barWidth < 20 && candidate.votes > 0 && ( -
- {candidate.votes} + + {/* Candidate Info Below Bar */} +
+ {/* Profile Image */} +
+ {candidate.image_url ? ( + {candidate.name} + ) : ( +
+ +
+ )} + + {/* Rank Badge */} +
+
+ #{index + 1} +
+
+ + {/* Winner Crown */} + {index === 0 && candidate.votes > 0 && ( +
+ +
+ )}
- )} + + {/* Candidate Name */} +

{candidate.name}

+ + {/* Winner/Place Badge */} + {index < 3 && candidate.votes > 0 && ( + + {index === 0 ? '🏆 Winner' : index === 1 ? '🥈 2nd' : '🥉 3rd'} + + )} +
-
- ) - })} - + ) + })} +
+ {/* Total Votes Summary */}
@@ -510,55 +851,9 @@ function ResultsPageContent() { )} - - {/* Summary Stats */} -
- - - Total Votes - - - -
{results.total_votes}
-

- Cast across all candidates -

-
-
- - - - Candidates - - - -
{results.candidates.length}
-

- Participated in voting -

-
-
- - - - Winning Margin - - - -
- {winner && results.candidates.length > 1 ? - `${Math.max(0, winner.vote_count - Math.max(...results.candidates.filter(c => c.id !== winner.id).map(c => c.vote_count)))}` : - '0' - } -
-

- Vote difference -

-
-
-
)} +
)} @@ -603,4 +898,4 @@ export default function ResultsPage() { ) -} \ No newline at end of file +}