From 8d67703d931328b782266e4cab3af418513c2ce6 Mon Sep 17 00:00:00 2001 From: "Amar A.J Bouakaz" Date: Sun, 30 Nov 2025 00:49:04 +0100 Subject: [PATCH 1/4] Refactor CSS styles across multiple components to enhance visual consistency and improve user experience. Updated background colors, borders, and hover effects to align with the new design theme. Adjusted button styles and text colors for better readability and accessibility. Removed inline styles from authentication templates and linked to a centralized CSS file for maintainability. --- src/src/static/css/auth.css | 206 ++++++++++++++++++ src/src/static/css/challenge-details.css | 68 +++--- src/src/static/css/challenge-page.css | 2 +- src/src/static/css/challenges-page.css | 36 +-- src/src/static/css/leaderboard.css | 36 +-- src/src/static/css/shop.css | 38 ++-- src/src/templates/components/base.html | 10 +- .../templates/components/common-styles.html | 98 +++++---- src/src/templates/components/nav.html | 31 ++- .../templates/registration/createTeam.html | 120 +--------- src/src/templates/registration/login.html | 112 +--------- src/src/templates/registration/signup.html | 151 +------------ 12 files changed, 376 insertions(+), 532 deletions(-) create mode 100644 src/src/static/css/auth.css diff --git a/src/src/static/css/auth.css b/src/src/static/css/auth.css new file mode 100644 index 0000000..1ee0244 --- /dev/null +++ b/src/src/static/css/auth.css @@ -0,0 +1,206 @@ +/* Authentication Pages Styles */ + +.auth-container { + min-height: calc(100vh - 50px); + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + margin-top: 50px; +} + +.auth-card { + width: 100%; + max-width: 500px; + background: #1A2332; + border: 1px solid #2C3E50; + border-radius: 12px; + padding: 48px 40px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); +} + +.auth-header { + text-align: center; + margin-bottom: 32px; +} + +.auth-title { + font-size: 28px; + font-weight: 700; + color: #F0E5D8; + margin-bottom: 8px; +} + +.auth-subtitle { + font-size: 14px; + color: #E8EDF2; + line-height: 1.6; +} + +.section-title { + font-size: 16px; + font-weight: 600; + color: #F0E5D8; + margin: 24px 0 16px; + padding-bottom: 8px; + border-bottom: 1px solid #2C3E50; +} + +.section-title:first-of-type { + margin-top: 0; +} + +.form-group { + margin-bottom: 20px; +} + +.form-label { + display: block; + font-size: 13px; + font-weight: 500; + color: #E8EDF2; + margin-bottom: 8px; +} + +.form-input { + width: 100%; + padding: 12px 16px; + background: #0A0E27; + border: 1px solid #2C3E50; + border-radius: 6px; + color: #E8EDF2; + font-size: 14px; + transition: all 0.3s; +} + +.form-input:focus { + outline: none; + border-color: #FF6B35; + background: #1A2332; + box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.1); +} + +.form-input::placeholder { + color: #858585; +} + +.password-requirements { + margin-bottom: 16px; + padding: 12px; + background: #0A0E27; + border: 1px solid #2C3E50; + border-radius: 6px; +} + +.password-requirements p { + font-size: 13px; + color: #E8EDF2; + margin-bottom: 8px; +} + +.password-requirements ul { + list-style: none; + padding: 0; + margin: 0; +} + +.password-requirements li { + font-size: 12px; + color: #858585; + padding: 4px 0; + padding-left: 20px; + position: relative; +} + +.password-requirements li::before { + content: "•"; + position: absolute; + left: 8px; + color: #FF6B35; +} + +.form-footer { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 24px; + flex-wrap: wrap; + gap: 12px; +} + +.form-link { + font-size: 14px; + color: #858585; +} + +.form-link a { + color: #FF6B35; + text-decoration: none; + font-weight: 500; +} + +.form-link a:hover { + text-decoration: underline; + color: #FF8555; +} + +.submit-btn { + padding: 12px 24px; + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); + color: #fff; + border: none; + border-radius: 6px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.3s; +} + +.submit-btn:hover { + background: linear-gradient(135deg, #FF8555 0%, #FF6B35 100%); + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(255, 107, 53, 0.4); +} + +.submit-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} + +.messages { + max-width: 500px; + margin: 0 auto 20px; +} + +.message { + padding: 12px 16px; + border-radius: 6px; + margin-bottom: 12px; + border-left: 4px solid; + font-size: 14px; +} + +.message.error { + background: rgba(229, 62, 62, 0.15); + border-color: #E53E3E; + color: #E53E3E; +} + +.message.success { + background: rgba(72, 187, 120, 0.15); + border-color: #48BB78; + color: #48BB78; +} + +.message.info { + background: rgba(255, 107, 53, 0.15); + border-color: #FF6B35; + color: #FF6B35; +} + +.message.warning { + background: rgba(212, 165, 116, 0.15); + border-color: #D4A574; + color: #D4A574; +} diff --git a/src/src/static/css/challenge-details.css b/src/src/static/css/challenge-details.css index 025f243..67b95b9 100644 --- a/src/src/static/css/challenge-details.css +++ b/src/src/static/css/challenge-details.css @@ -9,16 +9,16 @@ /* Problem Panel */ .problem-panel { width: 50%; - background: #252526; + background: #1A2332; overflow-y: auto; - border-right: 1px solid #3e3e42; + border-right: 1px solid #2C3E50; height: calc(100vh - 50px); } .problem-header { padding: 24px 32px; - border-bottom: 1px solid #3e3e42; - background: #2d2d30; + border-bottom: 1px solid #2C3E50; + background: #0A0E27; } .problem-title { @@ -40,8 +40,8 @@ } .example-box { - background: #1e1e1e; - border: 1px solid #3e3e42; + background: #0A0E27; + border: 1px solid #2C3E50; border-radius: 6px; padding: 16px; margin-bottom: 12px; @@ -66,15 +66,15 @@ width: 50%; display: flex; flex-direction: column; - background: #1e1e1e; + background: #0A0E27; overflow: hidden; height: calc(100vh - 50px); } .editor-header { padding: 16px 24px; - background: #2d2d30; - border-bottom: 1px solid #3e3e42; + background: #1A2332; + border-bottom: 1px solid #2C3E50; display: flex; justify-content: space-between; align-items: center; @@ -88,10 +88,10 @@ .editor-tab { padding: 8px 16px; - background: #1e1e1e; - border: 1px solid #3e3e42; + background: #0A0E27; + border: 1px solid #2C3E50; border-radius: 4px 4px 0 0; - color: #cccccc; + color: #E8EDF2; font-size: 13px; display: flex; align-items: center; @@ -99,9 +99,9 @@ } .editor-tab.active { - background: #1e1e1e; + background: #0A0E27; color: #fff; - border-bottom: 2px solid #0e639c; + border-bottom: 2px solid #FF6B35; } .editor-actions { @@ -124,8 +124,8 @@ .editor-footer { padding: 12px 24px; - background: #2d2d30; - border-top: 1px solid #3e3e42; + background: #1A2332; + border-top: 1px solid #2C3E50; display: flex; justify-content: space-between; align-items: center; @@ -147,8 +147,8 @@ /* Test Results Section */ .test-results-section { - background: #1e1e1e; - border-top: 1px solid #3e3e42; + background: #0A0E27; + border-top: 1px solid #2C3E50; max-height: 200px; overflow-y: auto; flex-shrink: 0; @@ -156,8 +156,8 @@ .test-results-header { padding: 12px 24px; - background: #252526; - border-bottom: 1px solid #3e3e42; + background: #1A2332; + border-bottom: 1px solid #2C3E50; display: flex; justify-content: space-between; align-items: center; @@ -166,7 +166,7 @@ .test-results-title { font-size: 13px; font-weight: 600; - color: #cccccc; + color: #E8EDF2; display: flex; align-items: center; gap: 8px; @@ -183,7 +183,7 @@ .test-case-item { margin-bottom: 4px; - background: #252526; + background: #1A2332; border-left: 3px solid; border-radius: 4px; overflow: hidden; @@ -199,7 +199,7 @@ } .test-case-header:hover { - background: #2d2d30; + background: #2C3E50; } .test-case-header.locked { @@ -207,16 +207,16 @@ } .test-case-item.passed { - border-left-color: #3fb950; + border-left-color: #48BB78; } .test-case-item.failed { - border-left-color: #f85149; + border-left-color: #E53E3E; } .test-case-name { font-size: 13px; - color: #d4d4d4; + color: #E8EDF2; display: flex; align-items: center; gap: 8px; @@ -237,11 +237,11 @@ } .test-case-status.passed { - color: #3fb950; + color: #48BB78; } .test-case-status.failed { - color: #f85149; + color: #E53E3E; } .test-status-icon { @@ -279,7 +279,7 @@ max-height: 0; overflow: hidden; transition: max-height 0.3s ease; - background: #1e1e1e; + background: #0A0E27; } .test-case-details.expanded { @@ -289,7 +289,7 @@ .test-case-content { padding: 12px 16px; - border-top: 1px solid #3e3e42; + border-top: 1px solid #2C3E50; } .test-io-section { @@ -313,18 +313,18 @@ font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 12px; color: #4ec9b0; - background: #252526; + background: #1A2332; padding: 8px 12px; border-radius: 4px; - border: 1px solid #3e3e42; + border: 1px solid #2C3E50; white-space: pre-wrap; word-break: break-all; } /* Loading Spinner */ .spinner { - border: 2px solid #3e3e42; - border-top: 2px solid #58a6ff; + border: 2px solid #2C3E50; + border-top: 2px solid #FF6B35; border-radius: 50%; width: 14px; height: 14px; diff --git a/src/src/static/css/challenge-page.css b/src/src/static/css/challenge-page.css index 008c120..4cffb56 100644 --- a/src/src/static/css/challenge-page.css +++ b/src/src/static/css/challenge-page.css @@ -8,7 +8,7 @@ body { /* background: #fff; */ min-height: 100vh; - background-color: #e7ebee; + background-color: #0A0E27; padding: 10px; flex-direction: column; align-items: center; diff --git a/src/src/static/css/challenges-page.css b/src/src/static/css/challenges-page.css index 706547d..fa3ef8e 100644 --- a/src/src/static/css/challenges-page.css +++ b/src/src/static/css/challenges-page.css @@ -30,9 +30,9 @@ } .level-select { - background: #2d2d30; - color: #cccccc; - border: 1px solid #3e3e42; + background: #1A2332; + color: #E8EDF2; + border: 1px solid #2C3E50; padding: 10px 16px; border-radius: 6px; font-size: 14px; @@ -42,12 +42,12 @@ } .level-select:hover { - border-color: #0e639c; + border-color: #FF6B35; } .level-select:focus { - border-color: #0e639c; - box-shadow: 0 0 0 3px rgba(14, 99, 156, 0.1); + border-color: #FF6B35; + box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.1); } /* Challenge Cards */ @@ -57,8 +57,8 @@ } .challenge-card { - background: #252526; - border: 1px solid #3e3e42; + background: #1A2332; + border: 1px solid #2C3E50; border-radius: 8px; padding: 20px; display: flex; @@ -69,9 +69,9 @@ } .challenge-card:hover { - border-color: #0e639c; + border-color: #FF6B35; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: 0 4px 12px rgba(255, 107, 53, 0.2); } .challenge-info { @@ -119,25 +119,25 @@ /* Button Colors */ button[style*="background-color: #ffa50080"] { - background-color: #d29100 !important; + background-color: #D4A574 !important; } button[style*="background-color: #46e34c"] { - background-color: #238636 !important; + background-color: #48BB78 !important; } button[style*="background-color: #ff4444"] { - background-color: #da3633 !important; + background-color: #E53E3E !important; } .solve-btn:not([style]) { - background: #2d2d30; - color: #cccccc; - border: 1px solid #3e3e42; + background: #1A2332; + color: #E8EDF2; + border: 1px solid #2C3E50; } .solve-btn:not([style]):hover { - background: #3e3e42; + background: #2C3E50; color: #fff; - border-color: #0e639c; + border-color: #FF6B35; } diff --git a/src/src/static/css/leaderboard.css b/src/src/static/css/leaderboard.css index 97fcf63..cdb0a77 100644 --- a/src/src/static/css/leaderboard.css +++ b/src/src/static/css/leaderboard.css @@ -61,7 +61,7 @@ .podium-card { width: 100%; - background: #252526; + background: #1A2332; border: 2px solid; border-radius: 12px; padding: 24px; @@ -77,19 +77,19 @@ /* Rank-specific styling */ .podium-item[data-rank="1"] .podium-card { border-color: #ffd700; - background: linear-gradient(135deg, #252526 0%, #3d3520 100%); + background: linear-gradient(135deg, #1A2332 0%, #3d3520 100%); box-shadow: 0 8px 24px rgba(255, 215, 0, 0.3); } .podium-item[data-rank="2"] .podium-card { border-color: #c0c0c0; - background: linear-gradient(135deg, #252526 0%, #353535 100%); + background: linear-gradient(135deg, #1A2332 0%, #2C3E50 100%); box-shadow: 0 6px 20px rgba(192, 192, 192, 0.2); } .podium-item[data-rank="3"] .podium-card { border-color: #cd7f32; - background: linear-gradient(135deg, #252526 0%, #3d2e20 100%); + background: linear-gradient(135deg, #1A2332 0%, #3d2e20 100%); box-shadow: 0 6px 20px rgba(205, 127, 50, 0.2); } @@ -144,7 +144,7 @@ gap: 8px; margin-top: 16px; padding-top: 16px; - border-top: 1px solid #3e3e42; + border-top: 1px solid #2C3E50; } .total-score { @@ -195,13 +195,13 @@ color: #fff; margin-bottom: 24px; padding-bottom: 12px; - border-bottom: 2px solid #3e3e42; + border-bottom: 2px solid #2C3E50; } .rankings-table { width: 100%; - background: #252526; - border: 1px solid #3e3e42; + background: #1A2332; + border: 1px solid #2C3E50; border-radius: 8px; overflow: hidden; } @@ -212,8 +212,8 @@ } .rankings-table thead { - background: #2d2d30; - border-bottom: 2px solid #3e3e42; + background: #0A0E27; + border-bottom: 2px solid #2C3E50; } .rankings-table th { @@ -221,7 +221,7 @@ text-align: left; font-size: 13px; font-weight: 600; - color: #cccccc; + color: #E8EDF2; text-transform: uppercase; letter-spacing: 0.5px; } @@ -237,12 +237,12 @@ } .rankings-table tbody tr { - border-bottom: 1px solid #3e3e42; + border-bottom: 1px solid #2C3E50; transition: background 0.2s; } .rankings-table tbody tr:hover { - background: #2d2d30; + background: #2C3E50; } .rankings-table tbody tr:last-child { @@ -252,7 +252,7 @@ .rankings-table td { padding: 16px 20px; font-size: 14px; - color: #d4d4d4; + color: #E8EDF2; } .rank-number { @@ -269,7 +269,7 @@ .score-cell { text-align: right; font-weight: 600; - color: #58a6ff; + color: #FF6B35; font-size: 16px; } @@ -306,7 +306,7 @@ left: 0; right: 0; bottom: 0; - background: rgba(30, 30, 30, 0.95); + background: rgba(10, 14, 39, 0.95); backdrop-filter: blur(10px); z-index: 9999; display: flex; @@ -324,7 +324,7 @@ text-align: center; max-width: 600px; padding: 48px; - background: #252526; + background: #1A2332; border: 2px solid #ffd700; border-radius: 16px; box-shadow: 0 0 40px rgba(255, 215, 0, 0.3); @@ -361,7 +361,7 @@ font-size: 14px; color: #858585; padding: 16px; - background: #1e1e1e; + background: #0A0E27; border-radius: 8px; border-left: 3px solid #ffd700; margin-bottom: 24px; diff --git a/src/src/static/css/shop.css b/src/src/static/css/shop.css index fe787c3..00a5bd8 100644 --- a/src/src/static/css/shop.css +++ b/src/src/static/css/shop.css @@ -7,12 +7,12 @@ } .coin-balance { - background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); padding: 2rem; border-radius: 12px; text-align: center; margin-bottom: 2rem; - box-shadow: 0 8px 16px rgba(245, 158, 11, 0.3); + box-shadow: 0 8px 16px rgba(255, 107, 53, 0.3); } .coin-amount { @@ -29,8 +29,8 @@ } .cooldown-notice { - background: #3e3e42; - border-left: 4px solid #f59e0b; + background: #1A2332; + border-left: 4px solid #FF6B35; padding: 1rem 1.5rem; border-radius: 8px; margin-bottom: 2rem; @@ -44,8 +44,8 @@ } .power-card { - background: #252526; - border: 1px solid #3e3e42; + background: #1A2332; + border: 1px solid #2C3E50; border-radius: 12px; padding: 1.5rem; transition: all 0.3s ease; @@ -60,13 +60,13 @@ left: 0; right: 0; height: 4px; - background: linear-gradient(90deg, #0066cc, #0099ff); + background: linear-gradient(90deg, #FF6B35, #FF8555); } .power-card:hover { transform: translateY(-4px); - border-color: #0066cc; - box-shadow: 0 12px 24px rgba(0, 102, 204, 0.2); + border-color: #FF6B35; + box-shadow: 0 12px 24px rgba(255, 107, 53, 0.2); } .power-icon { @@ -76,7 +76,7 @@ display: flex; align-items: center; justify-content: center; - background: linear-gradient(135deg, #0066cc 0%, #0099ff 100%); + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); border-radius: 12px; padding: 10px; } @@ -107,17 +107,17 @@ align-items: center; justify-content: space-between; padding-top: 1rem; - border-top: 1px solid #3e3e42; + border-top: 1px solid #2C3E50; } .cost-amount { font-size: 1.25rem; font-weight: 700; - color: #f59e0b; + color: #FF6B35; } .buy-button { - background: linear-gradient(135deg, #0066cc 0%, #0099ff 100%); + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); color: white; border: none; padding: 0.625rem 1.5rem; @@ -129,11 +129,11 @@ .buy-button:hover:not(:disabled) { transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 102, 204, 0.4); + box-shadow: 0 4px 12px rgba(255, 107, 53, 0.4); } .buy-button:disabled { - background: #3e3e42; + background: #2C3E50; cursor: not-allowed; opacity: 0.5; } @@ -148,12 +148,12 @@ color: white; margin-bottom: 1.5rem; padding-bottom: 0.75rem; - border-bottom: 2px solid #3e3e42; + border-bottom: 2px solid #2C3E50; } .purchase-item { - background: #252526; - border: 1px solid #3e3e42; + background: #1A2332; + border: 1px solid #2C3E50; border-radius: 8px; padding: 1rem 1.5rem; margin-bottom: 0.75rem; @@ -178,7 +178,7 @@ } .purchase-cost { - color: #f59e0b; + color: #FF6B35; font-weight: 600; } diff --git a/src/src/templates/components/base.html b/src/src/templates/components/base.html index 0b4d871..1144153 100644 --- a/src/src/templates/components/base.html +++ b/src/src/templates/components/base.html @@ -16,8 +16,8 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; - background: #1e1e1e; - color: #d4d4d4; + background: #0A0E27; + color: #E8EDF2; min-height: 100vh; } @@ -28,16 +28,16 @@ } ::-webkit-scrollbar-track { - background: #1e1e1e; + background: #0A0E27; } ::-webkit-scrollbar-thumb { - background: #424242; + background: #2C3E50; border-radius: 6px; } ::-webkit-scrollbar-thumb:hover { - background: #4e4e4e; + background: #FF6B35; } diff --git a/src/src/templates/components/common-styles.html b/src/src/templates/components/common-styles.html index e5d8de9..c7cfe0d 100644 --- a/src/src/templates/components/common-styles.html +++ b/src/src/templates/components/common-styles.html @@ -15,60 +15,62 @@ } .level-easy, .level-tag.easy { - background: #2ea04326; - color: #3fb950; - border: 1px solid #3fb950; + background: rgba(72, 187, 120, 0.15); + color: #48BB78; + border: 1px solid #48BB78; } .level-medium, .level-tag.medium { - background: #d2910033; - color: #f0b341; - border: 1px solid #f0b341; + background: rgba(212, 165, 116, 0.15); + color: #D4A574; + border: 1px solid #D4A574; } .level-hard, .level-tag.hard { - background: #da363733; - color: #f85149; - border: 1px solid #f85149; + background: rgba(255, 107, 53, 0.15); + color: #FF6B35; + border: 1px solid #FF6B35; } .meta-points { - background: #1f6feb26; - color: #58a6ff; - border: 1px solid #58a6ff; + background: rgba(255, 107, 53, 0.15); + color: #FF6B35; + border: 1px solid #FF6B35; } .meta-attempts { - background: #8957e526; - color: #bc8cff; - border: 1px solid #bc8cff; + background: rgba(176, 120, 86, 0.15); + color: #D4A574; + border: 1px solid #B07856; } .meta-score { - background: #3fb95026; - color: #56d364; - border: 1px solid #56d364; + background: rgba(72, 187, 120, 0.15); + color: #48BB78; + border: 1px solid #48BB78; } /* Button Styles - Reusable */ .btn { padding: 8px 16px; - border: 1px solid #3e3e42; - border-radius: 4px; - background: #2d2d30; - color: #cccccc; + border: 1px solid #2C3E50; + border-radius: 6px; + background: #1A2332; + color: #E8EDF2; cursor: pointer; font-size: 13px; font-weight: 500; display: flex; align-items: center; gap: 6px; - transition: all 0.2s; + transition: all 0.3s ease; } .btn:hover:not(:disabled) { - background: #3e3e42; + background: #2C3E50; color: #fff; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(255, 107, 53, 0.2); } .btn:disabled { @@ -77,35 +79,37 @@ } .btn-primary { - background: #0e639c; + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); color: #fff; - border-color: #1177bb; + border-color: #FF6B35; } .btn-primary:hover:not(:disabled) { - background: #1177bb; + background: linear-gradient(135deg, #FF8555 0%, #FF6B35 100%); + box-shadow: 0 6px 16px rgba(255, 107, 53, 0.4); } .btn-success { - background: #238636; + background: #48BB78; color: #fff; - border-color: #2ea043; + border-color: #48BB78; } .btn-success:hover:not(:disabled) { - background: #2ea043; + background: #38A169; + box-shadow: 0 6px 16px rgba(72, 187, 120, 0.4); } .btn-danger { - background: #da3633; + background: #E53E3E; color: #fff; - border-color: #f85149; + border-color: #E53E3E; } .btn-warning { - background: #d29100; - color: #fff; - border-color: #f0b341; + background: #D4A574; + color: #0A0E27; + border-color: #B07856; } /* Status Icons */ @@ -115,22 +119,22 @@ border-radius: 50%; } - .status-icon.ready { background: #3fb950; } - .status-icon.processing { background: #f0b341; } - .status-icon.blocked { background: #f85149; } + .status-icon.ready { background: #48BB78; } + .status-icon.processing { background: #D4A574; } + .status-icon.blocked { background: #E53E3E; } /* Card Styles */ .card { - background: #252526; - border: 1px solid #3e3e42; - border-radius: 8px; - transition: all 0.2s; + background: #1A2332; + border: 1px solid #2C3E50; + border-radius: 12px; + transition: all 0.3s ease; } .card:hover { - border-color: #0e639c; + border-color: #FF6B35; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: 0 8px 24px rgba(255, 107, 53, 0.15); } /* Section Styles */ @@ -141,15 +145,15 @@ .section-title { font-size: 18px; font-weight: 600; - color: #fff; + color: #F0E5D8; margin-bottom: 16px; padding-bottom: 8px; - border-bottom: 2px solid #3e3e42; + border-bottom: 2px solid rgba(255, 107, 53, 0.3); } .section-text { line-height: 1.8; - color: #cccccc; + color: #E8EDF2; white-space: pre-wrap; } diff --git a/src/src/templates/components/nav.html b/src/src/templates/components/nav.html index b6633f0..676140e 100644 --- a/src/src/templates/components/nav.html +++ b/src/src/templates/components/nav.html @@ -7,7 +7,7 @@ {% if request.user.is_authenticated %} -
+
{{ request.user.participant.team.name }} | {{ request.user.username }} @@ -33,8 +33,8 @@ justify-content: space-between; align-items: center; padding: 1rem 2rem; - background: white; - border-bottom: 1px solid #eee; + background: #1A2332; + border-bottom: 1px solid #2C3E50; border-radius: 20px; position: sticky; height: 63px; @@ -45,7 +45,7 @@ .logo { font-weight: bold; font-size: 1.5rem; - color: #333; + color: #F0E5D8; text-decoration: none; } @@ -57,8 +57,13 @@ .nav-links a { text-decoration: none; - color: #666; + color: #E8EDF2; font-weight: 500; + transition: color 0.3s; + } + + .nav-links a:hover { + color: #FF6B35; } /* Hamburger button styling */ .hamburger { @@ -68,33 +73,35 @@ } .hamburger span { - background-color: black; + background-color: #E8EDF2; height: 3px; width: 25px; margin: 3px 0; } .sign-in { - background: #1a73e8; + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); color: white !important; padding: 0.5rem 1.25rem; border-radius: 4px; - transition: background 0.3s; + transition: all 0.3s; } .sign-in:hover { - background: #1557b0; + background: linear-gradient(135deg, #FF8555 0%, #FF6B35 100%); + box-shadow: 0 4px 12px rgba(255, 107, 53, 0.4); } .logout { - background: #d41d1d; + background: #E53E3E; color: white !important; padding: 0.5rem 1.25rem; border-radius: 4px; - transition: background 0.3s; + transition: all 0.3s; } .logout:hover { - background: #871818; + background: #C53030; + box-shadow: 0 4px 12px rgba(229, 62, 62, 0.4); } \ No newline at end of file diff --git a/src/src/templates/registration/createTeam.html b/src/src/templates/registration/createTeam.html index d746cdf..d4ddc6e 100644 --- a/src/src/templates/registration/createTeam.html +++ b/src/src/templates/registration/createTeam.html @@ -4,118 +4,7 @@ {% block title %}Create Team - Solve It{% endblock %} {% block extra_css %} - + {% endblock %} {% block content %} @@ -147,6 +36,7 @@

Create a Team

-{% endblock %} - - - - \ No newline at end of file +{% endblock %} \ No newline at end of file diff --git a/src/src/templates/registration/login.html b/src/src/templates/registration/login.html index ecc4dad..7aa78fa 100644 --- a/src/src/templates/registration/login.html +++ b/src/src/templates/registration/login.html @@ -4,117 +4,7 @@ {% block title %}Login - Solve It{% endblock %} {% block extra_css %} - + {% endblock %} {% block content %} diff --git a/src/src/templates/registration/signup.html b/src/src/templates/registration/signup.html index d565dd8..024b09e 100644 --- a/src/src/templates/registration/signup.html +++ b/src/src/templates/registration/signup.html @@ -4,156 +4,7 @@ {% block title %}Join Team - Solve It{% endblock %} {% block extra_css %} - + {% endblock %} {% block content %} From 484ceb2bb98c220bda92c133364dbfc8a457d08e Mon Sep 17 00:00:00 2001 From: "Amar A.J Bouakaz" Date: Sun, 30 Nov 2025 11:51:26 +0100 Subject: [PATCH 2/4] Add styles and scripts for authentication, challenge, leaderboard, and shop pages - Created new CSS files for authentication pages, challenge details, challenges page, leaderboard, and shop page with responsive designs. - Updated existing challenge page styles to match the new theme. - Implemented JavaScript for challenge details and challenges page to handle code submission and filtering challenges. - Modified Kafka consumer service to use app settings for determining submission status based on score. --- src/src/templates/components/nav.html | 65 +++- src/src/templates/home/home-page.html | 201 +++++++--- src/staticfiles/css/auth.css | 206 ++++++++++ src/staticfiles/css/challenge-details.css | 349 +++++++++++++++++ src/staticfiles/css/challenge-page.css | 2 +- src/staticfiles/css/challenges-page.css | 143 +++++++ src/staticfiles/css/leaderboard.css | 437 ++++++++++++++++++++++ src/staticfiles/css/shop.css | 189 ++++++++++ src/staticfiles/js/challenge-details.js | 111 ++++++ src/staticfiles/js/challenges-page.js | 23 ++ src/tasks/kafka_consumer.py | 4 +- 11 files changed, 1667 insertions(+), 63 deletions(-) create mode 100644 src/staticfiles/css/auth.css create mode 100644 src/staticfiles/css/challenge-details.css create mode 100644 src/staticfiles/css/challenges-page.css create mode 100644 src/staticfiles/css/leaderboard.css create mode 100644 src/staticfiles/css/shop.css create mode 100644 src/staticfiles/js/challenge-details.js create mode 100644 src/staticfiles/js/challenges-page.js diff --git a/src/src/templates/components/nav.html b/src/src/templates/components/nav.html index 676140e..718bdb5 100644 --- a/src/src/templates/components/nav.html +++ b/src/src/templates/components/nav.html @@ -33,13 +33,16 @@ justify-content: space-between; align-items: center; padding: 1rem 2rem; - background: #1A2332; - border-bottom: 1px solid #2C3E50; + background: linear-gradient(135deg, #0A0E27 0%, #1A2332 100%); + border-bottom: 2px solid rgba(255, 107, 53, 0.3); border-radius: 20px; position: sticky; height: 63px; width: 100%; top: 0; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4); + backdrop-filter: blur(10px); + z-index: 1000; } .logo { @@ -47,6 +50,18 @@ font-size: 1.5rem; color: #F0E5D8; text-decoration: none; + display: flex; + align-items: center; + gap: 0.5rem; + transition: transform 0.3s; + } + + .logo:hover { + transform: scale(1.05); + } + + .logo img { + filter: drop-shadow(0 0 8px rgba(255, 107, 53, 0.3)); } .nav-links { @@ -59,12 +74,30 @@ text-decoration: none; color: #E8EDF2; font-weight: 500; - transition: color 0.3s; + transition: all 0.3s; + position: relative; + padding: 0.25rem 0; + } + + .nav-links a:not(.sign-in):not(.logout)::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: linear-gradient(90deg, #FF6B35, #FF8555); + transition: width 0.3s; + } + + .nav-links a:not(.sign-in):not(.logout):hover::after { + width: 100%; } - .nav-links a:hover { + .nav-links a:not(.sign-in):not(.logout):hover { color: #FF6B35; } + /* Hamburger button styling */ .hamburger { display: none; @@ -83,25 +116,41 @@ background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); color: white !important; padding: 0.5rem 1.25rem; - border-radius: 4px; + border-radius: 6px; transition: all 0.3s; + box-shadow: 0 2px 8px rgba(255, 107, 53, 0.3); + font-weight: 600; } .sign-in:hover { background: linear-gradient(135deg, #FF8555 0%, #FF6B35 100%); - box-shadow: 0 4px 12px rgba(255, 107, 53, 0.4); + box-shadow: 0 4px 16px rgba(255, 107, 53, 0.5); + transform: translateY(-2px); } .logout { background: #E53E3E; color: white !important; padding: 0.5rem 1.25rem; - border-radius: 4px; + border-radius: 6px; transition: all 0.3s; + box-shadow: 0 2px 8px rgba(229, 62, 62, 0.3); + font-weight: 600; } .logout:hover { background: #C53030; - box-shadow: 0 4px 12px rgba(229, 62, 62, 0.4); + box-shadow: 0 4px 16px rgba(229, 62, 62, 0.5); + transform: translateY(-2px); + } + + @media (max-width: 768px) { + .navbar { + padding: 1rem; + } + + .nav-links { + gap: 1rem; + } } \ No newline at end of file diff --git a/src/src/templates/home/home-page.html b/src/src/templates/home/home-page.html index 03aee8b..4ab1f18 100644 --- a/src/src/templates/home/home-page.html +++ b/src/src/templates/home/home-page.html @@ -9,25 +9,34 @@ .hero { text-align: center; padding: 120px 24px 80px; - background: linear-gradient(135deg, #1e1e1e 0%, #2d2d30 100%); + background: linear-gradient(135deg, #0A0E27 0%, #1A2332 100%); + border-radius: 12px; + margin-bottom: 40px; } .hero img { - height: 120px; + height: 140px; margin-bottom: 32px; - filter: drop-shadow(0 0 20px rgba(88, 166, 255, 0.3)); + filter: drop-shadow(0 0 30px rgba(255, 107, 53, 0.4)); + animation: float 3s ease-in-out infinite; + } + + @keyframes float { + 0%, 100% { transform: translateY(0px); } + 50% { transform: translateY(-10px); } } .hero h1 { - font-size: 48px; + font-size: 56px; font-weight: 700; - color: #fff; + color: #F0E5D8; margin-bottom: 16px; + text-shadow: 0 4px 12px rgba(255, 107, 53, 0.3); } .hero p { font-size: 20px; - color: #858585; + color: #E8EDF2; max-width: 600px; margin: 0 auto 40px; line-height: 1.6; @@ -42,63 +51,77 @@ .button { padding: 14px 32px; - border-radius: 6px; + border-radius: 8px; font-size: 16px; - font-weight: 500; + font-weight: 600; text-decoration: none; - transition: all 0.2s; + transition: all 0.3s; display: inline-block; } .primary-button { - background: #0e639c; + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); color: #fff; - border: 1px solid #1177bb; + border: none; + box-shadow: 0 4px 12px rgba(255, 107, 53, 0.3); } .primary-button:hover { - background: #1177bb; + background: linear-gradient(135deg, #FF8555 0%, #FF6B35 100%); transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(14, 99, 156, 0.4); + box-shadow: 0 6px 20px rgba(255, 107, 53, 0.5); } .secondary-button { background: transparent; - color: #58a6ff; - border: 1px solid #58a6ff; + color: #FF6B35; + border: 2px solid #FF6B35; } .secondary-button:hover { - background: #58a6ff; + background: #FF6B35; color: #fff; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(88, 166, 255, 0.4); + box-shadow: 0 6px 20px rgba(255, 107, 53, 0.4); } /* Content Section */ .content-section { max-width: 900px; - margin: 0 auto; - padding: 60px 24px; - background: #252526; - border-radius: 8px; - margin-top: -40px; - margin-bottom: 60px; - border: 1px solid #3e3e42; + margin: 0 auto 60px; + padding: 60px 40px; + background: #1A2332; + border-radius: 16px; + border: 1px solid #2C3E50; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); } .content-section h2 { - font-size: 32px; - font-weight: 600; - color: #fff; + font-size: 36px; + font-weight: 700; + color: #F0E5D8; margin-bottom: 24px; text-align: center; + position: relative; + padding-bottom: 16px; + } + + .content-section h2::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 80px; + height: 4px; + background: linear-gradient(90deg, #FF6B35, #FF8555); + border-radius: 2px; } .content-section p { font-size: 16px; line-height: 1.8; - color: #cccccc; + color: #E8EDF2; text-align: center; } @@ -113,35 +136,59 @@ } .feature-card { - background: #252526; - border: 1px solid #3e3e42; - border-radius: 8px; - padding: 32px; + background: #1A2332; + border: 1px solid #2C3E50; + border-radius: 16px; + padding: 40px 32px; text-align: center; - transition: all 0.2s; + transition: all 0.3s; + position: relative; + overflow: hidden; + } + + .feature-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #FF6B35, #FF8555); + transform: scaleX(0); + transition: transform 0.3s; + } + + .feature-card:hover::before { + transform: scaleX(1); } .feature-card:hover { - border-color: #0e639c; - transform: translateY(-4px); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3); + border-color: #FF6B35; + transform: translateY(-8px); + box-shadow: 0 12px 32px rgba(255, 107, 53, 0.3); } .feature-icon { - font-size: 48px; - margin-bottom: 16px; + font-size: 56px; + margin-bottom: 20px; + display: inline-block; + transition: transform 0.3s; + } + + .feature-card:hover .feature-icon { + transform: scale(1.1); } .feature-card h3 { - font-size: 20px; + font-size: 22px; font-weight: 600; - color: #fff; + color: #F0E5D8; margin-bottom: 12px; } .feature-card p { font-size: 14px; - color: #858585; + color: #E8EDF2; line-height: 1.6; } @@ -153,28 +200,78 @@ } .message { - padding: 12px 16px; - border-radius: 6px; + padding: 14px 18px; + border-radius: 8px; margin-bottom: 12px; border-left: 4px solid; + font-size: 14px; + animation: slideIn 0.3s ease-out; + } + + @keyframes slideIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } } .message.error { - background: #da363726; - border-color: #f85149; - color: #f85149; + background: rgba(229, 62, 62, 0.15); + border-color: #E53E3E; + color: #E53E3E; } .message.success { - background: #2ea04326; - border-color: #3fb950; - color: #3fb950; + background: rgba(72, 187, 120, 0.15); + border-color: #48BB78; + color: #48BB78; } .message.info { - background: #1f6feb26; - border-color: #58a6ff; - color: #58a6ff; + background: rgba(255, 107, 53, 0.15); + border-color: #FF6B35; + color: #FF6B35; + } + + /* Responsive */ + @media (max-width: 768px) { + .hero { + padding: 80px 24px 60px; + } + + .hero h1 { + font-size: 40px; + } + + .hero p { + font-size: 16px; + } + + .hero img { + height: 100px; + } + + .content-section { + padding: 40px 24px; + } + + .content-section h2 { + font-size: 28px; + } + + .features { + grid-template-columns: 1fr; + gap: 16px; + } + + .button { + width: 100%; + max-width: 300px; + } } {% endblock %} diff --git a/src/staticfiles/css/auth.css b/src/staticfiles/css/auth.css new file mode 100644 index 0000000..1ee0244 --- /dev/null +++ b/src/staticfiles/css/auth.css @@ -0,0 +1,206 @@ +/* Authentication Pages Styles */ + +.auth-container { + min-height: calc(100vh - 50px); + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + margin-top: 50px; +} + +.auth-card { + width: 100%; + max-width: 500px; + background: #1A2332; + border: 1px solid #2C3E50; + border-radius: 12px; + padding: 48px 40px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); +} + +.auth-header { + text-align: center; + margin-bottom: 32px; +} + +.auth-title { + font-size: 28px; + font-weight: 700; + color: #F0E5D8; + margin-bottom: 8px; +} + +.auth-subtitle { + font-size: 14px; + color: #E8EDF2; + line-height: 1.6; +} + +.section-title { + font-size: 16px; + font-weight: 600; + color: #F0E5D8; + margin: 24px 0 16px; + padding-bottom: 8px; + border-bottom: 1px solid #2C3E50; +} + +.section-title:first-of-type { + margin-top: 0; +} + +.form-group { + margin-bottom: 20px; +} + +.form-label { + display: block; + font-size: 13px; + font-weight: 500; + color: #E8EDF2; + margin-bottom: 8px; +} + +.form-input { + width: 100%; + padding: 12px 16px; + background: #0A0E27; + border: 1px solid #2C3E50; + border-radius: 6px; + color: #E8EDF2; + font-size: 14px; + transition: all 0.3s; +} + +.form-input:focus { + outline: none; + border-color: #FF6B35; + background: #1A2332; + box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.1); +} + +.form-input::placeholder { + color: #858585; +} + +.password-requirements { + margin-bottom: 16px; + padding: 12px; + background: #0A0E27; + border: 1px solid #2C3E50; + border-radius: 6px; +} + +.password-requirements p { + font-size: 13px; + color: #E8EDF2; + margin-bottom: 8px; +} + +.password-requirements ul { + list-style: none; + padding: 0; + margin: 0; +} + +.password-requirements li { + font-size: 12px; + color: #858585; + padding: 4px 0; + padding-left: 20px; + position: relative; +} + +.password-requirements li::before { + content: "•"; + position: absolute; + left: 8px; + color: #FF6B35; +} + +.form-footer { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 24px; + flex-wrap: wrap; + gap: 12px; +} + +.form-link { + font-size: 14px; + color: #858585; +} + +.form-link a { + color: #FF6B35; + text-decoration: none; + font-weight: 500; +} + +.form-link a:hover { + text-decoration: underline; + color: #FF8555; +} + +.submit-btn { + padding: 12px 24px; + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); + color: #fff; + border: none; + border-radius: 6px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.3s; +} + +.submit-btn:hover { + background: linear-gradient(135deg, #FF8555 0%, #FF6B35 100%); + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(255, 107, 53, 0.4); +} + +.submit-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} + +.messages { + max-width: 500px; + margin: 0 auto 20px; +} + +.message { + padding: 12px 16px; + border-radius: 6px; + margin-bottom: 12px; + border-left: 4px solid; + font-size: 14px; +} + +.message.error { + background: rgba(229, 62, 62, 0.15); + border-color: #E53E3E; + color: #E53E3E; +} + +.message.success { + background: rgba(72, 187, 120, 0.15); + border-color: #48BB78; + color: #48BB78; +} + +.message.info { + background: rgba(255, 107, 53, 0.15); + border-color: #FF6B35; + color: #FF6B35; +} + +.message.warning { + background: rgba(212, 165, 116, 0.15); + border-color: #D4A574; + color: #D4A574; +} diff --git a/src/staticfiles/css/challenge-details.css b/src/staticfiles/css/challenge-details.css new file mode 100644 index 0000000..67b95b9 --- /dev/null +++ b/src/staticfiles/css/challenge-details.css @@ -0,0 +1,349 @@ +/* Challenge Details Page Specific Styles */ +.challenge-container { + display: flex; + height: 100vh; + padding-top: 50px; + overflow: hidden; +} + +/* Problem Panel */ +.problem-panel { + width: 50%; + background: #1A2332; + overflow-y: auto; + border-right: 1px solid #2C3E50; + height: calc(100vh - 50px); +} + +.problem-header { + padding: 24px 32px; + border-bottom: 1px solid #2C3E50; + background: #0A0E27; +} + +.problem-title { + font-size: 24px; + font-weight: 600; + color: #fff; + margin-bottom: 12px; +} + +.problem-meta { + display: flex; + gap: 12px; + flex-wrap: wrap; + align-items: center; +} + +.problem-content { + padding: 32px; +} + +.example-box { + background: #0A0E27; + border: 1px solid #2C3E50; + border-radius: 6px; + padding: 16px; + margin-bottom: 12px; +} + +.example-label { + color: #858585; + font-size: 12px; + text-transform: uppercase; + font-weight: 600; + margin-bottom: 8px; +} + +.example-value { + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + color: #4ec9b0; + font-size: 14px; +} + +/* Editor Panel */ +.editor-panel { + width: 50%; + display: flex; + flex-direction: column; + background: #0A0E27; + overflow: hidden; + height: calc(100vh - 50px); +} + +.editor-header { + padding: 16px 24px; + background: #1A2332; + border-bottom: 1px solid #2C3E50; + display: flex; + justify-content: space-between; + align-items: center; + flex-shrink: 0; +} + +.editor-tabs { + display: flex; + gap: 8px; +} + +.editor-tab { + padding: 8px 16px; + background: #0A0E27; + border: 1px solid #2C3E50; + border-radius: 4px 4px 0 0; + color: #E8EDF2; + font-size: 13px; + display: flex; + align-items: center; + gap: 6px; +} + +.editor-tab.active { + background: #0A0E27; + color: #fff; + border-bottom: 2px solid #FF6B35; +} + +.editor-actions { + display: flex; + gap: 8px; +} + +.editor-body { + flex: 1; + position: relative; + overflow: hidden; + min-height: 0; +} + +.CodeMirror { + height: 100% !important; + font-size: 14px; + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; +} + +.editor-footer { + padding: 12px 24px; + background: #1A2332; + border-top: 1px solid #2C3E50; + display: flex; + justify-content: space-between; + align-items: center; + flex-shrink: 0; +} + +.status-info { + display: flex; + gap: 16px; + font-size: 13px; + color: #858585; +} + +.status-item { + display: flex; + align-items: center; + gap: 6px; +} + +/* Test Results Section */ +.test-results-section { + background: #0A0E27; + border-top: 1px solid #2C3E50; + max-height: 200px; + overflow-y: auto; + flex-shrink: 0; +} + +.test-results-header { + padding: 12px 24px; + background: #1A2332; + border-bottom: 1px solid #2C3E50; + display: flex; + justify-content: space-between; + align-items: center; +} + +.test-results-title { + font-size: 13px; + font-weight: 600; + color: #E8EDF2; + display: flex; + align-items: center; + gap: 8px; +} + +.test-results-summary { + font-size: 12px; + color: #858585; +} + +.test-results-list { + padding: 8px; +} + +.test-case-item { + margin-bottom: 4px; + background: #1A2332; + border-left: 3px solid; + border-radius: 4px; + overflow: hidden; +} + +.test-case-header { + padding: 10px 16px; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + transition: background 0.2s; +} + +.test-case-header:hover { + background: #2C3E50; +} + +.test-case-header.locked { + cursor: default; +} + +.test-case-item.passed { + border-left-color: #48BB78; +} + +.test-case-item.failed { + border-left-color: #E53E3E; +} + +.test-case-name { + font-size: 13px; + color: #E8EDF2; + display: flex; + align-items: center; + gap: 8px; +} + +.test-case-right { + display: flex; + align-items: center; + gap: 12px; +} + +.test-case-status { + font-size: 12px; + font-weight: 600; + display: flex; + align-items: center; + gap: 6px; +} + +.test-case-status.passed { + color: #48BB78; +} + +.test-case-status.failed { + color: #E53E3E; +} + +.test-status-icon { + font-size: 16px; +} + +.accordion-icon { + color: #858585; + transition: transform 0.3s; + display: flex; + align-items: center; +} + +.accordion-icon svg { + width: 14px; + height: 14px; +} + +.accordion-icon.expanded { + transform: rotate(90deg); +} + +.lock-icon { + color: #858585; + display: flex; + align-items: center; +} + +.lock-icon svg { + width: 14px; + height: 14px; +} + +.test-case-details { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease; + background: #0A0E27; +} + +.test-case-details.expanded { + max-height: 300px; + overflow-y: auto; +} + +.test-case-content { + padding: 12px 16px; + border-top: 1px solid #2C3E50; +} + +.test-io-section { + margin-bottom: 12px; +} + +.test-io-section:last-child { + margin-bottom: 0; +} + +.test-io-label { + font-size: 11px; + text-transform: uppercase; + color: #858585; + font-weight: 600; + margin-bottom: 6px; + letter-spacing: 0.5px; +} + +.test-io-value { + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + font-size: 12px; + color: #4ec9b0; + background: #1A2332; + padding: 8px 12px; + border-radius: 4px; + border: 1px solid #2C3E50; + white-space: pre-wrap; + word-break: break-all; +} + +/* Loading Spinner */ +.spinner { + border: 2px solid #2C3E50; + border-top: 2px solid #FF6B35; + border-radius: 50%; + width: 14px; + height: 14px; + animation: spin 0.8s linear infinite; + display: inline-block; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Responsive */ +@media (max-width: 1024px) { + .challenge-container { + flex-direction: column; + } + .problem-panel, .editor-panel { + width: 100%; + height: 50vh; + } +} diff --git a/src/staticfiles/css/challenge-page.css b/src/staticfiles/css/challenge-page.css index 008c120..4cffb56 100644 --- a/src/staticfiles/css/challenge-page.css +++ b/src/staticfiles/css/challenge-page.css @@ -8,7 +8,7 @@ body { /* background: #fff; */ min-height: 100vh; - background-color: #e7ebee; + background-color: #0A0E27; padding: 10px; flex-direction: column; align-items: center; diff --git a/src/staticfiles/css/challenges-page.css b/src/staticfiles/css/challenges-page.css new file mode 100644 index 0000000..fa3ef8e --- /dev/null +++ b/src/staticfiles/css/challenges-page.css @@ -0,0 +1,143 @@ +/* Challenges Page Specific Styles */ +.container { + max-width: 1400px; + margin: 0 auto; + padding: 80px 24px 24px; +} + +.page-header { + margin-bottom: 32px; +} + +.page-header h1 { + font-size: 32px; + font-weight: 600; + color: #fff; + margin-bottom: 8px; +} + +.page-header p { + color: #858585; + font-size: 16px; +} + +/* Filter Section */ +.level-filter { + margin-bottom: 24px; + display: flex; + gap: 12px; + align-items: center; +} + +.level-select { + background: #1A2332; + color: #E8EDF2; + border: 1px solid #2C3E50; + padding: 10px 16px; + border-radius: 6px; + font-size: 14px; + cursor: pointer; + outline: none; + transition: all 0.2s; +} + +.level-select:hover { + border-color: #FF6B35; +} + +.level-select:focus { + border-color: #FF6B35; + box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.1); +} + +/* Challenge Cards */ +.challenge-list { + display: grid; + gap: 16px; +} + +.challenge-card { + background: #1A2332; + border: 1px solid #2C3E50; + border-radius: 8px; + padding: 20px; + display: flex; + justify-content: space-between; + align-items: center; + transition: all 0.2s; + cursor: pointer; +} + +.challenge-card:hover { + border-color: #FF6B35; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(255, 107, 53, 0.2); +} + +.challenge-info { + display: flex; + gap: 16px; + align-items: center; + flex: 1; +} + +.challenge-details h3 { + font-size: 18px; + font-weight: 600; + color: #fff; + margin-bottom: 8px; +} + +.challenge-meta { + display: flex; + gap: 12px; + flex-wrap: wrap; + align-items: center; + font-size: 13px; +} + +.challenge-meta span { + color: #858585; +} + +/* Action Buttons */ +.solve-btn { + padding: 10px 20px; + border: none; + border-radius: 6px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; +} + +.solve-btn:hover { + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); +} + +/* Button Colors */ +button[style*="background-color: #ffa50080"] { + background-color: #D4A574 !important; +} + +button[style*="background-color: #46e34c"] { + background-color: #48BB78 !important; +} + +button[style*="background-color: #ff4444"] { + background-color: #E53E3E !important; +} + +.solve-btn:not([style]) { + background: #1A2332; + color: #E8EDF2; + border: 1px solid #2C3E50; +} + +.solve-btn:not([style]):hover { + background: #2C3E50; + color: #fff; + border-color: #FF6B35; +} diff --git a/src/staticfiles/css/leaderboard.css b/src/staticfiles/css/leaderboard.css new file mode 100644 index 0000000..cdb0a77 --- /dev/null +++ b/src/staticfiles/css/leaderboard.css @@ -0,0 +1,437 @@ +/* Leaderboard Page Styles */ + +.leaderboard-container { + margin-top: 50px; + padding: 40px 24px; + max-width: 1400px; + margin-left: auto; + margin-right: auto; +} + +.page-header { + text-align: center; + margin-bottom: 48px; +} + +.page-title { + font-size: 36px; + font-weight: 700; + color: #fff; + margin-bottom: 12px; +} + +.page-subtitle { + font-size: 16px; + color: #858585; +} + +/* Podium Section */ +.podium-section { + margin-bottom: 48px; +} + +.podium-container { + display: flex; + align-items: flex-end; + justify-content: center; + gap: 24px; + margin-bottom: 32px; + flex-wrap: wrap; +} + +.podium-item { + flex: 0 1 320px; + display: flex; + flex-direction: column; + align-items: center; +} + +/* Reorder: 2nd, 1st, 3rd */ +.podium-item[data-rank="1"] { + order: 2; +} + +.podium-item[data-rank="2"] { + order: 1; +} + +.podium-item[data-rank="3"] { + order: 3; +} + +.podium-card { + width: 100%; + background: #1A2332; + border: 2px solid; + border-radius: 12px; + padding: 24px; + text-align: center; + position: relative; + transition: transform 0.3s, box-shadow 0.3s; +} + +.podium-card:hover { + transform: translateY(-4px); +} + +/* Rank-specific styling */ +.podium-item[data-rank="1"] .podium-card { + border-color: #ffd700; + background: linear-gradient(135deg, #1A2332 0%, #3d3520 100%); + box-shadow: 0 8px 24px rgba(255, 215, 0, 0.3); +} + +.podium-item[data-rank="2"] .podium-card { + border-color: #c0c0c0; + background: linear-gradient(135deg, #1A2332 0%, #2C3E50 100%); + box-shadow: 0 6px 20px rgba(192, 192, 192, 0.2); +} + +.podium-item[data-rank="3"] .podium-card { + border-color: #cd7f32; + background: linear-gradient(135deg, #1A2332 0%, #3d2e20 100%); + box-shadow: 0 6px 20px rgba(205, 127, 50, 0.2); +} + +.rank-badge { + display: inline-flex; + align-items: center; + justify-content: center; + width: 60px; + height: 60px; + border-radius: 50%; + font-size: 28px; + font-weight: 700; + margin-bottom: 16px; + border: 3px solid; +} + +.podium-item[data-rank="1"] .rank-badge { + background: linear-gradient(135deg, #ffd700, #ffed4e); + color: #1e1e1e; + border-color: #ffed4e; + box-shadow: 0 4px 12px rgba(255, 215, 0, 0.5); +} + +.podium-item[data-rank="2"] .rank-badge { + background: linear-gradient(135deg, #c0c0c0, #e8e8e8); + color: #1e1e1e; + border-color: #e8e8e8; + box-shadow: 0 4px 12px rgba(192, 192, 192, 0.4); +} + +.podium-item[data-rank="3"] .rank-badge { + background: linear-gradient(135deg, #cd7f32, #e89b5a); + color: #1e1e1e; + border-color: #e89b5a; + box-shadow: 0 4px 12px rgba(205, 127, 50, 0.4); +} + +.team-name { + font-size: 24px; + font-weight: 600; + color: #fff; + margin-bottom: 16px; +} + +.podium-item[data-rank="1"] .team-name { + color: #ffd700; +} + +.score-display { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 16px; + padding-top: 16px; + border-top: 1px solid #2C3E50; +} + +.total-score { + font-size: 32px; + font-weight: 700; + color: #fff; +} + +.podium-item[data-rank="1"] .total-score { + color: #ffd700; +} + +.podium-item[data-rank="2"] .total-score { + color: #c0c0c0; +} + +.podium-item[data-rank="3"] .total-score { + color: #cd7f32; +} + +.score-label { + font-size: 13px; + color: #858585; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.tasks-completed { + font-size: 14px; + color: #cccccc; + margin-top: 8px; +} + +/* Trophy Icons */ +.trophy-icon { + font-size: 48px; + margin-bottom: 8px; +} + +/* Regular Rankings Table */ +.rankings-section { + margin-top: 48px; +} + +.section-title { + font-size: 24px; + font-weight: 600; + color: #fff; + margin-bottom: 24px; + padding-bottom: 12px; + border-bottom: 2px solid #2C3E50; +} + +.rankings-table { + width: 100%; + background: #1A2332; + border: 1px solid #2C3E50; + border-radius: 8px; + overflow: hidden; +} + +.rankings-table table { + width: 100%; + border-collapse: collapse; +} + +.rankings-table thead { + background: #0A0E27; + border-bottom: 2px solid #2C3E50; +} + +.rankings-table th { + padding: 16px 20px; + text-align: left; + font-size: 13px; + font-weight: 600; + color: #E8EDF2; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.rankings-table th:first-child { + width: 80px; + text-align: center; +} + +.rankings-table th:last-child { + width: 200px; + text-align: right; +} + +.rankings-table tbody tr { + border-bottom: 1px solid #2C3E50; + transition: background 0.2s; +} + +.rankings-table tbody tr:hover { + background: #2C3E50; +} + +.rankings-table tbody tr:last-child { + border-bottom: none; +} + +.rankings-table td { + padding: 16px 20px; + font-size: 14px; + color: #E8EDF2; +} + +.rank-number { + text-align: center; + font-weight: 600; + color: #858585; +} + +.team-name-cell { + font-weight: 500; + color: #fff; +} + +.score-cell { + text-align: right; + font-weight: 600; + color: #FF6B35; + font-size: 16px; +} + +.tasks-cell { + text-align: right; + color: #858585; +} + +.empty-state { + text-align: center; + padding: 64px 24px; + color: #858585; +} + +.empty-state-icon { + font-size: 64px; + margin-bottom: 16px; + opacity: 0.5; +} + +.empty-state-text { + font-size: 18px; + margin-bottom: 8px; +} + +.empty-state-subtext { + font-size: 14px; +} + +/* Rush Hour Freeze Overlay */ +.rush-hour-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(10, 14, 39, 0.95); + backdrop-filter: blur(10px); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + animation: fadeIn 0.5s ease-in-out; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.rush-hour-content { + text-align: center; + max-width: 600px; + padding: 48px; + background: #1A2332; + border: 2px solid #ffd700; + border-radius: 16px; + box-shadow: 0 0 40px rgba(255, 215, 0, 0.3); +} + +.rush-hour-icon { + font-size: 80px; + margin-bottom: 24px; + animation: pulse 2s ease-in-out infinite; +} + +@keyframes pulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.1); } +} + +.rush-hour-title { + font-size: 32px; + font-weight: 700; + color: #ffd700; + margin-bottom: 16px; + text-transform: uppercase; + letter-spacing: 2px; +} + +.rush-hour-message { + font-size: 18px; + color: #cccccc; + line-height: 1.6; + margin-bottom: 24px; +} + +.rush-hour-note { + font-size: 14px; + color: #858585; + padding: 16px; + background: #0A0E27; + border-radius: 8px; + border-left: 3px solid #ffd700; + margin-bottom: 24px; +} + +.rush-hour-button { + display: inline-block; + padding: 14px 32px; + background: linear-gradient(135deg, #ffd700, #ffed4e); + color: #1e1e1e; + border: none; + border-radius: 8px; + font-size: 16px; + font-weight: 600; + text-decoration: none; + transition: all 0.3s; + box-shadow: 0 4px 12px rgba(255, 215, 0, 0.3); +} + +.rush-hour-button:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(255, 215, 0, 0.5); + background: linear-gradient(135deg, #ffed4e, #ffd700); +} + +/* Responsive */ +@media (max-width: 768px) { + .podium-container { + flex-direction: column; + align-items: center; + } + + .podium-item { + order: initial !important; + max-width: 400px; + } + + .page-title { + font-size: 28px; + } + + .rankings-table { + overflow-x: auto; + } + + .rankings-table th, + .rankings-table td { + padding: 12px 16px; + font-size: 13px; + } + + .rush-hour-content { + margin: 24px; + padding: 32px 24px; + } + + .rush-hour-icon { + font-size: 64px; + } + + .rush-hour-title { + font-size: 24px; + } + + .rush-hour-message { + font-size: 16px; + } + + .rush-hour-button { + padding: 12px 24px; + font-size: 14px; + } +} diff --git a/src/staticfiles/css/shop.css b/src/staticfiles/css/shop.css new file mode 100644 index 0000000..00a5bd8 --- /dev/null +++ b/src/staticfiles/css/shop.css @@ -0,0 +1,189 @@ +/* Shop Page Styles */ + +.shop-container { + max-width: 1200px; + margin: 50px auto 0; + padding: 2rem; +} + +.coin-balance { + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); + padding: 2rem; + border-radius: 12px; + text-align: center; + margin-bottom: 2rem; + box-shadow: 0 8px 16px rgba(255, 107, 53, 0.3); +} + +.coin-amount { + font-size: 3rem; + font-weight: 700; + color: white; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); +} + +.coin-label { + font-size: 1.125rem; + color: #fef3c7; + margin-top: 0.5rem; +} + +.cooldown-notice { + background: #1A2332; + border-left: 4px solid #FF6B35; + padding: 1rem 1.5rem; + border-radius: 8px; + margin-bottom: 2rem; +} + +.powers-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 1.5rem; + margin-bottom: 3rem; +} + +.power-card { + background: #1A2332; + border: 1px solid #2C3E50; + border-radius: 12px; + padding: 1.5rem; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.power-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #FF6B35, #FF8555); +} + +.power-card:hover { + transform: translateY(-4px); + border-color: #FF6B35; + box-shadow: 0 12px 24px rgba(255, 107, 53, 0.2); +} + +.power-icon { + width: 48px; + height: 48px; + margin-bottom: 1rem; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); + border-radius: 12px; + padding: 10px; +} + +.power-icon svg { + width: 28px; + height: 28px; + stroke: white; +} + +.power-name { + font-size: 1.5rem; + font-weight: 600; + color: white; + margin-bottom: 0.5rem; +} + +.power-description { + color: #858585; + font-size: 0.9375rem; + line-height: 1.6; + margin-bottom: 1.5rem; + min-height: 60px; +} + +.power-cost { + display: flex; + align-items: center; + justify-content: space-between; + padding-top: 1rem; + border-top: 1px solid #2C3E50; +} + +.cost-amount { + font-size: 1.25rem; + font-weight: 700; + color: #FF6B35; +} + +.buy-button { + background: linear-gradient(135deg, #FF6B35 0%, #FF8555 100%); + color: white; + border: none; + padding: 0.625rem 1.5rem; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; +} + +.buy-button:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(255, 107, 53, 0.4); +} + +.buy-button:disabled { + background: #2C3E50; + cursor: not-allowed; + opacity: 0.5; +} + +.purchases-section { + margin-top: 3rem; +} + +.section-title { + font-size: 1.5rem; + font-weight: 600; + color: white; + margin-bottom: 1.5rem; + padding-bottom: 0.75rem; + border-bottom: 2px solid #2C3E50; +} + +.purchase-item { + background: #1A2332; + border: 1px solid #2C3E50; + border-radius: 8px; + padding: 1rem 1.5rem; + margin-bottom: 0.75rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.purchase-info { + color: #cccccc; +} + +.purchase-power { + font-weight: 600; + color: white; + margin-right: 1rem; +} + +.purchase-date { + color: #858585; + font-size: 0.875rem; +} + +.purchase-cost { + color: #FF6B35; + font-weight: 600; +} + +.no-purchases { + text-align: center; + padding: 3rem; + color: #858585; +} diff --git a/src/staticfiles/js/challenge-details.js b/src/staticfiles/js/challenge-details.js new file mode 100644 index 0000000..53b4cd1 --- /dev/null +++ b/src/staticfiles/js/challenge-details.js @@ -0,0 +1,111 @@ +// Challenge Details Page JavaScript + +// Initialize CodeMirror +let editor; +let initialCode; +let canSubmit; + +document.addEventListener("DOMContentLoaded", function () { + // Get initial values from template (passed via data attributes or inline script) + const editorElement = document.getElementById("codeEditor"); + if (!editorElement) return; + + editor = CodeMirror.fromTextArea(editorElement, { + lineNumbers: true, + mode: "text/x-csrc", + theme: "monokai", + indentUnit: 4, + tabSize: 4, + matchBrackets: true, + autoCloseBrackets: true, + readOnly: !canSubmit, + lineWrapping: false, + extraKeys: { + "Tab": function(cm) { + if (cm.somethingSelected()) { + cm.indentSelection("add"); + } else { + cm.replaceSelection(" ", "end"); + } + } + } + }); + + editor.setSize(null, "100%"); +}); + +// Reset button +function resetCode() { + const resetBtn = document.getElementById("resetBtn"); + if (resetBtn && !resetBtn.disabled && editor) { + editor.setValue(initialCode); + } +} + +document.getElementById("resetBtn")?.addEventListener("click", resetCode); + +// Submit button +function submitCode() { + const submitBtn = document.getElementById("submitBtn"); + if (!submitBtn || submitBtn.disabled) return; + + const code = editor ? editor.getValue() : document.getElementById("codeEditor").value; + + if (!code.trim()) { + alert("Please write some code before submitting!"); + return; + } + + // Check if there's a previous submission + const hasPreviousSubmission = submitBtn.dataset.hasPrevious === 'true'; + const currentAttempts = parseInt(submitBtn.dataset.currentAttempts || '0'); + const currentScore = parseInt(submitBtn.dataset.currentScore || '0'); + const maxAttempts = parseInt(submitBtn.dataset.maxAttempts || '0'); + + if (hasPreviousSubmission && currentAttempts > 0) { + const confirmed = confirm( + "⚠️ Warning: This submission will overwrite your previous attempt.\n\n" + + "Your current score: " + currentScore + "%\n" + + "Attempts used: " + currentAttempts + "/" + maxAttempts + "\n\n" + + "Are you sure you want to continue?" + ); + + if (!confirmed) { + return; // User cancelled, don't submit + } + } + + // Disable button immediately and show loading state + submitBtn.disabled = true; + submitBtn.innerHTML = '
Submitting...'; + submitBtn.className = 'btn btn-warning'; + + const file = new Blob([code], { type: "text/plain" }); + const inputElement = document.getElementById("uploadedFile"); + const dataTransfer = new DataTransfer(); + dataTransfer.items.add(new File([file], "code.c")); + inputElement.files = dataTransfer.files; + + document.getElementById("hiddenForm").submit(); +} + +document.getElementById("submitBtn")?.addEventListener("click", submitCode); + +// Toggle test case accordion +function toggleTestCase(index) { + const details = document.getElementById(`test-details-${index}`); + const icon = document.getElementById(`accordion-icon-${index}`); + + if (details && icon) { + if (details.classList.contains('expanded')) { + details.classList.remove('expanded'); + icon.classList.remove('expanded'); + } else { + details.classList.add('expanded'); + icon.classList.add('expanded'); + } + } +} + +// Make function globally available for onclick handlers +window.toggleTestCase = toggleTestCase; diff --git a/src/staticfiles/js/challenges-page.js b/src/staticfiles/js/challenges-page.js new file mode 100644 index 0000000..7c63037 --- /dev/null +++ b/src/staticfiles/js/challenges-page.js @@ -0,0 +1,23 @@ +// Challenges Page JavaScript + +const levelsDropdown = document.getElementById("levelsDropdown"); + +function filterChallenges() { + const selectedLevel = levelsDropdown.value.toLowerCase(); + const challengeCards = document.querySelectorAll(".challenge-card"); + + challengeCards.forEach((card) => { + const cardLevel = card.getAttribute("data-level"); + const matchesLevel = selectedLevel === "all" || cardLevel === selectedLevel; + + if (matchesLevel) { + card.style.display = "flex"; + } else { + card.style.display = "none"; + } + }); +} + +if (levelsDropdown) { + levelsDropdown.addEventListener("change", filterChallenges); +} diff --git a/src/tasks/kafka_consumer.py b/src/tasks/kafka_consumer.py index 6900572..9230e70 100644 --- a/src/tasks/kafka_consumer.py +++ b/src/tasks/kafka_consumer.py @@ -112,8 +112,8 @@ async def consume_results(self): # Determine status based on score and threshold from tasks.models import Settings - settings = await asyncio.to_thread(Settings.get_settings) - if submission.score >= settings.pass_threshold: + app_settings = await asyncio.to_thread(Settings.get_settings) + if submission.score >= app_settings.pass_threshold: submission.status = 'completed' else: submission.status = 'failed' From 6d542e524587dae0aa2a26a5c3a3482704fab71f Mon Sep 17 00:00:00 2001 From: "Amar A.J Bouakaz" Date: Sun, 30 Nov 2025 18:36:41 +0100 Subject: [PATCH 3/4] Implement resizable panels for challenge details page with vertical and horizontal resizing functionality --- src/src/static/css/challenge-details.css | 99 ++++++++++++++++- src/src/static/js/challenge-details.js | 105 ++++++++++++++++++ .../templates/tasks/challenge-detailes.html | 14 ++- src/staticfiles/css/challenge-details.css | 99 ++++++++++++++++- src/staticfiles/js/challenge-details.js | 105 ++++++++++++++++++ 5 files changed, 412 insertions(+), 10 deletions(-) diff --git a/src/src/static/css/challenge-details.css b/src/src/static/css/challenge-details.css index 67b95b9..fc7d0a0 100644 --- a/src/src/static/css/challenge-details.css +++ b/src/src/static/css/challenge-details.css @@ -9,10 +9,13 @@ /* Problem Panel */ .problem-panel { width: 50%; + min-width: 300px; + max-width: calc(100% - 304px); /* 100% minus min editor width + handle */ background: #1A2332; overflow-y: auto; - border-right: 1px solid #2C3E50; height: calc(100vh - 50px); + flex-shrink: 0; + position: relative; } .problem-header { @@ -63,12 +66,13 @@ /* Editor Panel */ .editor-panel { - width: 50%; + flex: 1; display: flex; flex-direction: column; background: #0A0E27; overflow: hidden; height: calc(100vh - 50px); + min-width: 300px; } .editor-header { @@ -149,9 +153,12 @@ .test-results-section { background: #0A0E27; border-top: 1px solid #2C3E50; - max-height: 200px; + height: 250px; overflow-y: auto; flex-shrink: 0; + min-height: 100px; + max-height: 600px; + position: relative; } .test-results-header { @@ -337,6 +344,92 @@ 100% { transform: rotate(360deg); } } +/* Resize Handles */ +.resize-handle { + width: 5px; + background: #2C3E50; + cursor: col-resize; + flex-shrink: 0; + position: relative; + transition: background 0.2s, width 0.2s; + z-index: 10; +} + +.resize-handle:hover { + background: #FF6B35; + width: 6px; +} + +.resize-handle.resizing { + background: #FF6B35; + width: 6px; +} + +.resize-handle::before { + content: ''; + position: absolute; + top: 0; + left: -4px; + right: -4px; + bottom: 0; + /* Wider hit area for easier grabbing */ +} + +.resize-handle-horizontal { + height: 5px; + background: #2C3E50; + cursor: row-resize; + flex-shrink: 0; + position: relative; + transition: background 0.2s, height 0.2s; + z-index: 10; +} + +.resize-handle-horizontal:hover { + background: #FF6B35; + height: 6px; +} + +.resize-handle-horizontal.resizing { + background: #FF6B35; + height: 6px; +} + +.resize-handle-horizontal::before { + content: ''; + position: absolute; + left: 0; + right: 0; + top: -4px; + bottom: -4px; + /* Taller hit area for easier grabbing */ +} + +/* Prevent text selection during resize */ +body.resizing { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: col-resize !important; +} + +body.resizing * { + cursor: col-resize !important; +} + +body.resizing-horizontal { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: row-resize !important; +} + +body.resizing-horizontal * { + cursor: row-resize !important; +} + /* Responsive */ @media (max-width: 1024px) { .challenge-container { diff --git a/src/src/static/js/challenge-details.js b/src/src/static/js/challenge-details.js index 53b4cd1..3c50faf 100644 --- a/src/src/static/js/challenge-details.js +++ b/src/src/static/js/challenge-details.js @@ -109,3 +109,108 @@ function toggleTestCase(index) { // Make function globally available for onclick handlers window.toggleTestCase = toggleTestCase; + +// Resizable panels functionality +document.addEventListener("DOMContentLoaded", function () { + // Vertical resize (between problem and editor panels) + const resizeHandle1 = document.getElementById("resizeHandle1"); + const problemPanel = document.getElementById("problemPanel"); + const editorPanel = document.getElementById("editorPanel"); + + if (resizeHandle1 && problemPanel && editorPanel) { + let isResizing = false; + let startX = 0; + let startWidth = 0; + + resizeHandle1.addEventListener("mousedown", function(e) { + isResizing = true; + startX = e.clientX; + startWidth = problemPanel.offsetWidth; + resizeHandle1.classList.add("resizing"); + document.body.classList.add("resizing"); + e.preventDefault(); + }); + + document.addEventListener("mousemove", function(e) { + if (!isResizing) return; + + const delta = e.clientX - startX; + const newWidth = startWidth + delta; + const containerWidth = problemPanel.parentElement.offsetWidth; + const minWidth = 300; // Minimum width in pixels + const handleWidth = 5; // Width of resize handle + const maxWidth = containerWidth - 300 - handleWidth; // Leave space for editor and handle + + if (newWidth >= minWidth && newWidth <= maxWidth) { + problemPanel.style.width = newWidth + "px"; + // Trigger CodeMirror refresh during resize for smooth rendering + if (editor) { + editor.refresh(); + } + } + }); + + document.addEventListener("mouseup", function() { + if (isResizing) { + isResizing = false; + resizeHandle1.classList.remove("resizing"); + document.body.classList.remove("resizing"); + + // Refresh CodeMirror after resize + if (editor) { + setTimeout(() => editor.refresh(), 10); + } + } + }); + } + + // Horizontal resize (between editor and test results) + const resizeHandle2 = document.getElementById("resizeHandle2"); + const editorBody = document.getElementById("editorBody"); + const testResultsSection = document.getElementById("testResultsSection"); + + if (resizeHandle2 && editorBody && testResultsSection) { + let isResizingH = false; + let startY = 0; + let startHeight = 0; + + resizeHandle2.addEventListener("mousedown", function(e) { + isResizingH = true; + startY = e.clientY; + startHeight = testResultsSection.offsetHeight; + resizeHandle2.classList.add("resizing"); + document.body.classList.add("resizing-horizontal"); + e.preventDefault(); + }); + + document.addEventListener("mousemove", function(e) { + if (!isResizingH) return; + + const delta = startY - e.clientY; // Inverted because we're resizing from bottom + const newHeight = startHeight + delta; + const minHeight = 100; // Minimum height for test results + const maxHeight = 600; // Maximum height for test results + + if (newHeight >= minHeight && newHeight <= maxHeight) { + testResultsSection.style.height = newHeight + "px"; + // Trigger CodeMirror refresh during resize for smooth rendering + if (editor) { + editor.refresh(); + } + } + }); + + document.addEventListener("mouseup", function() { + if (isResizingH) { + isResizingH = false; + resizeHandle2.classList.remove("resizing"); + document.body.classList.remove("resizing-horizontal"); + + // Refresh CodeMirror after resize + if (editor) { + setTimeout(() => editor.refresh(), 10); + } + } + }); + } +}); diff --git a/src/src/templates/tasks/challenge-detailes.html b/src/src/templates/tasks/challenge-detailes.html index 3ae4d76..63ff483 100644 --- a/src/src/templates/tasks/challenge-detailes.html +++ b/src/src/templates/tasks/challenge-detailes.html @@ -16,7 +16,7 @@
-
+

{{task.title}}

@@ -57,8 +57,11 @@

Sample Test Cases

+ +
+ -
+
@@ -81,10 +84,13 @@

Sample Test Cases

-
+
+ +
+