From 059e6eedf9a642c43a8e735f2849aac62aa3e4d1 Mon Sep 17 00:00:00 2001 From: "n.tolstov" Date: Sun, 30 Nov 2025 23:18:39 +0300 Subject: [PATCH] feat: Implement a cyberpunk-themed UI with new decorative components, illustrations, and visual effects. --- frontend/src/app/contests/page.tsx | 200 +++-- frontend/src/app/globals.css | 681 +++++++++++++++--- frontend/src/app/layout.tsx | 34 +- frontend/src/app/login/page.tsx | 269 ++++--- frontend/src/app/page.tsx | 414 +++++++---- frontend/src/app/profile/page.tsx | 431 ++++++----- frontend/src/app/register/page.tsx | 387 ++++++---- frontend/src/app/submissions/page.tsx | 501 +++++++------ frontend/src/components/Navbar.tsx | 132 ++-- frontend/src/components/VolguLogo.tsx | 86 ++- .../components/decorative/CornerBrackets.tsx | 78 ++ .../src/components/decorative/GlitchText.tsx | 72 ++ .../src/components/decorative/GlowOrbs.tsx | 75 ++ .../src/components/decorative/NeonBorder.tsx | 56 ++ .../components/decorative/TypewriterText.tsx | 65 ++ frontend/src/components/decorative/index.ts | 5 + .../src/components/domain/contest-card.tsx | 164 ++++- .../src/components/domain/contest-timer.tsx | 128 +++- .../src/components/effects/MatrixRain.tsx | 151 ++++ .../components/effects/ScanlineOverlay.tsx | 48 ++ frontend/src/components/effects/index.ts | 2 + .../illustrations/AuthIllustration.tsx | 204 ++++++ .../illustrations/EmptyStateIllustration.tsx | 147 ++++ .../illustrations/HeroIllustration.tsx | 278 +++++++ .../illustrations/TrophyIllustration.tsx | 158 ++++ .../src/components/illustrations/index.ts | 4 + frontend/src/components/ui/badge.tsx | 56 +- frontend/src/components/ui/button.tsx | 71 +- frontend/src/components/ui/card.tsx | 66 +- frontend/src/components/ui/input.tsx | 21 +- 30 files changed, 3935 insertions(+), 1049 deletions(-) create mode 100644 frontend/src/components/decorative/CornerBrackets.tsx create mode 100644 frontend/src/components/decorative/GlitchText.tsx create mode 100644 frontend/src/components/decorative/GlowOrbs.tsx create mode 100644 frontend/src/components/decorative/NeonBorder.tsx create mode 100644 frontend/src/components/decorative/TypewriterText.tsx create mode 100644 frontend/src/components/decorative/index.ts create mode 100644 frontend/src/components/effects/MatrixRain.tsx create mode 100644 frontend/src/components/effects/ScanlineOverlay.tsx create mode 100644 frontend/src/components/effects/index.ts create mode 100644 frontend/src/components/illustrations/AuthIllustration.tsx create mode 100644 frontend/src/components/illustrations/EmptyStateIllustration.tsx create mode 100644 frontend/src/components/illustrations/HeroIllustration.tsx create mode 100644 frontend/src/components/illustrations/TrophyIllustration.tsx create mode 100644 frontend/src/components/illustrations/index.ts diff --git a/frontend/src/app/contests/page.tsx b/frontend/src/app/contests/page.tsx index 2fcf025..0a7fbbb 100644 --- a/frontend/src/app/contests/page.tsx +++ b/frontend/src/app/contests/page.tsx @@ -7,7 +7,10 @@ import { ContestCard } from "@/components/domain/contest-card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Skeleton } from "@/components/ui/skeleton"; import { AlertError } from "@/components/ui/alert"; -import { Calendar, Clock, History } from "lucide-react"; +import { Badge } from "@/components/ui/badge"; +import { Calendar, Clock, History, Trophy, Zap, Activity } from "lucide-react"; +import { GlowOrbs, GlitchText } from "@/components/decorative"; +import { EmptyStateIllustration } from "@/components/illustrations"; import type { ContestListItem } from "@/types"; const containerVariants = { @@ -59,10 +62,13 @@ export default function ContestsPage() { if (isLoading) { return (
- -
- {[...Array(3)].map((_, i) => ( - +
+ + +
+
+ {[...Array(6)].map((_, i) => ( + ))}
@@ -78,68 +84,136 @@ export default function ContestsPage() { } return ( -
-
-

Контесты

-
- -
- {activeContests.length} активных - - {contests.length} всего -
-
+
+ {/* Background */} + +
- - - - - Все ({contests.length}) - - - - Активные ({activeContests.length}) - - - - Предстоящие ({upcomingContests.length}) - - - - Прошедшие ({pastContests.length}) - - - - - {filteredContests().length > 0 ? ( +
+ {/* Header */} - {filteredContests().map((contest) => ( - - - - ))} +
+
+ +
+
+

+ +

+

+ $ active_competitions.list() +

+
+
+ + {/* Stats */} +
+ {activeContests.length > 0 && ( +
+ + + {activeContests.length} LIVE + +
+ )} +
+ {contests.length} всего +
+
- ) : ( -
- -

Контестов в этой категории пока нет

-
- )} + + {/* Tabs */} + + + + + Все + + {contests.length} + + + + + Активные + {activeContests.length > 0 && ( + + {activeContests.length} + + )} + + + + Предстоящие + + {upcomingContests.length} + + + + + Прошедшие + + {pastContests.length} + + + + + + {/* Contest Grid */} + {filteredContests().length > 0 ? ( + + {filteredContests().map((contest) => ( + + + + ))} + + ) : ( + + +

+ > Контестов в этой категории пока нет +

+

+ // check back later +

+
+ )} +
); } diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 3ddfd11..5d53232 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -1,84 +1,237 @@ @import "tailwindcss"; @theme { - /* Base colors */ - --color-background: #ffffff; - --color-foreground: #0a0a0a; - --color-primary: #2563eb; - --color-primary-foreground: #ffffff; - --color-secondary: #f1f5f9; - --color-secondary-foreground: #0f172a; - --color-muted: #f1f5f9; - --color-muted-foreground: #64748b; - --color-accent: #f1f5f9; - --color-accent-foreground: #0f172a; - --color-destructive: #ef4444; + /* ======================================== + CYBERPUNK THEME - ВолГУ.Контесты + ======================================== */ + + /* Base colors - Dark theme by default */ + --color-background: #0a0a0f; + --color-foreground: #e4e4e7; + + /* Neon accent colors */ + --color-neon-green: #00ff88; + --color-neon-cyan: #00f5ff; + --color-neon-purple: #bf00ff; + --color-neon-pink: #ff0080; + --color-neon-yellow: #f0ff00; + --color-neon-orange: #ff6b00; + + /* Semantic color mapping */ + --color-primary: #00ff88; + --color-primary-foreground: #0a0a0f; + --color-secondary: #1a1a2e; + --color-secondary-foreground: #e4e4e7; + --color-muted: #1a1a2e; + --color-muted-foreground: #71717a; + --color-accent: #1e1e3f; + --color-accent-foreground: #00f5ff; + --color-destructive: #ff3366; --color-destructive-foreground: #ffffff; - --color-popover: #ffffff; - --color-popover-foreground: #0a0a0a; - --color-card: #ffffff; - --color-card-foreground: #0a0a0a; - --color-border: #e2e8f0; - --color-input: #e2e8f0; - --color-ring: #2563eb; - --radius: 0.5rem; + --color-popover: #0f0f1a; + --color-popover-foreground: #e4e4e7; + --color-card: #0f0f1a; + --color-card-foreground: #e4e4e7; + --color-border: #2a2a4a; + --color-input: #1a1a2e; + --color-ring: #00ff88; + --radius: 0.25rem; - /* Additional semantic colors */ - --color-success: #22c55e; - --color-success-foreground: #ffffff; - --color-warning: #f59e0b; - --color-warning-foreground: #000000; - --color-info: #3b82f6; - --color-info-foreground: #ffffff; + /* Semantic status colors */ + --color-success: #00ff88; + --color-success-foreground: #0a0a0f; + --color-warning: #f0ff00; + --color-warning-foreground: #0a0a0f; + --color-info: #00f5ff; + --color-info-foreground: #0a0a0f; - /* Card shadow */ - --shadow-card: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); - --shadow-card-hover: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + /* Glow effects */ + --glow-green: 0 0 20px rgba(0, 255, 136, 0.5); + --glow-green-intense: 0 0 30px rgba(0, 255, 136, 0.7), 0 0 60px rgba(0, 255, 136, 0.3); + --glow-cyan: 0 0 20px rgba(0, 245, 255, 0.5); + --glow-cyan-intense: 0 0 30px rgba(0, 245, 255, 0.7), 0 0 60px rgba(0, 245, 255, 0.3); + --glow-purple: 0 0 20px rgba(191, 0, 255, 0.5); + --glow-purple-intense: 0 0 30px rgba(191, 0, 255, 0.7), 0 0 60px rgba(191, 0, 255, 0.3); + --glow-pink: 0 0 20px rgba(255, 0, 128, 0.5); + --glow-yellow: 0 0 20px rgba(240, 255, 0, 0.5); + --glow-orange: 0 0 20px rgba(255, 107, 0, 0.5); + + /* Card shadows */ + --shadow-card: 0 4px 20px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.03); + --shadow-card-hover: 0 8px 30px rgba(0, 0, 0, 0.6), 0 0 20px rgba(0, 255, 136, 0.1); + + /* Typography */ + --font-display: 'Orbitron', sans-serif; + --font-mono: 'JetBrains Mono', monospace; } -@media (prefers-color-scheme: dark) { - @theme { - --color-background: #0a0a0a; - --color-foreground: #fafafa; - --color-primary: #3b82f6; - --color-primary-foreground: #ffffff; - --color-secondary: #1e293b; - --color-secondary-foreground: #f8fafc; - --color-muted: #1e293b; - --color-muted-foreground: #94a3b8; - --color-accent: #1e293b; - --color-accent-foreground: #f8fafc; - --color-destructive: #dc2626; - --color-destructive-foreground: #ffffff; - --color-popover: #1c1c1c; - --color-popover-foreground: #fafafa; - --color-card: #1c1c1c; - --color-card-foreground: #fafafa; - --color-border: #334155; - --color-input: #334155; - --color-ring: #3b82f6; - - /* Dark mode semantic colors */ - --color-success: #4ade80; - --color-success-foreground: #000000; - --color-warning: #fbbf24; - --color-warning-foreground: #000000; - --color-info: #60a5fa; - --color-info-foreground: #000000; - - /* Dark mode shadows */ - --shadow-card: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 1px 2px -1px rgb(0 0 0 / 0.3); - --shadow-card-hover: 0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -4px rgb(0 0 0 / 0.3); - } -} +/* ======================================== + BASE STYLES + ======================================== */ body { background: var(--color-background); color: var(--color-foreground); - font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-family: var(--font-mono), system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } -/* Animations */ +/* ======================================== + CYBERPUNK ANIMATIONS + ======================================== */ + +/* Glitch effect */ +@keyframes glitch { + 0%, 100% { + clip-path: inset(0 0 0 0); + transform: translate(0); + } + 20% { + clip-path: inset(20% 0 60% 0); + transform: translate(-2px, 2px); + } + 40% { + clip-path: inset(40% 0 40% 0); + transform: translate(2px, -2px); + } + 60% { + clip-path: inset(60% 0 20% 0); + transform: translate(-1px, 1px); + } + 80% { + clip-path: inset(80% 0 5% 0); + transform: translate(1px, -1px); + } +} + +@keyframes glitch-text { + 0%, 100% { + text-shadow: 2px 0 var(--color-neon-cyan), -2px 0 var(--color-neon-pink); + } + 25% { + text-shadow: -2px 0 var(--color-neon-cyan), 2px 0 var(--color-neon-pink); + } + 50% { + text-shadow: 2px 0 var(--color-neon-pink), -2px 0 var(--color-neon-cyan); + } + 75% { + text-shadow: -2px 0 var(--color-neon-pink), 2px 0 var(--color-neon-cyan); + } +} + +@keyframes glitch-skew { + 0%, 100% { + transform: skew(0deg); + } + 20% { + transform: skew(-2deg); + } + 40% { + transform: skew(2deg); + } + 60% { + transform: skew(-1deg); + } + 80% { + transform: skew(1deg); + } +} + +/* Neon pulse */ +@keyframes neon-pulse { + 0%, 100% { + filter: drop-shadow(0 0 5px currentColor); + opacity: 1; + } + 50% { + filter: drop-shadow(0 0 20px currentColor) drop-shadow(0 0 40px currentColor); + opacity: 0.9; + } +} + +@keyframes neon-border-pulse { + 0%, 100% { + box-shadow: 0 0 5px var(--color-neon-green), inset 0 0 5px rgba(0, 255, 136, 0.1); + border-color: var(--color-neon-green); + } + 50% { + box-shadow: 0 0 20px var(--color-neon-green), 0 0 40px var(--color-neon-green), inset 0 0 10px rgba(0, 255, 136, 0.2); + border-color: var(--color-neon-green); + } +} + +/* Scanline effect */ +@keyframes scanline { + 0% { + transform: translateY(-100%); + } + 100% { + transform: translateY(100vh); + } +} + +/* Blink cursor */ +@keyframes blink-cursor { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0; + } +} + +/* Flicker */ +@keyframes flicker { + 0%, 100% { + opacity: 1; + } + 92% { + opacity: 1; + } + 93% { + opacity: 0.3; + } + 94% { + opacity: 1; + } + 96% { + opacity: 0.5; + } + 97% { + opacity: 1; + } +} + +/* Data stream */ +@keyframes data-stream { + 0% { + background-position: 0% 0%; + } + 100% { + background-position: 0% 100%; + } +} + +/* Float animation */ +@keyframes float { + 0%, 100% { + transform: translateY(0px); + } + 50% { + transform: translateY(-20px); + } +} + +/* Rotate glow */ +@keyframes rotate-glow { + 0% { + filter: hue-rotate(0deg); + } + 100% { + filter: hue-rotate(360deg); + } +} + +/* Preserved animations from original */ @keyframes shimmer { 0% { background-position: -200% 0; @@ -102,13 +255,13 @@ body { @keyframes pulse-ring { 0% { - box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7); + box-shadow: 0 0 0 0 rgba(0, 255, 136, 0.7); } 70% { - box-shadow: 0 0 0 10px rgba(34, 197, 94, 0); + box-shadow: 0 0 0 10px rgba(0, 255, 136, 0); } 100% { - box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); + box-shadow: 0 0 0 0 rgba(0, 255, 136, 0); } } @@ -121,7 +274,54 @@ body { } } -/* Utility classes */ +/* ======================================== + ANIMATION UTILITY CLASSES + ======================================== */ + +.animate-glitch { + animation: glitch 0.3s infinite; +} + +.animate-glitch-text { + animation: glitch-text 2s infinite; +} + +.animate-glitch-skew { + animation: glitch-skew 0.5s infinite; +} + +.animate-neon-pulse { + animation: neon-pulse 2s ease-in-out infinite; +} + +.animate-neon-border-pulse { + animation: neon-border-pulse 2s ease-in-out infinite; +} + +.animate-scanline { + animation: scanline 8s linear infinite; +} + +.animate-blink-cursor { + animation: blink-cursor 1s step-end infinite; +} + +.animate-flicker { + animation: flicker 4s infinite; +} + +.animate-data-stream { + animation: data-stream 20s linear infinite; +} + +.animate-float { + animation: float 6s ease-in-out infinite; +} + +.animate-rotate-glow { + animation: rotate-glow 10s linear infinite; +} + .animate-shimmer { background: linear-gradient( 90deg, @@ -145,15 +345,266 @@ body { animation: spin 1s linear infinite; } -/* Focus visible styles for accessibility */ -.focus-visible-ring:focus-visible { - outline: none; - ring: 2px; - ring-color: var(--color-ring); - ring-offset: 2px; +/* ======================================== + GLOW UTILITY CLASSES + ======================================== */ + +.glow-green { + box-shadow: var(--glow-green); } -/* Scrollbar styling */ +.glow-green-intense { + box-shadow: var(--glow-green-intense); +} + +.glow-cyan { + box-shadow: var(--glow-cyan); +} + +.glow-cyan-intense { + box-shadow: var(--glow-cyan-intense); +} + +.glow-purple { + box-shadow: var(--glow-purple); +} + +.glow-purple-intense { + box-shadow: var(--glow-purple-intense); +} + +.glow-pink { + box-shadow: var(--glow-pink); +} + +.glow-yellow { + box-shadow: var(--glow-yellow); +} + +.glow-orange { + box-shadow: var(--glow-orange); +} + +.text-glow-green { + text-shadow: 0 0 10px var(--color-neon-green), 0 0 20px var(--color-neon-green); +} + +.text-glow-cyan { + text-shadow: 0 0 10px var(--color-neon-cyan), 0 0 20px var(--color-neon-cyan); +} + +.text-glow-purple { + text-shadow: 0 0 10px var(--color-neon-purple), 0 0 20px var(--color-neon-purple); +} + +.text-glow-pink { + text-shadow: 0 0 10px var(--color-neon-pink), 0 0 20px var(--color-neon-pink); +} + +/* ======================================== + CYBER DECORATIVE CLASSES + ======================================== */ + +/* Cyber grid background */ +.cyber-grid { + background-image: + linear-gradient(rgba(0, 255, 136, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 255, 136, 0.03) 1px, transparent 1px); + background-size: 50px 50px; +} + +.cyber-grid-dense { + background-image: + linear-gradient(rgba(0, 255, 136, 0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 255, 136, 0.05) 1px, transparent 1px); + background-size: 20px 20px; +} + +/* Scanline overlay */ +.scanline-overlay { + position: relative; +} + +.scanline-overlay::after { + content: ''; + position: absolute; + inset: 0; + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 0, 0, 0.1) 2px, + rgba(0, 0, 0, 0.1) 4px + ); + pointer-events: none; +} + +/* Corner accent brackets */ +.corner-accent { + position: relative; +} + +.corner-accent::before, +.corner-accent::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + border-color: var(--color-neon-green); + border-style: solid; + border-width: 0; + opacity: 0.5; + transition: opacity 0.3s ease; +} + +.corner-accent::before { + top: 8px; + left: 8px; + border-top-width: 2px; + border-left-width: 2px; +} + +.corner-accent::after { + bottom: 8px; + right: 8px; + border-bottom-width: 2px; + border-right-width: 2px; +} + +.corner-accent:hover::before, +.corner-accent:hover::after { + opacity: 1; +} + +/* All corners variant */ +.corner-accent-all { + position: relative; +} + +.corner-accent-all::before { + content: ''; + position: absolute; + top: 4px; + left: 4px; + right: 4px; + bottom: 4px; + border: 2px solid transparent; + border-image: linear-gradient( + 45deg, + var(--color-neon-green) 0%, + transparent 30%, + transparent 70%, + var(--color-neon-green) 100% + ) 1; + pointer-events: none; +} + +/* Cyber border with gradient */ +.cyber-border { + border: 1px solid var(--color-border); + position: relative; +} + +.cyber-border::before { + content: ''; + position: absolute; + inset: -1px; + background: linear-gradient( + 45deg, + var(--color-neon-green), + transparent, + var(--color-neon-cyan), + transparent, + var(--color-neon-purple) + ); + z-index: -1; + opacity: 0; + transition: opacity 0.3s ease; + border-radius: inherit; +} + +.cyber-border:hover::before { + opacity: 0.5; +} + +/* Neon underline */ +.neon-underline { + position: relative; +} + +.neon-underline::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + width: 0; + height: 2px; + background: var(--color-neon-green); + box-shadow: var(--glow-green); + transition: width 0.3s ease; +} + +.neon-underline:hover::after, +.neon-underline.active::after { + width: 100%; +} + +/* Hexagon clip path */ +.clip-hexagon { + clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%); +} + +/* Diamond clip path */ +.clip-diamond { + clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); +} + +/* Chamfered corners (cut corners) */ +.clip-chamfer { + clip-path: polygon( + 12px 0, + calc(100% - 12px) 0, + 100% 12px, + 100% calc(100% - 12px), + calc(100% - 12px) 100%, + 12px 100%, + 0 calc(100% - 12px), + 0 12px + ); +} + +/* ======================================== + FONT UTILITY CLASSES + ======================================== */ + +.font-display { + font-family: var(--font-display); + letter-spacing: 0.1em; +} + +.font-mono-cyber { + font-family: var(--font-mono); + font-variant-numeric: tabular-nums; +} + +/* Tech heading style */ +.cyber-heading { + font-family: var(--font-display); + text-transform: uppercase; + letter-spacing: 0.15em; +} + +/* Terminal text */ +.terminal-text { + font-family: var(--font-mono); + font-size: 0.875rem; + line-height: 1.5; +} + +/* ======================================== + SCROLLBAR STYLING + ======================================== */ + ::-webkit-scrollbar { width: 8px; height: 8px; @@ -165,25 +616,87 @@ body { } ::-webkit-scrollbar-thumb { - background: var(--color-muted-foreground); + background: var(--color-border); border-radius: 4px; + border: 1px solid var(--color-muted); } ::-webkit-scrollbar-thumb:hover { - background: var(--color-foreground); + background: var(--color-neon-green); + box-shadow: var(--glow-green); } -/* Panel resize handle styling */ +/* ======================================== + PANEL RESIZE HANDLE STYLING + ======================================== */ + [data-panel-resize-handle-enabled] { - transition: background-color 0.2s ease; + transition: all 0.2s ease; } [data-panel-resize-handle-enabled]:hover { - background-color: var(--color-primary) !important; + background-color: var(--color-neon-green) !important; opacity: 0.5; + box-shadow: var(--glow-green); } [data-panel-resize-handle-enabled][data-resize-handle-active] { - background-color: var(--color-primary) !important; + background-color: var(--color-neon-green) !important; opacity: 0.8; + box-shadow: var(--glow-green-intense); +} + +/* ======================================== + FOCUS VISIBLE STYLES + ======================================== */ + +.focus-visible-ring:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--color-background), 0 0 0 4px var(--color-neon-green), var(--glow-green); +} + +/* ======================================== + ACCESSIBILITY - REDUCED MOTION + ======================================== */ + +@media (prefers-reduced-motion: reduce) { + .animate-glitch, + .animate-glitch-text, + .animate-glitch-skew, + .animate-neon-pulse, + .animate-neon-border-pulse, + .animate-scanline, + .animate-blink-cursor, + .animate-flicker, + .animate-data-stream, + .animate-float, + .animate-rotate-glow, + .animate-shimmer, + .animate-shake, + .animate-pulse-ring, + .animate-spin { + animation: none !important; + } + + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} + +/* ======================================== + SELECTION STYLING + ======================================== */ + +::selection { + background: rgba(0, 255, 136, 0.3); + color: var(--color-foreground); +} + +::-moz-selection { + background: rgba(0, 255, 136, 0.3); + color: var(--color-foreground); } diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 73ca7d7..9ebe73a 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,12 +1,27 @@ import type { Metadata } from "next"; +import { JetBrains_Mono, Orbitron } from "next/font/google"; import "./globals.css"; import { Navbar } from "@/components/Navbar"; import { AuthProvider } from "@/lib/auth-context"; import { Toaster } from "sonner"; +// Cyberpunk fonts +const jetbrainsMono = JetBrains_Mono({ + subsets: ["latin", "cyrillic"], + variable: "--font-mono", + display: "swap", +}); + +const orbitron = Orbitron({ + subsets: ["latin"], + variable: "--font-display", + display: "swap", +}); + export const metadata: Metadata = { title: "ВолГУ.Контесты — Соревнования по программированию", - description: "Платформа для проведения соревнований по олимпиадному программированию от Волгоградского государственного университета", + description: + "Платформа для проведения соревнований по олимпиадному программированию от Волгоградского государственного университета", }; export default function RootLayout({ @@ -15,11 +30,17 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - + + - -
{children}
+ {/* Cyber grid background overlay */} +
+ + {/* Main content */} +
+ +
{children}
+
diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx index bafbe42..37d3ec7 100644 --- a/frontend/src/app/login/page.tsx +++ b/frontend/src/app/login/page.tsx @@ -11,7 +11,10 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { AlertError } from "@/components/ui/alert"; -import { LogIn, Mail, Lock, Eye, EyeOff, Trophy } from "lucide-react"; +import { LogIn, Mail, Lock, Eye, EyeOff, Terminal, ArrowRight } from "lucide-react"; +import { GlowOrbs, GlitchText, CornerBrackets } from "@/components/decorative"; +import { AuthIllustration } from "@/components/illustrations"; +import { CyberBrandText, VolguLogo } from "@/components/VolguLogo"; export default function LoginPage() { const [email, setEmail] = useState(""); @@ -39,103 +42,185 @@ export default function LoginPage() { }; return ( -
- - - - - - - Вход -

- Войдите в свой аккаунт для участия в контестах +

+ {/* Background effects */} + +
+ + {/* Decorative elements */} +
+ + {"// authenticating..."} + +
+
+ + {""} + +
+ +
+ {/* Left side - Illustration (hidden on mobile) */} + + +
+

+ $ Безопасный вход в систему

- - -
- {error && ( - - {error} - - )} +
+
-
- -
- - setEmail(e.target.value)} - required - className="pl-10" - placeholder="email@example.com" + {/* Right side - Login form */} + + + {/* Card glow effect */} +
+ + + +
+ + {/* Animated ring */} +
-
+
-
- -
- - setPassword(e.target.value)} - required - className="pl-10 pr-10" - placeholder="••••••••" - /> - -
-
- - - - -
-

- Нет аккаунта?{" "} - - Зарегистрироваться - + + + +

+ > Войдите для участия в контестах

-
- - - + + + +
+ {error && ( + + {error} + + )} + +
+ +
+ + setEmail(e.target.value)} + required + className="pl-10 font-mono" + placeholder="user@example.com" + /> +
+
+ +
+ +
+ + setPassword(e.target.value)} + required + className="pl-10 pr-10 font-mono" + placeholder="••••••••" + /> + +
+
+ + +
+ + {/* Divider */} +
+
+
+
+
+ + или + +
+
+ +
+

+ Нет аккаунта?{" "} + + Зарегистрироваться + +

+
+ + {/* Bottom decoration */} +
+
+ + +
+
+ + + +
); } diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 5172cd2..0f43544 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -4,65 +4,72 @@ import Link from "next/link"; import { motion } from "framer-motion"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; import { useAuth } from "@/lib/auth-context"; import { Trophy, Zap, Code2, Users, - CheckCircle2, ArrowRight, Terminal, Timer, BarChart3, + Cpu, + Shield, + Sparkles, } from "lucide-react"; -import { VolguLogo } from "@/components/VolguLogo"; +import { CyberBrandText, VolguLogo } from "@/components/VolguLogo"; +import { GlowOrbs, GlitchText, CornerBrackets, TypewriterText } from "@/components/decorative"; +import { HeroIllustration } from "@/components/illustrations"; +import { MatrixRain } from "@/components/effects"; const features = [ { icon: Trophy, title: "Соревнования", description: "Участвуйте в контестах и соревнуйтесь с другими программистами", - color: "text-yellow-500", - bgColor: "bg-yellow-500/10", + color: "var(--color-neon-green)", }, { icon: Zap, title: "Автопроверка", description: "Мгновенная проверка решений с детальными результатами по каждому тесту", - color: "text-blue-500", - bgColor: "bg-blue-500/10", + color: "var(--color-neon-cyan)", }, { icon: Code2, title: "30+ языков", description: "Python, C++, Java, JavaScript, Go, Rust и многие другие языки", - color: "text-green-500", - bgColor: "bg-green-500/10", + color: "var(--color-neon-purple)", }, { icon: Timer, title: "Real-time таймеры", description: "Следите за временем контеста и оставшимся временем в реальном времени", - color: "text-orange-500", - bgColor: "bg-orange-500/10", + color: "var(--color-neon-orange)", }, { icon: BarChart3, title: "Рейтинг", description: "Отслеживайте свой прогресс и соревнуйтесь в таблице лидеров", - color: "text-purple-500", - bgColor: "bg-purple-500/10", + color: "var(--color-neon-pink)", }, { icon: Terminal, title: "Удобный редактор", description: "Современный редактор кода с подсветкой синтаксиса и автодополнением", - color: "text-pink-500", - bgColor: "bg-pink-500/10", + color: "var(--color-neon-yellow)", }, ]; +const stats = [ + { value: "30+", label: "Языков", color: "var(--color-neon-green)" }, + { value: "∞", label: "Контестов", color: "var(--color-neon-cyan)" }, + { value: "100%", label: "Автопроверка", color: "var(--color-neon-purple)" }, + { value: "24/7", label: "Доступность", color: "var(--color-neon-pink)" }, +]; + const containerVariants = { hidden: { opacity: 0 }, visible: { @@ -82,109 +89,164 @@ export default function HomePage() { const { user } = useAuth(); return ( -
+
{/* Hero Section */} -
- {/* Background gradient */} -
-
- -
- - - - Волгоградский государственный университет - - -

- ВолГУ - . - Контесты -

- -

- Платформа для проведения соревнований по олимпиадному программированию - от Волгоградского государственного университета. -

- - - - - Перейти к контестам - - - {!user && ( - - - Создать аккаунт - - )} - -
- - {/* Stats */} - - {[ - { value: "30+", label: "Языков программирования" }, - { value: "∞", label: "Контестов" }, - { value: "100%", label: "Автопроверка" }, - { value: "24/7", label: "Доступность" }, - ].map((stat, index) => ( -
-
- {stat.value} -
-
{stat.label}
-
- ))} -
+
+ {/* Background Effects */} +
+
+ + + {/* Cyber grid overlay */} +
+ + {/* Scanline effect */} +
+
+
+ +
+
+ {/* Left column - Text content */} + + {/* University badge */} + + + + + + + + {/* Main heading */} + + + + + {/* Subtitle with terminal style */} + +

+ $ Платформа для проведения соревнований по{" "} + + олимпиадному программированию + +

+

+ > + _ +

+
+ + {/* CTA Buttons */} + + + {!user && ( + + )} + + + {/* Stats */} + + {stats.map((stat, index) => ( +
+ +
+
+ {stat.value} +
+
+ {stat.label} +
+
+
+ ))} +
+
+ + {/* Right column - Illustration */} + + + +
+
+ + {/* Bottom gradient fade */} +
{/* Features Section */} -
-
+
+
+ +
- - Возможности + + + SYSTEM.FEATURES -

- Всё, что нужно для соревнований +

+ < + Возможности платформы + />

-

- Наша платформа предоставляет все необходимые инструменты для проведения - и участия в соревнованиях по программированию +

+ Все необходимые инструменты для проведения и участия в соревнованиях

@@ -199,15 +261,44 @@ export default function HomePage() { const Icon = feature.icon; return ( - - + + + {/* Icon with glow */}
- +
-

{feature.title}

-

{feature.description}

+ + {/* Title */} +

+ {feature.title} +

+ + {/* Description */} +

+ {feature.description} +

+ + {/* Hover effect line */} +
@@ -218,32 +309,76 @@ export default function HomePage() {
{/* CTA Section */} -
+
-
-
- -

- Готовы начать? + {/* Background with cyber styling */} +
+
+ + {/* Border glow */} +
+
+ + {/* Animated corner accents */} +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + {/* Content */} +
+ + + + +

+ Готовы{" "} + начать + ?

-

- Присоединяйтесь к платформе ВолГУ.Контесты и участвуйте - в соревнованиях по олимпиадному программированию. + +

+ Присоединяйтесь к платформе{" "} + >VOLGU.CONTESTS{" "} + и участвуйте в соревнованиях

+
- - {user ? "Перейти к контестам" : "Начать бесплатно"} - - +
@@ -251,9 +386,28 @@ export default function HomePage() {

{/* Footer */} -