From ab1f5d0f7f360e440e2102cc82776776e3cdfe52 Mon Sep 17 00:00:00 2001 From: stefan Date: Thu, 5 Jun 2025 17:34:03 +0200 Subject: [PATCH] fix SQLite --- .../src/main/kotlin/at/mocode/Application.kt | 17 +- .../main/kotlin/at/mocode/views/AdminView.kt | 154 ----- .../main/kotlin/at/mocode/views/HomeView.kt | 51 -- .../kotlin/at/mocode/views/LayoutTemplate.kt | 547 +++++------------- .../kotlin/at/mocode/views/NennungView.kt | 189 +----- .../src/main/resources/static/css/admin.css | 407 +++++++++++++ server/src/main/resources/static/css/base.css | 156 +++++ .../src/main/resources/static/css/buttons.css | 213 +++++++ .../src/main/resources/static/css/effects.css | 140 +++++ .../src/main/resources/static/css/footer.css | 191 ++++++ .../src/main/resources/static/css/forms.css | 365 ++++++++++++ server/src/main/resources/static/css/main.css | 21 + .../main/resources/static/css/navigation.css | 286 +++++++++ .../src/main/resources/static/css/tables.css | 289 +++++++++ .../main/resources/static/css/tournament.css | 481 +++++++++++++++ .../main/resources/static/css/variables.css | 45 ++ 16 files changed, 2759 insertions(+), 793 deletions(-) create mode 100644 server/src/main/resources/static/css/admin.css create mode 100644 server/src/main/resources/static/css/base.css create mode 100644 server/src/main/resources/static/css/buttons.css create mode 100644 server/src/main/resources/static/css/effects.css create mode 100644 server/src/main/resources/static/css/footer.css create mode 100644 server/src/main/resources/static/css/forms.css create mode 100644 server/src/main/resources/static/css/main.css create mode 100644 server/src/main/resources/static/css/navigation.css create mode 100644 server/src/main/resources/static/css/tables.css create mode 100644 server/src/main/resources/static/css/tournament.css create mode 100644 server/src/main/resources/static/css/variables.css diff --git a/server/src/main/kotlin/at/mocode/Application.kt b/server/src/main/kotlin/at/mocode/Application.kt index ec7a4c13..c7499b42 100644 --- a/server/src/main/kotlin/at/mocode/Application.kt +++ b/server/src/main/kotlin/at/mocode/Application.kt @@ -5,8 +5,9 @@ import at.mocode.routes.configureAdminRoutes import at.mocode.routes.configureHomeRoutes import at.mocode.routes.configureNennungRoutes import io.ktor.server.application.* +import io.ktor.server.http.content.* import io.ktor.server.netty.* - +import io.ktor.server.routing.* fun main(args: Array) { @@ -20,8 +21,22 @@ fun Application.module() { // Configure database first configureDatabase() + // Configure static resources + configureStaticResources() + // Configure routes configureHomeRoutes() configureNennungRoutes() configureAdminRoutes() } + +/** + * Configure static resources. + */ +fun Application.configureStaticResources() { + routing { + staticResources(remotePath = "/css", basePackage = "static/css") + staticResources(remotePath = "/js", basePackage = "static/js") + staticResources(remotePath = "/images", basePackage = "static/images") + } +} diff --git a/server/src/main/kotlin/at/mocode/views/AdminView.kt b/server/src/main/kotlin/at/mocode/views/AdminView.kt index 6dfccb67..6b79c2e0 100644 --- a/server/src/main/kotlin/at/mocode/views/AdminView.kt +++ b/server/src/main/kotlin/at/mocode/views/AdminView.kt @@ -154,92 +154,6 @@ class AdminView { } } - // Additional styles specific to the admin page - style { - +""" - .admin-section { - background-color: var(--container-bg); - border-radius: 8px; - padding: 1.5rem; - margin-bottom: 2rem; - box-shadow: 0 2px 10px rgba(0,0,0,0.05); - } - - .admin-section h2 { - border-bottom: 1px solid var(--border-color); - padding-bottom: 0.75rem; - margin-bottom: 1.5rem; - color: var(--primary-color); - } - - .admin-form { - max-width: 100%; - } - - .form-row { - display: flex; - flex-wrap: wrap; - margin: 0 -10px; - } - - .form-group-third { - flex: 0 0 calc(33.333% - 20px); - margin: 0 10px; - } - - .bewerbe-section { - border-top: 1px solid var(--border-color); - padding-top: 1.5rem; - } - - .bewerb-container { - background-color: var(--light-bg); - border-radius: 8px; - padding: 1.5rem; - margin-bottom: 1rem; - border: 1px solid var(--border-color); - } - - .bewerb-container h4 { - margin-top: 0; - color: var(--secondary-color); - border-bottom: 1px solid var(--border-color); - padding-bottom: 0.5rem; - margin-bottom: 1rem; - } - - .table-responsive { - overflow-x: auto; - } - - .competition-list { - margin: 0; - padding-left: 1.2rem; - } - - .competition-list li { - margin-bottom: 0.25rem; - } - - .empty-state { - text-align: center; - padding: 2rem; - color: var(--light-text); - } - - .empty-state i { - font-size: 3rem; - margin-bottom: 1rem; - display: block; - } - - @media (max-width: 768px) { - .form-group-third { - flex: 0 0 calc(100% - 20px); - } - } - """ - } // JavaScript for dynamic form handling script(type = "text/javascript") { @@ -442,74 +356,6 @@ class AdminView { } } - // Additional styles specific to the edit page - style { - +""" - .admin-section { - background-color: var(--container-bg); - border-radius: 8px; - padding: 1.5rem; - margin-bottom: 2rem; - box-shadow: 0 2px 10px rgba(0,0,0,0.05); - } - - .admin-form { - max-width: 100%; - } - - .form-row { - display: flex; - flex-wrap: wrap; - margin: 0 -10px; - } - - .form-group-third { - flex: 0 0 calc(33.333% - 20px); - margin: 0 10px; - } - - .bewerbe-section { - border-top: 1px solid var(--border-color); - padding-top: 1.5rem; - } - - .bewerb-container { - background-color: var(--light-bg); - border-radius: 8px; - padding: 1.5rem; - margin-bottom: 1rem; - border: 1px solid var(--border-color); - } - - .bewerb-container h4 { - margin-top: 0; - color: var(--secondary-color); - border-bottom: 1px solid var(--border-color); - padding-bottom: 0.5rem; - margin-bottom: 1rem; - } - - .readonly-field { - background-color: var(--light-bg); - cursor: not-allowed; - } - - .ml-2 { - margin-left: 0.5rem; - } - - @media (max-width: 768px) { - .form-group-third { - flex: 0 0 calc(100% - 20px); - } - - .ml-2 { - margin-left: 0; - margin-top: 0.5rem; - } - } - """ - } // JavaScript for dynamic form handling script(type = "text/javascript") { diff --git a/server/src/main/kotlin/at/mocode/views/HomeView.kt b/server/src/main/kotlin/at/mocode/views/HomeView.kt index 03d2d91c..7d9a39aa 100644 --- a/server/src/main/kotlin/at/mocode/views/HomeView.kt +++ b/server/src/main/kotlin/at/mocode/views/HomeView.kt @@ -77,57 +77,6 @@ class HomeView { } } - style { - +""" - .tournament-list { - display: flex; - flex-direction: column; - gap: 16px; - max-width: 800px; - margin: 0 auto; - } - - .tournament-item { - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 24px; - transition: box-shadow 0.3s; - background-color: var(--container-bg); - box-shadow: 0 2px 8px rgba(0,0,0,0.05); - } - - .tournament-item:hover { - box-shadow: 0 5px 15px rgba(0,0,0,0.08); - } - - .tournament-header h3 { - margin-top: 0; - color: var(--primary-color); - font-size: 1.4rem; - } - - .tournament-header p { - color: var(--light-text); - margin-bottom: 0.5rem; - font-size: 0.95rem; - } - - .tournament-competitions h4 { - font-size: 1.1rem; - margin-bottom: 0.5rem; - color: var(--secondary-color); - } - - .tournament-competitions ul { - padding-left: 1.2rem; - } - - .tournament-actions { - margin-top: 1rem; - text-align: right; - } - """ - } } } } diff --git a/server/src/main/kotlin/at/mocode/views/LayoutTemplate.kt b/server/src/main/kotlin/at/mocode/views/LayoutTemplate.kt index eebfb1db..f5b18dfe 100644 --- a/server/src/main/kotlin/at/mocode/views/LayoutTemplate.kt +++ b/server/src/main/kotlin/at/mocode/views/LayoutTemplate.kt @@ -25,367 +25,73 @@ class LayoutTemplate { meta(name = "viewport", content = "width=device-width, initial-scale=1.0") title { +title } link(rel = "stylesheet", href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css") - link(rel = "stylesheet", href = "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap") - link(rel = "stylesheet", href = "https://fonts.googleapis.com/icon?family=Material+Icons") - link(rel = "stylesheet", href = "https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css") - style { - +""" - /* Base styles */ - :root { - --primary-color: #5d8aa8; - --primary-hover: #4a7a98; - --secondary-color: #7d9eb1; - --secondary-hover: #6a8ca1; - --text-color: #333; - --light-text: #666; - --lighter-text: #999; - --border-color: #e0e0e0; - --light-bg: #f5f7fa; - --container-bg: #fff; - --success-color: #66bb6a; - --warning-color: #ffa726; - --error-color: #ef5350; - } - - * { - box-sizing: border-box; - margin: 0; - padding: 0; - } - - body { - font-family: 'Roboto', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - line-height: 1.6; - color: var(--text-color); - background-color: var(--light-bg); - padding: 0; - margin: 0; - } - - .container { - width: 100%; - max-width: 1200px; - margin: 0 auto; - padding: 0 20px; - } - - /* Navigation */ - nav.nav-extended { - background-color: var(--primary-color); - margin-bottom: 20px; - } - - nav .brand-logo { - font-size: 1.6rem; - font-weight: 500; - padding-left: 10px; - } - - nav .brand-logo i { - margin-right: 8px; - } - - nav ul li a { - font-weight: 500; - transition: background-color 0.3s; - } - - nav ul li a:hover { - background-color: rgba(255,255,255,0.1); - } - - .sidenav { - width: 280px; - } - - .sidenav .user-view { - padding: 20px 16px 12px; - } - - .sidenav .user-view .name { - font-size: 1.4rem; - font-weight: 500; - margin-top: 8px; - color: var(--primary-color); - } - - .sidenav li > a { - display: flex; - align-items: center; - font-weight: 500; - } - - .sidenav li > a > i { - margin-right: 16px; - color: var(--primary-color); - } - - /* Main content */ - main { - padding: 2rem 0; - } - - .content-card { - background-color: var(--container-bg); - border-radius: 8px; - box-shadow: 0 2px 10px rgba(0,0,0,0.05); - padding: 2rem; - margin-bottom: 2rem; - } - - /* Typography */ - h1, h2, h3, h4, h5, h6 { - margin-bottom: 1rem; - line-height: 1.2; - color: var(--text-color); - } - - h1 { - font-size: 2.2rem; - text-align: center; - margin-bottom: 1.5rem; - } - - h2 { - font-size: 1.8rem; - margin-top: 1.5rem; - } - - h3 { - font-size: 1.5rem; - margin-top: 1.2rem; - } - - p { - margin-bottom: 1rem; - } - - /* Forms */ - .form-group { - margin-bottom: 1.5rem; - } - - label { - display: block; - margin-bottom: 0.5rem; - font-weight: 600; - } - - input[type="text"], - input[type="email"], - input[type="tel"], - input[type="number"], - textarea, - select { - width: 100%; - padding: 0.8rem; - border: 1px solid var(--border-color); - border-radius: 6px; - font-size: 1rem; - transition: all 0.3s; - margin-bottom: 1.2rem; - box-shadow: 0 1px 3px rgba(0,0,0,0.05); - background-color: white; - } - - input[type="text"]:focus, - input[type="email"]:focus, - input[type="tel"]:focus, - input[type="number"]:focus, - textarea:focus, - select:focus { - border-color: var(--primary-color); - outline: none; - box-shadow: 0 2px 8px rgba(93,138,168,0.2); - } - - .required:after { - content: " *"; - color: var(--error-color); - } - - /* Buttons */ - .button, button { - display: inline-block; - background-color: var(--primary-color); - color: white; - padding: 0.9rem 1.8rem; - border: none; - border-radius: 4px; - font-size: 1.1rem; - cursor: pointer; - text-decoration: none; - transition: all 0.3s; - text-transform: uppercase; - letter-spacing: 0.5px; - font-weight: 500; - box-shadow: 0 2px 5px rgba(0,0,0,0.2); - text-align: center; - } - - .button:hover, button:hover { - background-color: var(--primary-hover); - box-shadow: 0 4px 8px rgba(0,0,0,0.3); - transform: translateY(-2px); - } - - .button-secondary { - background-color: var(--secondary-color); - } - - .button-secondary:hover { - background-color: var(--secondary-hover); - } - - /* Tables */ - table { - width: 100%; - border-collapse: collapse; - margin-bottom: 1.5rem; - } - - th, td { - padding: 0.75rem; - text-align: left; - border-bottom: 1px solid var(--border-color); - } - - th { - background-color: var(--light-bg); - font-weight: 600; - } - - tr:hover { - background-color: rgba(0,0,0,0.02); - } - - /* Lists */ - ul, ol { - margin-bottom: 1rem; - padding-left: 1.5rem; - } - - li { - margin-bottom: 0.5rem; - } - - /* Utilities */ - .text-center { - text-align: center; - } - - .mt-1 { margin-top: 0.5rem; } - .mt-2 { margin-top: 1rem; } - .mt-3 { margin-top: 1.5rem; } - .mt-4 { margin-top: 2rem; } - - .mb-1 { margin-bottom: 0.5rem; } - .mb-2 { margin-bottom: 1rem; } - .mb-3 { margin-bottom: 1.5rem; } - .mb-4 { margin-bottom: 2rem; } - - /* Footer */ - footer { - background-color: var(--text-color); - color: white; - padding: 2rem 0; - margin-top: 2rem; - } - - footer a { - color: white; - text-decoration: none; - } - - footer a:hover { - text-decoration: underline; - } - - /* Responsive design */ - @media (max-width: 768px) { - .menu-toggle { - display: block; - } - - nav ul { - display: none; - position: absolute; - top: 60px; - left: 0; - right: 0; - flex-direction: column; - background-color: var(--primary-color); - padding: 1rem; - box-shadow: 0 2px 5px rgba(0,0,0,0.1); - } - - nav ul.show { - display: flex; - } - - nav ul li { - margin: 0.5rem 0; - } - - .content-card { - padding: 1.5rem; - } - - h1 { - font-size: 1.8rem; - } - - h2 { - font-size: 1.5rem; - } - - h3 { - font-size: 1.3rem; - } - } - - @media (max-width: 480px) { - .container { - padding: 0 15px; - } - - .content-card { - padding: 1rem; - } - - h1 { - font-size: 1.6rem; - } - - .button, button { - display: block; - margin-bottom: 0.5rem; - } - } - """ - } - script(src = "https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js") {} + link(rel = "stylesheet", href = "https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto:wght@300;400;500;700&display=swap") + link(rel = "stylesheet", href = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css") + link(rel = "stylesheet", href = "https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.css") + link(rel = "stylesheet", href = "/css/main.css") + script(src = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js") {} + script(src = "https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.js") {} script { unsafe { +""" document.addEventListener('DOMContentLoaded', function() { - // Mobile menu toggle - const menuToggle = document.querySelector('.menu-toggle'); - const navMenu = document.querySelector('nav ul'); + // Initialize Bootstrap tooltips + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); - if (menuToggle && navMenu) { - menuToggle.addEventListener('click', function() { - navMenu.classList.toggle('show'); + // Initialize Bootstrap popovers + const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]'); + const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl)); + + // Add Bootstrap validation classes to forms + const forms = document.querySelectorAll('.needs-validation'); + Array.from(forms).forEach(form => { + form.addEventListener('submit', event => { + if (!form.checkValidity()) { + event.preventDefault(); + event.stopPropagation(); + } + form.classList.add('was-validated'); + }, false); + }); + + // Initialize AOS (Animate On Scroll) + AOS.init({ + duration: 800, + easing: 'ease-in-out', + once: true + }); + + // Navbar scroll effect + const navbar = document.querySelector('.navbar'); + if (navbar) { + window.addEventListener('scroll', function() { + if (window.scrollY > 50) { + navbar.classList.add('navbar-scrolled'); + } else { + navbar.classList.remove('navbar-scrolled'); + } }); } - // Initialize Materialize components - M.AutoInit(); + // Add ripple effect to buttons + const buttons = document.querySelectorAll('.button, .btn'); + buttons.forEach(button => { + button.addEventListener('click', function(e) { + const x = e.clientX - e.target.getBoundingClientRect().left; + const y = e.clientY - e.target.getBoundingClientRect().top; - // Enhance form elements - const inputs = document.querySelectorAll('input, textarea, select'); - inputs.forEach(input => { - input.classList.add('browser-default'); + const ripple = document.createElement('span'); + ripple.classList.add('ripple-effect'); + ripple.style.left = x + 'px'; + ripple.style.top = y + 'px'; + + this.appendChild(ripple); + + setTimeout(() => { + ripple.remove(); + }, 600); + }); }); }); """ @@ -394,28 +100,35 @@ class LayoutTemplate { } body { if (showNavbar) { - nav(classes = "nav-extended z-depth-1") { - div("nav-wrapper") { - div("container") { - a(href = "/", classes = "brand-logo") { - i("material-icons left") { +"sports_handball" } - +"Meldestelle Portal" - } - a(href = "#", classes = "sidenav-trigger") { - attributes["data-target"] = "mobile-nav" - i("material-icons") { +"menu" } - } - ul(classes = "right hide-on-med-and-down") { - li { - a(href = "/") { - i("material-icons left") { +"home" } + nav(classes = "navbar navbar-expand-lg navbar-dark fixed-top") { + div("container") { + a(href = "/", classes = "navbar-brand") { + i("fas fa-horse-head") {} + +"Meldestelle Portal" + } + button(classes = "navbar-toggler") { + type = ButtonType.button + attributes["data-bs-toggle"] = "collapse" + attributes["data-bs-target"] = "#navbarContent" + attributes["aria-controls"] = "navbarContent" + attributes["aria-expanded"] = "false" + attributes["aria-label"] = "Toggle navigation" + span(classes = "navbar-toggler-icon") {} + } + + div(classes = "collapse navbar-collapse") { + id = "navbarContent" + ul(classes = "navbar-nav ms-auto mb-2 mb-lg-0") { + li(classes = "nav-item") { + a(href = "/", classes = "nav-link active") { + i("fas fa-home") {} +"Home" } } if (showAdminLink) { - li { - a(href = "/admin/tournaments") { - i("material-icons left") { +"event" } + li(classes = "nav-item") { + a(href = "/admin/tournaments", classes = "nav-link") { + i("fas fa-calendar-alt") {} +"Turnierverwaltung" } } @@ -424,46 +137,74 @@ class LayoutTemplate { } } } + } + // Add padding to account for fixed navbar + div(classes = "navbar-spacer") {} - // Mobile sidenav - ul(classes = "sidenav") { - attributes["id"] = "mobile-nav" - li { - div("user-view") { - div("background blue-grey lighten-4") { - style = "height: 80px;" - } - span("name") { +"Meldestelle Portal" } - } - } - li { - a(href = "/") { - i("material-icons") { +"home" } - +"Home" - } - } - if (showAdminLink) { - li { - a(href = "/admin/tournaments") { - i("material-icons") { +"event" } - +"Turnierverwaltung" - } + main(classes = "py-5") { + div("container") { + div(classes = "card shadow") { + attributes["data-aos"] = "fade-up" + attributes["data-aos-delay"] = "100" + div("card-body") { + content() } } } } - main { + footer(classes = "footer mt-5") { + attributes["data-aos"] = "fade-up" + attributes["data-aos-delay"] = "200" div("container") { - div("content-card") { - content() + div("footer-content") { + div("row gy-4") { + div("col-lg-4 col-md-6") { + div("footer-info") { + h3(classes = "gradient-text") { +"Meldestelle Portal" } + p { + +"Ihre zentrale Plattform für Turnierorganisation und Anmeldungen." + } + div("social-links mt-3") { + a(href = "#", classes = "facebook") { i("fab fa-facebook-f") {} } + a(href = "#", classes = "twitter") { i("fab fa-twitter") {} } + a(href = "#", classes = "instagram") { i("fab fa-instagram") {} } + a(href = "#", classes = "linkedin") { i("fab fa-linkedin-in") {} } + } + } + } + div("col-lg-4 col-md-6") { + div("footer-links") { + h4 { +"Nützliche Links" } + ul { + li { a(href = "/") { +"Home" } } + li { a(href = "#") { +"Über uns" } } + li { a(href = "#") { +"Turniere" } } + li { a(href = "#") { +"Kontakt" } } + } + } + } + div("col-lg-4 col-md-6") { + div("footer-contact") { + h4 { +"Kontakt" } + p { + i("fas fa-envelope me-2") {} + +"info@meldestelle-portal.at" + } + p { + i("fas fa-phone me-2") {} + +"+43 123 456 789" + } + } + } + } } - } - } - footer { - div("container") { - div("text-center") { - p { +"© ${java.time.Year.now().value} Meldestelle Portal. Alle Rechte vorbehalten." } - p { + div("footer-legal text-center") { + div("copyright") { + +"© ${java.time.Year.now().value} " + strong { +"Meldestelle Portal" } + +". Alle Rechte vorbehalten." + } + div("credits") { +"Entwickelt von " a(href = "#") { +"mocode" } } diff --git a/server/src/main/kotlin/at/mocode/views/NennungView.kt b/server/src/main/kotlin/at/mocode/views/NennungView.kt index 76bb5b51..e526907c 100644 --- a/server/src/main/kotlin/at/mocode/views/NennungView.kt +++ b/server/src/main/kotlin/at/mocode/views/NennungView.kt @@ -144,132 +144,6 @@ class NennungView { } } - // Additional styles specific to the registration form - style { - +""" - .tournament-info { - margin-bottom: 2rem; - } - - .tournament-info h2 { - color: var(--primary-color); - } - - .form-section { - background-color: white; - border-radius: 8px; - padding: 2rem; - margin-bottom: 2rem; - box-shadow: 0 2px 10px rgba(0,0,0,0.08); - border-left: 4px solid var(--primary-color); - } - - .form-section h3 { - border-bottom: 1px solid var(--border-color); - padding-bottom: 0.8rem; - margin-bottom: 1.5rem; - text-align: left; - color: var(--primary-color); - font-size: 1.4rem; - } - - .form-row { - display: flex; - flex-direction: column; - width: 100%; - margin: 0; - } - - @media (min-width: 768px) { - .form-row { - flex-direction: row; - gap: 20px; - } - - .form-group-half { - flex: 1; - } - } - - .form-group { - width: 100%; - margin: 0 0 1.5rem 0; - } - - .form-group-half { - flex: 0 0 100%; - margin: 0 0 1.5rem 0; - max-width: 100%; - } - - .form-hint { - font-size: 0.9rem; - color: var(--light-text); - margin-top: 0.5rem; - text-align: center; - } - - .competitions-list { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 15px; - width: 100%; - margin: 0; - } - - .competition-item { - padding: 18px; - border-radius: 8px; - transition: all 0.3s; - width: 100%; - text-align: left; - border: 1px solid var(--border-color); - background-color: white; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); - } - - .competition-item:hover { - background-color: rgba(0,0,0,0.02); - box-shadow: 0 3px 8px rgba(0,0,0,0.12); - } - - .competition-item label { - display: flex; - align-items: center; - justify-content: flex-start; - cursor: pointer; - } - - .competition-item input[type="checkbox"] { - margin-right: 15px; - transform: scale(1.2); - } - - .competition-details { - text-align: left; - } - - /* Participant information styling */ - .participant-details { - width: 100%; - } - - .participant-details label { - display: block; - margin-bottom: 0.5rem; - font-weight: 500; - } - - .participant-details input { - width: 100%; - } - - @media (max-width: 768px) { - /* Mobile styles already handled by the responsive layout */ - } - """ - } } } } @@ -318,7 +192,11 @@ class NennungView { } p(classes = "confirmation-message") { - +"Sie erhalten in Kürze eine Bestätigung per E-Mail." + +"Start und Ergebnislisten auf" + } + + p(classes = "confirmation-message") { + +"www.ihremeldestelle.at" } div(classes = "confirmation-actions") { @@ -328,63 +206,6 @@ class NennungView { } } - // Additional styles specific to the confirmation page - style { - +""" - .confirmation-box { - background-color: var(--light-bg); - border-radius: 8px; - padding: 2rem; - text-align: center; - max-width: 600px; - margin: 0 auto; - box-shadow: 0 4px 15px rgba(0,0,0,0.05); - } - - .confirmation-icon { - font-size: 4rem; - color: var(--success-color); - margin-bottom: 1rem; - } - - .confirmation-details { - margin: 1.5rem 0; - padding: 1rem; - background-color: var(--container-bg); - border-radius: 8px; - text-align: left; - } - - .detail-item { - display: flex; - margin-bottom: 0.5rem; - padding: 0.5rem 0; - border-bottom: 1px solid var(--border-color); - } - - .detail-item:last-child { - border-bottom: none; - } - - .detail-label { - font-weight: bold; - width: 100px; - } - - .detail-value { - flex: 1; - } - - .confirmation-message { - margin: 1.5rem 0; - color: var(--light-text); - } - - .confirmation-actions { - margin-top: 1.5rem; - } - """ - } } } } diff --git a/server/src/main/resources/static/css/admin.css b/server/src/main/resources/static/css/admin.css new file mode 100644 index 00000000..175e00a5 --- /dev/null +++ b/server/src/main/resources/static/css/admin.css @@ -0,0 +1,407 @@ +/* Modern admin interface styles with enhanced UX */ + +/* Admin dashboard layout */ +.admin-dashboard { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 1.5rem; + margin-bottom: 2rem; +} + +.admin-dashboard-card { + background-color: white; + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + padding: 1.5rem; + transition: all var(--transition-fast); + border: 1px solid var(--border-color); + position: relative; + overflow: hidden; +} + +.admin-dashboard-card:hover { + transform: translateY(-5px); + box-shadow: var(--box-shadow-lg); +} + +.admin-dashboard-card:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 4px; + height: 100%; + background-color: var(--primary-color); +} + +.admin-dashboard-card h3 { + color: var(--primary-color); + font-size: 1.25rem; + margin-bottom: 1rem; + font-weight: 700; +} + +.admin-dashboard-card .stat { + font-size: 2.5rem; + font-weight: 700; + color: var(--text-color); + margin-bottom: 0.5rem; +} + +.admin-dashboard-card .stat-label { + color: var(--light-text); + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +/* Admin section styling */ +.admin-section { + background-color: white; + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + margin-bottom: 2rem; + overflow: hidden; + border: 1px solid var(--border-color); + transition: all var(--transition-fast); +} + +.admin-section:hover { + box-shadow: var(--box-shadow-lg); +} + +.admin-section .card-header { + background: linear-gradient(to right, rgba(67, 97, 238, 0.05), rgba(114, 9, 183, 0.05)); + border-bottom: 1px solid var(--border-color); + padding: 1.25rem 1.5rem; + display: flex; + align-items: center; + justify-content: space-between; +} + +.admin-section .card-body { + padding: 1.5rem; +} + +.admin-section h2 { + color: var(--primary-color); + font-size: 1.5rem; + margin-bottom: 0; + font-weight: 700; + display: flex; + align-items: center; +} + +.admin-section h2 i { + margin-right: 0.75rem; + font-size: 1.25rem; + background-color: rgba(67, 97, 238, 0.1); + width: 2rem; + height: 2rem; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: transform var(--transition-fast); +} + +.admin-section:hover h2 i { + transform: rotate(10deg); +} + +/* Admin form styling */ +.admin-form { + width: 100%; +} + +.admin-form .form-row { + display: flex; + flex-wrap: wrap; + margin: 0 -0.75rem; +} + +.admin-form .form-group { + padding: 0 0.75rem; + margin-bottom: 1.5rem; +} + +.admin-form .form-group-third { + flex: 0 0 33.333333%; + max-width: 33.333333%; +} + +.admin-form label { + font-weight: 600; + margin-bottom: 0.5rem; + display: block; + color: var(--text-color); +} + +.admin-form input, +.admin-form select, +.admin-form textarea { + width: 100%; + padding: 0.75rem 1rem; + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + transition: all var(--transition-fast); +} + +.admin-form input:focus, +.admin-form select:focus, +.admin-form textarea:focus { + border-color: var(--primary-color); + box-shadow: 0 0 0 0.25rem rgba(67, 97, 238, 0.25); + outline: none; +} + +/* Competition section styling */ +.bewerbe-section { + border-top: 1px solid var(--border-color); + padding-top: 2rem; + margin-top: 2rem; + position: relative; +} + +.bewerbe-section h3 { + color: var(--secondary-color); + font-size: 1.5rem; + margin-bottom: 1.5rem; + font-weight: 700; + display: flex; + align-items: center; +} + +.bewerbe-section h3 i { + margin-right: 0.75rem; + font-size: 1.25rem; + color: var(--accent-color); +} + +/* Competition container styling */ +.bewerb-container { + background-color: var(--neutral-50); + border-radius: var(--border-radius); + padding: 1.5rem; + margin-bottom: 1.5rem; + border: 1px solid var(--border-color); + position: relative; + transition: all var(--transition-fast); + box-shadow: var(--box-shadow-sm); +} + +.bewerb-container:hover { + background-color: white; + transform: translateY(-3px); + box-shadow: var(--box-shadow); + border-color: var(--primary-color); +} + +.bewerb-container h4 { + color: var(--secondary-color); + font-size: 1.25rem; + border-bottom: 1px solid var(--border-color); + padding-bottom: 0.75rem; + margin-bottom: 1.25rem; + font-weight: 600; + display: flex; + align-items: center; +} + +.bewerb-container h4 i { + margin-right: 0.5rem; + color: var(--accent-color); + transition: transform var(--transition-fast); +} + +.bewerb-container:hover h4 i { + transform: rotate(10deg); +} + +/* Empty state styling */ +.empty-state { + text-align: center; + padding: 3rem 2rem; + background-color: var(--neutral-50); + border-radius: var(--border-radius); + border: 1px dashed var(--border-color); + margin: 2rem 0; +} + +.empty-state i { + font-size: 4rem; + margin-bottom: 1.5rem; + display: block; + color: var(--secondary-color); + opacity: 0.7; +} + +.empty-state p { + font-size: 1.25rem; + color: var(--text-color); + margin-bottom: 1.5rem; + font-weight: 500; +} + +.empty-state .button { + margin-top: 1rem; +} + +/* Admin table styling */ +.admin-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + margin-bottom: 1.5rem; +} + +.admin-table th { + background-color: var(--neutral-100); + font-weight: 700; + text-transform: uppercase; + font-size: 0.85rem; + letter-spacing: 0.05em; + color: var(--primary-color); + padding: 1rem; + border-bottom: 2px solid var(--primary-color); +} + +.admin-table td { + padding: 1rem; + border-bottom: 1px solid var(--border-color); + vertical-align: middle; +} + +.admin-table tr:hover td { + background-color: rgba(67, 97, 238, 0.03); +} + +.admin-table tr:last-child td { + border-bottom: none; +} + +/* Admin action buttons */ +.admin-actions { + display: flex; + gap: 0.75rem; + margin-top: 1.5rem; + justify-content: flex-end; +} + +.admin-actions .button { + display: inline-flex; + align-items: center; + justify-content: center; +} + +.admin-actions .button i { + margin-right: 0.5rem; + font-size: 1em; +} + +/* Admin tabs */ +.admin-tabs { + display: flex; + border-bottom: 1px solid var(--border-color); + margin-bottom: 2rem; +} + +.admin-tab { + padding: 1rem 1.5rem; + font-weight: 600; + color: var(--light-text); + cursor: pointer; + position: relative; + transition: all var(--transition-fast); +} + +.admin-tab:hover { + color: var(--primary-color); +} + +.admin-tab.active { + color: var(--primary-color); +} + +.admin-tab.active:after { + content: ''; + position: absolute; + bottom: -1px; + left: 0; + width: 100%; + height: 3px; + background-color: var(--primary-color); +} + +/* Admin filters */ +.admin-filters { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-bottom: 1.5rem; + padding: 1rem; + background-color: var(--neutral-50); + border-radius: var(--border-radius); +} + +.admin-filter { + flex: 1; + min-width: 200px; +} + +.admin-filter label { + font-weight: 600; + margin-bottom: 0.5rem; + display: block; + font-size: 0.875rem; +} + +/* Responsive adjustments */ +@media (max-width: 992px) { + .admin-dashboard { + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + } + + .admin-form .form-group-third { + flex: 0 0 50%; + max-width: 50%; + } +} + +@media (max-width: 768px) { + .admin-section .card-header { + flex-direction: column; + align-items: flex-start; + } + + .admin-actions { + flex-direction: column; + width: 100%; + margin-top: 1rem; + } + + .admin-form .form-group-third { + flex: 0 0 100%; + max-width: 100%; + } + + .admin-tabs { + overflow-x: auto; + white-space: nowrap; + -webkit-overflow-scrolling: touch; + } +} + +@media (max-width: 576px) { + .admin-dashboard { + grid-template-columns: 1fr; + } + + .bewerb-container { + padding: 1.25rem; + } + + .admin-section .card-body { + padding: 1.25rem; + } +} diff --git a/server/src/main/resources/static/css/base.css b/server/src/main/resources/static/css/base.css new file mode 100644 index 00000000..561b2b99 --- /dev/null +++ b/server/src/main/resources/static/css/base.css @@ -0,0 +1,156 @@ +/* Base styles - Modern and accessible */ +body { + font-family: 'Inter', 'Roboto', 'Segoe UI', system-ui, -apple-system, sans-serif; + color: var(--text-color); + background-color: var(--neutral-50); + line-height: 1.7; + font-size: 16px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; +} + +/* Modern typography system */ +h1, h2, h3, h4, h5, h6 { + margin-bottom: var(--spacer-3); + font-weight: 700; + line-height: 1.3; + color: var(--text-color); + letter-spacing: -0.02em; +} + +h1 { + font-size: 2.5rem; + margin-bottom: var(--spacer-4); +} + +h2 { + font-size: 2rem; + margin-top: var(--spacer-5); + margin-bottom: var(--spacer-3); +} + +h3 { + font-size: 1.5rem; + margin-top: var(--spacer-4); +} + +h4 { + font-size: 1.25rem; +} + +p { + margin-bottom: var(--spacer-3); + max-width: 70ch; /* Optimal reading width */ +} + +a { + color: var(--primary-color); + text-decoration: none; + transition: color var(--transition-fast); +} + +a:hover { + color: var(--primary-hover); + text-decoration: underline; +} + +/* Custom page title */ +.page-title { + text-align: center; + margin-bottom: var(--spacer-5); + font-weight: 700; + color: var(--primary-color); + position: relative; + padding-bottom: var(--spacer-3); +} + +.page-title::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 60px; + height: 4px; + background-color: var(--accent-color); + border-radius: 2px; +} + +/* Modern card styles */ +.content-card { + background-color: var(--container-bg); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + transition: transform var(--transition-fast), box-shadow var(--transition-fast); + overflow: hidden; + border: 1px solid var(--border-color); +} + +.content-card:hover { + transform: translateY(-3px); + box-shadow: var(--box-shadow-lg); +} + +/* Section styling */ +.section { + padding: var(--spacer-5) 0; +} + +.section-title { + text-align: center; + margin-bottom: var(--spacer-5); +} + +/* Custom utility classes */ +.text-primary { color: var(--primary-color) !important; } +.text-secondary { color: var(--secondary-color) !important; } +.text-accent { color: var(--accent-color) !important; } +.text-muted { color: var(--light-text) !important; } + +.bg-primary { background-color: var(--primary-color) !important; } +.bg-secondary { background-color: var(--secondary-color) !important; } +.bg-accent { background-color: var(--accent-color) !important; } +.bg-light { background-color: var(--neutral-100) !important; } + +.rounded { border-radius: var(--border-radius) !important; } +.rounded-lg { border-radius: var(--border-radius-lg) !important; } +.rounded-circle { border-radius: 50% !important; } + +.shadow-sm { box-shadow: var(--box-shadow-sm) !important; } +.shadow { box-shadow: var(--box-shadow) !important; } +.shadow-lg { box-shadow: var(--box-shadow-lg) !important; } + +/* Custom responsive adjustments */ +@media (max-width: 992px) { + h1 { font-size: 2.2rem; } + h2 { font-size: 1.8rem; } + h3 { font-size: 1.4rem; } +} + +@media (max-width: 768px) { + body { font-size: 15px; } + h1 { font-size: 2rem; } + h2 { font-size: 1.6rem; } + h3 { font-size: 1.3rem; } + + .page-title { + font-size: 1.8rem; + margin-bottom: var(--spacer-4); + } +} + +@media (max-width: 480px) { + h1 { font-size: 1.8rem; } + h2 { font-size: 1.5rem; } + h3 { font-size: 1.2rem; } + + .page-title { + font-size: 1.6rem; + margin-bottom: var(--spacer-3); + } + + .section { + padding: var(--spacer-4) 0; + } +} diff --git a/server/src/main/resources/static/css/buttons.css b/server/src/main/resources/static/css/buttons.css new file mode 100644 index 00000000..36d759a6 --- /dev/null +++ b/server/src/main/resources/static/css/buttons.css @@ -0,0 +1,213 @@ +/* Modern button styles with enhanced UX */ + +/* Base button styles - applies to both .btn and .button */ +.btn, .button { + display: inline-flex; + align-items: center; + justify-content: center; + font-weight: 600; + text-align: center; + vertical-align: middle; + cursor: pointer; + user-select: none; + padding: 0.625rem 1.25rem; + font-size: 1rem; + border-radius: var(--border-radius); + transition: all var(--transition-fast); + text-decoration: none; + letter-spacing: 0.01em; + position: relative; + overflow: hidden; + border: none; + line-height: 1.5; +} + +/* Primary button */ +.btn-primary, .button { + background-color: var(--primary-color); + color: white; + box-shadow: var(--box-shadow-sm); +} + +/* Secondary button */ +.btn-secondary, .button-secondary { + background-color: var(--secondary-color); + color: white; + box-shadow: var(--box-shadow-sm); +} + +/* Accent button */ +.btn-accent, .button-accent { + background-color: var(--accent-color); + color: white; + box-shadow: var(--box-shadow-sm); +} + +/* Outline buttons */ +.btn-outline-primary { + background-color: transparent; + color: var(--primary-color); + border: 2px solid var(--primary-color); +} + +.btn-outline-secondary { + background-color: transparent; + color: var(--secondary-color); + border: 2px solid var(--secondary-color); +} + +.btn-outline-accent { + background-color: transparent; + color: var(--accent-color); + border: 2px solid var(--accent-color); +} + +/* Ghost buttons */ +.btn-ghost, .button-ghost { + background-color: transparent; + color: var(--text-color); + box-shadow: none; +} + +/* Enhanced hover and focus effects */ +.btn:hover, .button:hover { + transform: translateY(-3px); + box-shadow: var(--box-shadow); +} + +.btn:active, .button:active { + transform: translateY(-1px); +} + +.btn:focus, .button:focus { + outline: none; + box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.3); +} + +/* Ripple effect for buttons */ +.btn::after, .button::after { + content: ''; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + pointer-events: none; + background-image: radial-gradient(circle, #fff 10%, transparent 10.01%); + background-repeat: no-repeat; + background-position: 50%; + transform: scale(10, 10); + opacity: 0; + transition: transform 0.5s, opacity 0.8s; +} + +.btn:active::after, .button:active::after { + transform: scale(0, 0); + opacity: 0.3; + transition: 0s; +} + +/* Button sizes */ +.btn-sm, .button-sm { + padding: 0.375rem 0.75rem; + font-size: 0.875rem; +} + +.btn-lg, .button-lg { + padding: 0.875rem 1.75rem; + font-size: 1.125rem; + font-weight: 600; + letter-spacing: 0.02em; +} + +/* Button with icons */ +.btn i, .button i { + margin-right: 0.625rem; + font-size: 1.1em; +} + +.btn-icon, .button-icon { + width: 2.5rem; + height: 2.5rem; + padding: 0; + border-radius: 50%; +} + +.btn-icon i, .button-icon i { + margin-right: 0; +} + +/* Button group */ +.btn-group { + display: inline-flex; +} + +.btn-group .btn, .btn-group .button { + border-radius: 0; +} + +.btn-group .btn:first-child, .btn-group .button:first-child { + border-top-left-radius: var(--border-radius); + border-bottom-left-radius: var(--border-radius); +} + +.btn-group .btn:last-child, .btn-group .button:last-child { + border-top-right-radius: var(--border-radius); + border-bottom-right-radius: var(--border-radius); +} + +/* Disabled state */ +.btn:disabled, .button:disabled, +.btn.disabled, .button.disabled { + opacity: 0.65; + pointer-events: none; + box-shadow: none; +} + +/* Loading state */ +.btn-loading, .button-loading { + position: relative; + color: transparent !important; +} + +.btn-loading::before, .button-loading::before { + content: ''; + position: absolute; + width: 1rem; + height: 1rem; + border: 2px solid rgba(255, 255, 255, 0.3); + border-radius: 50%; + border-top-color: white; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Responsive button adjustments */ +@media (max-width: 768px) { + .btn, .button { + padding: 0.5rem 1rem; + } + + .btn-lg, .button-lg { + padding: 0.75rem 1.5rem; + } +} + +@media (max-width: 480px) { + .d-grid .btn, .d-grid .button { + margin-bottom: 0.5rem; + width: 100%; + } + + .btn-group { + flex-direction: column; + } + + .btn-group .btn, .btn-group .button { + border-radius: var(--border-radius); + margin-bottom: 0.5rem; + } +} diff --git a/server/src/main/resources/static/css/effects.css b/server/src/main/resources/static/css/effects.css new file mode 100644 index 00000000..eaf3e25b --- /dev/null +++ b/server/src/main/resources/static/css/effects.css @@ -0,0 +1,140 @@ +/* Special effects and animations */ + +/* Navbar spacer to account for fixed navbar */ +.navbar-spacer { + height: 70px; /* Adjust based on navbar height */ +} + +/* Ripple effect for buttons */ +.ripple-effect { + position: absolute; + border-radius: 50%; + background-color: rgba(255, 255, 255, 0.7); + width: 100px; + height: 100px; + margin-top: -50px; + margin-left: -50px; + animation: ripple 0.6s linear; + pointer-events: none; +} + +@keyframes ripple { + 0% { + transform: scale(0); + opacity: 0.5; + } + 100% { + transform: scale(4); + opacity: 0; + } +} + +/* Hover lift effect */ +.hover-lift { + transition: transform var(--transition-fast), box-shadow var(--transition-fast); +} + +.hover-lift:hover { + transform: translateY(-5px); + box-shadow: var(--box-shadow-lg); +} + +/* Gradient text */ +.gradient-text { + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + display: inline-block; +} + +/* Gradient border */ +.gradient-border { + position: relative; + border-radius: var(--border-radius); + padding: 4px; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); +} + +.gradient-border-content { + background-color: white; + border-radius: calc(var(--border-radius) - 2px); + padding: 1.5rem; + height: 100%; +} + +/* Shimmer effect for loading states */ +.shimmer { + background: linear-gradient(90deg, + var(--neutral-100) 0%, + var(--neutral-200) 50%, + var(--neutral-100) 100%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; +} + +@keyframes shimmer { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} + +/* Fade in animation */ +.fade-in { + animation: fadeIn 0.5s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Pulse animation */ +.pulse { + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } + 100% { + transform: scale(1); + } +} + +/* Floating animation */ +.floating { + animation: floating 3s ease-in-out infinite; +} + +@keyframes floating { + 0% { + transform: translateY(0px); + } + 50% { + transform: translateY(-10px); + } + 100% { + transform: translateY(0px); + } +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .navbar-spacer { + height: 60px; + } +} diff --git a/server/src/main/resources/static/css/footer.css b/server/src/main/resources/static/css/footer.css new file mode 100644 index 00000000..ae97138e --- /dev/null +++ b/server/src/main/resources/static/css/footer.css @@ -0,0 +1,191 @@ +/* Modern footer styles */ + +.footer { + background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); + color: white; + padding: 4rem 0 2rem; + position: relative; + overflow: hidden; +} + +.footer::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: url('data:image/svg+xml;utf8,'); + background-repeat: no-repeat; + background-position: bottom; + background-size: cover; + opacity: 0.1; + z-index: 0; +} + +.footer-content { + position: relative; + z-index: 1; + margin-bottom: 2rem; +} + +/* Footer info section */ +.footer-info h3 { + font-size: 1.8rem; + margin-bottom: 1rem; + font-weight: 700; +} + +.footer-info p { + font-size: 0.95rem; + line-height: 1.7; + margin-bottom: 1.5rem; + max-width: 300px; +} + +/* Social links */ +.social-links { + display: flex; + gap: 0.75rem; +} + +.social-links a { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + background-color: rgba(255, 255, 255, 0.1); + border-radius: 50%; + color: white; + transition: all var(--transition-fast); +} + +.social-links a:hover { + background-color: white; + color: var(--primary-color); + transform: translateY(-3px); +} + +/* Footer links */ +.footer-links h4 { + font-size: 1.2rem; + margin-bottom: 1.25rem; + font-weight: 600; + position: relative; + padding-bottom: 0.75rem; +} + +.footer-links h4::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 30px; + height: 2px; + background-color: white; +} + +.footer-links ul { + list-style: none; + padding: 0; + margin: 0; +} + +.footer-links li { + margin-bottom: 0.75rem; + position: relative; + padding-left: 1.25rem; +} + +.footer-links li::before { + content: '›'; + position: absolute; + left: 0; + top: 0; + font-size: 1.2rem; + color: rgba(255, 255, 255, 0.7); +} + +.footer-links a { + color: rgba(255, 255, 255, 0.8); + text-decoration: none; + transition: all var(--transition-fast); +} + +.footer-links a:hover { + color: white; + padding-left: 5px; +} + +/* Footer contact */ +.footer-contact h4 { + font-size: 1.2rem; + margin-bottom: 1.25rem; + font-weight: 600; + position: relative; + padding-bottom: 0.75rem; +} + +.footer-contact h4::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 30px; + height: 2px; + background-color: white; +} + +.footer-contact p { + margin-bottom: 0.75rem; + display: flex; + align-items: center; +} + +.footer-contact i { + width: 20px; + text-align: center; + margin-right: 0.75rem; +} + +/* Footer legal section */ +.footer-legal { + border-top: 1px solid rgba(255, 255, 255, 0.1); + padding-top: 1.5rem; + margin-top: 1.5rem; + color: rgba(255, 255, 255, 0.8); + font-size: 0.9rem; +} + +.footer-legal .copyright { + margin-bottom: 0.5rem; +} + +.footer-legal a { + color: white; + text-decoration: none; + font-weight: 600; + transition: all var(--transition-fast); +} + +.footer-legal a:hover { + text-decoration: underline; +} + +/* Responsive adjustments */ +@media (max-width: 992px) { + .footer { + padding: 3rem 0 1.5rem; + } +} + +@media (max-width: 768px) { + .footer-info, .footer-links, .footer-contact { + margin-bottom: 2rem; + } + + .footer-info p { + max-width: 100%; + } +} diff --git a/server/src/main/resources/static/css/forms.css b/server/src/main/resources/static/css/forms.css new file mode 100644 index 00000000..6e7c9f38 --- /dev/null +++ b/server/src/main/resources/static/css/forms.css @@ -0,0 +1,365 @@ +/* Modern form styles with enhanced UX and accessibility */ + +/* Form layout and spacing */ +.form-group { + margin-bottom: 1.5rem; + position: relative; +} + +.form-row { + display: flex; + flex-wrap: wrap; + margin-right: -0.75rem; + margin-left: -0.75rem; +} + +.form-col { + flex: 1 0 0; + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +/* Modern label styling */ +.form-label { + display: block; + margin-bottom: 0.5rem; + font-weight: 600; + color: var(--text-color); + font-size: 0.9375rem; + transition: color var(--transition-fast); +} + +/* Floating labels */ +.form-floating { + position: relative; +} + +.form-floating > .form-control, +.form-floating > .form-select { + height: calc(3.5rem + 2px); + padding: 1.625rem 0.75rem 0.625rem; +} + +.form-floating > label { + position: absolute; + top: 0; + left: 0; + height: 100%; + padding: 1rem 0.75rem; + pointer-events: none; + border: 1px solid transparent; + transform-origin: 0 0; + transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; +} + +.form-floating > .form-control:focus ~ label, +.form-floating > .form-control:not(:placeholder-shown) ~ label, +.form-floating > .form-select ~ label { + opacity: 0.65; + transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); +} + +/* Input styling */ +.form-control { + display: block; + width: 100%; + padding: 0.75rem 1rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: var(--text-color); + background-color: #fff; + background-clip: padding-box; + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + transition: border-color var(--transition-fast), box-shadow var(--transition-fast); +} + +.form-control:focus { + border-color: var(--primary-color); + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(67, 97, 238, 0.25); +} + +.form-control::placeholder { + color: var(--lighter-text); + opacity: 1; +} + +/* Select styling */ +.form-select { + display: block; + width: 100%; + padding: 0.75rem 2.25rem 0.75rem 1rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: var(--text-color); + background-color: #fff; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right 1rem center; + background-size: 16px 12px; + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + appearance: none; + transition: border-color var(--transition-fast), box-shadow var(--transition-fast); +} + +.form-select:focus { + border-color: var(--primary-color); + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(67, 97, 238, 0.25); +} + +/* Checkbox and radio styling */ +.form-check { + display: block; + min-height: 1.5rem; + padding-left: 1.75em; + margin-bottom: 0.5rem; +} + +.form-check-input { + width: 1.25em; + height: 1.25em; + margin-top: 0.125em; + margin-left: -1.75em; + vertical-align: top; + background-color: #fff; + background-repeat: no-repeat; + background-position: center; + background-size: contain; + border: 1px solid var(--border-color); + appearance: none; + transition: background-color var(--transition-fast), border-color var(--transition-fast); +} + +.form-check-input[type="checkbox"] { + border-radius: 0.25em; +} + +.form-check-input[type="radio"] { + border-radius: 50%; +} + +.form-check-input:checked { + background-color: var(--primary-color); + border-color: var(--primary-color); +} + +.form-check-input:checked[type="checkbox"] { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e"); +} + +.form-check-input:checked[type="radio"] { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); +} + +.form-check-input:focus { + border-color: var(--primary-color); + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(67, 97, 238, 0.25); +} + +/* Switch styling */ +.form-switch { + padding-left: 2.5em; +} + +.form-switch .form-check-input { + width: 2em; + margin-left: -2.5em; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); + background-position: left center; + border-radius: 2em; + transition: background-position 0.15s ease-in-out; +} + +.form-switch .form-check-input:checked { + background-position: right center; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); +} + +/* Required field indicator */ +.required:after { + content: " *"; + color: var(--error-color); + font-weight: bold; +} + +/* Form validation states */ +.was-validated .form-control:valid, .form-control.is-valid { + border-color: var(--success-color); + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2338b000' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.was-validated .form-control:invalid, .form-control.is-invalid { + border-color: var(--error-color); + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23d90429'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23d90429' stroke='none'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.invalid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 0.875em; + color: var(--error-color); +} + +.was-validated :invalid ~ .invalid-feedback, +.is-invalid ~ .invalid-feedback { + display: block; +} + +/* Custom form hint text */ +.form-text { + margin-top: 0.25rem; + font-size: 0.875rem; + color: var(--light-text); +} + +/* Custom form section styling */ +.form-section { + background-color: white; + border-radius: var(--border-radius); + padding: 2rem; + margin-bottom: 2rem; + box-shadow: var(--box-shadow); + border-left: 4px solid var(--primary-color); + transition: transform var(--transition-fast), box-shadow var(--transition-fast); +} + +.form-section:hover { + transform: translateY(-3px); + box-shadow: var(--box-shadow-lg); +} + +.form-section h3 { + border-bottom: 1px solid var(--border-color); + padding-bottom: 0.75rem; + margin-bottom: 1.5rem; + color: var(--primary-color); + font-size: 1.25rem; + font-weight: 600; +} + +/* Custom form action buttons container */ +.form-actions { + margin-top: 2.5rem; + display: flex; + gap: 1rem; + justify-content: flex-end; +} + +/* Input sizes */ +.form-control-lg { + min-height: calc(1.5em + 1rem + 2px); + padding: 0.5rem 1rem; + font-size: 1.25rem; + border-radius: var(--border-radius); +} + +.form-control-sm { + min-height: calc(1.5em + 0.5rem + 2px); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + border-radius: var(--border-radius-sm); +} + +/* Readonly and disabled states */ +.form-control:disabled, .form-control[readonly] { + background-color: var(--neutral-100); + opacity: 1; +} + +/* File input */ +.form-control[type="file"] { + overflow: hidden; +} + +.form-control[type="file"]:not(:disabled):not([readonly]) { + cursor: pointer; +} + +/* Range input */ +.form-range { + width: 100%; + height: 1.5rem; + padding: 0; + background-color: transparent; + appearance: none; +} + +.form-range:focus { + outline: 0; +} + +.form-range:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(67, 97, 238, 0.25); +} + +.form-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + background-color: var(--primary-color); + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; +} + +.form-range::-webkit-slider-thumb:active { + background-color: var(--primary-hover); +} + +.form-range::-webkit-slider-runnable-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: var(--neutral-200); + border-color: transparent; + border-radius: 1rem; +} + +/* Responsive adjustments */ +@media (max-width: 992px) { + .form-row { + flex-direction: column; + } + + .form-col { + padding-right: 0; + padding-left: 0; + } +} + +@media (max-width: 768px) { + .form-section { + padding: 1.5rem; + } + + .form-actions { + flex-direction: column; + } +} + +@media (max-width: 480px) { + .form-section { + padding: 1.25rem; + } + + .form-control, .form-select { + font-size: 16px; /* Prevents iOS zoom on focus */ + } +} diff --git a/server/src/main/resources/static/css/main.css b/server/src/main/resources/static/css/main.css new file mode 100644 index 00000000..357248bb --- /dev/null +++ b/server/src/main/resources/static/css/main.css @@ -0,0 +1,21 @@ +/* Main CSS file that imports all other CSS files */ + +/* Import variables first */ +@import url('variables.css'); + +/* Base styles */ +@import url('base.css'); + +/* Component styles */ +@import url('navigation.css'); +@import url('forms.css'); +@import url('buttons.css'); +@import url('tables.css'); +@import url('footer.css'); + +/* Special effects and animations */ +@import url('effects.css'); + +/* Page-specific styles */ +@import url('tournament.css'); +@import url('admin.css'); diff --git a/server/src/main/resources/static/css/navigation.css b/server/src/main/resources/static/css/navigation.css new file mode 100644 index 00000000..c28a5d7c --- /dev/null +++ b/server/src/main/resources/static/css/navigation.css @@ -0,0 +1,286 @@ +/* Modern navigation styles with enhanced UX */ + +/* Main navbar styling */ +.navbar { + padding: 1rem 0; + box-shadow: var(--box-shadow); + transition: all var(--transition-fast); + position: sticky; + top: 0; + z-index: 1030; +} + +.navbar-scrolled { + padding: 0.5rem 0; + box-shadow: var(--box-shadow-lg); + backdrop-filter: blur(10px); + background-color: rgba(255, 255, 255, 0.95); +} + +/* Enhanced navbar brand */ +.navbar-brand { + font-weight: 700; + font-size: 1.5rem; + color: white; + display: flex; + align-items: center; + letter-spacing: -0.01em; + position: relative; + padding: 0.5rem 0; +} + +.navbar-brand:hover { + color: rgba(255, 255, 255, 0.9); +} + +.navbar-brand i { + margin-right: 0.75rem; + font-size: 1.25em; + transition: transform var(--transition-fast); +} + +.navbar-brand:hover i { + transform: scale(1.1) rotate(-5deg); +} + +/* Navbar toggler animation */ +.navbar-toggler { + border: none; + padding: 0.5rem; + transition: transform var(--transition-fast); +} + +.navbar-toggler:focus { + box-shadow: none; + outline: none; +} + +.navbar-toggler:hover { + transform: scale(1.05); +} + +.navbar-toggler-icon { + transition: transform var(--transition-fast); +} + +.navbar-toggler:not(.collapsed) .navbar-toggler-icon { + transform: rotate(90deg); +} + +/* Nav links styling */ +.nav-link { + font-weight: 600; + padding: 0.5rem 1rem; + color: rgba(255, 255, 255, 0.85); + position: relative; + transition: color var(--transition-fast); +} + +.nav-link:hover, .nav-link:focus { + color: white; +} + +/* Animated underline effect */ +.nav-link::after { + content: ''; + position: absolute; + width: 0; + height: 2px; + bottom: 0; + left: 50%; + background-color: white; + transition: all var(--transition); + transform: translateX(-50%); + opacity: 0; +} + +.nav-link:hover::after, +.nav-link:focus::after, +.nav-link.active::after { + width: 80%; + opacity: 1; +} + +.nav-link.active { + color: white; + font-weight: 700; +} + +.nav-link i { + margin-right: 0.5rem; + font-size: 1.1em; + transition: transform var(--transition-fast); +} + +.nav-link:hover i { + transform: translateY(-2px); +} + +/* Dropdown menus */ +.dropdown-menu { + border: none; + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + padding: 0.5rem; + margin-top: 0.5rem; + animation: fadeIn 0.2s ease-out; +} + +.dropdown-item { + padding: 0.75rem 1rem; + border-radius: var(--border-radius-sm); + transition: all var(--transition-fast); +} + +.dropdown-item:hover, .dropdown-item:focus { + background-color: var(--neutral-100); + color: var(--primary-color); +} + +.dropdown-item.active { + background-color: var(--primary-color); + color: white; +} + +.dropdown-item i { + margin-right: 0.5rem; + color: var(--primary-color); +} + +.dropdown-item.active i { + color: white; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); } +} + +/* Navbar color variants */ +.navbar-light { + background-color: white; +} + +.navbar-light .navbar-brand, +.navbar-light .nav-link { + color: var(--text-color); +} + +.navbar-light .nav-link:hover, +.navbar-light .nav-link:focus, +.navbar-light .nav-link.active { + color: var(--primary-color); +} + +.navbar-light .nav-link::after { + background-color: var(--primary-color); +} + +.navbar-dark { + background-color: var(--primary-color); +} + +.navbar-dark .navbar-toggler-icon { + filter: brightness(0) invert(1); +} + +/* Custom card styling */ +.card { + border: none; + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + transition: transform var(--transition-fast), box-shadow var(--transition-fast); + overflow: hidden; +} + +.card:hover { + transform: translateY(-5px); + box-shadow: var(--box-shadow-lg); +} + +.card-body { + padding: 2rem; +} + +.card-title { + color: var(--primary-color); + margin-bottom: 1rem; + font-weight: 700; +} + +.card-subtitle { + color: var(--secondary-color); + margin-bottom: 1rem; + font-weight: 600; +} + +.card-img-top { + transition: transform var(--transition); +} + +.card:hover .card-img-top { + transform: scale(1.05); +} + +/* Responsive adjustments */ +@media (max-width: 992px) { + .navbar-collapse { + background-color: var(--primary-color); + border-radius: var(--border-radius); + padding: 1rem; + margin-top: 0.5rem; + box-shadow: var(--box-shadow); + } + + .navbar-light .navbar-collapse { + background-color: white; + } + + .navbar-nav { + padding: 0.5rem 0; + } + + .nav-link { + padding: 0.75rem 1rem; + } + + .nav-link::after { + bottom: auto; + top: 0; + height: 100%; + width: 3px; + left: 0; + transform: none; + } + + .nav-link:hover::after, + .nav-link:focus::after, + .nav-link.active::after { + width: 3px; + height: 80%; + } +} + +@media (max-width: 768px) { + .navbar-brand { + font-size: 1.25rem; + } + + .card-body { + padding: 1.5rem; + } +} + +@media (max-width: 480px) { + .navbar { + padding: 0.75rem 0; + } + + .navbar-brand i { + margin-right: 0.5rem; + } + + .card-body { + padding: 1.25rem; + } +} diff --git a/server/src/main/resources/static/css/tables.css b/server/src/main/resources/static/css/tables.css new file mode 100644 index 00000000..d40e3410 --- /dev/null +++ b/server/src/main/resources/static/css/tables.css @@ -0,0 +1,289 @@ +/* Modern table styles with enhanced UX */ + +/* Base table styling */ +.table { + width: 100%; + margin-bottom: 1.5rem; + color: var(--text-color); + vertical-align: middle; + border-color: var(--border-color); + --bs-table-striped-bg: var(--neutral-50); + --bs-table-striped-color: var(--text-color); + --bs-table-active-bg: var(--neutral-100); + --bs-table-active-color: var(--text-color); + --bs-table-hover-bg: var(--neutral-100); + --bs-table-hover-color: var(--text-color); + border-collapse: separate; + border-spacing: 0; +} + +/* Enhanced table header */ +.table thead { + position: relative; +} + +.table thead th { + background-color: var(--neutral-100); + font-weight: 700; + border-top: none; + border-bottom: 2px solid var(--primary-color); + padding: 1rem; + text-transform: uppercase; + font-size: 0.875rem; + letter-spacing: 0.05em; + color: var(--primary-color); + vertical-align: bottom; + position: sticky; + top: 0; + z-index: 10; +} + +/* Table body styling */ +.table tbody td { + padding: 1rem; + vertical-align: middle; + border-bottom: 1px solid var(--border-color); + transition: background-color var(--transition-fast); +} + +/* Custom table hover effect */ +.table-hover tbody tr { + transition: all var(--transition-fast); +} + +.table-hover tbody tr:hover { + background-color: rgba(67, 97, 238, 0.05); + transform: translateY(-2px); + box-shadow: var(--box-shadow-sm); +} + +/* Striped table */ +.table-striped > tbody > tr:nth-of-type(odd) > * { + background-color: var(--neutral-50); +} + +/* Bordered table */ +.table-bordered { + border: 1px solid var(--border-color); +} + +.table-bordered th, +.table-bordered td { + border: 1px solid var(--border-color); +} + +/* Custom modern table */ +.table-modern { + border-radius: var(--border-radius); + overflow: hidden; + box-shadow: var(--box-shadow); + border: 1px solid var(--border-color); +} + +.table-modern thead th { + background-color: var(--primary-color); + color: white; + border-bottom: none; +} + +.table-modern thead th:first-child { + border-top-left-radius: var(--border-radius); +} + +.table-modern thead th:last-child { + border-top-right-radius: var(--border-radius); +} + +.table-modern tbody tr:last-child td:first-child { + border-bottom-left-radius: var(--border-radius); +} + +.table-modern tbody tr:last-child td:last-child { + border-bottom-right-radius: var(--border-radius); +} + +/* Table with cards for mobile */ +.table-responsive-card { + overflow-x: auto; +} + +@media (max-width: 768px) { + .table-responsive-card table, + .table-responsive-card thead, + .table-responsive-card tbody, + .table-responsive-card th, + .table-responsive-card td, + .table-responsive-card tr { + display: block; + } + + .table-responsive-card thead tr { + position: absolute; + top: -9999px; + left: -9999px; + } + + .table-responsive-card tr { + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + margin-bottom: 1rem; + box-shadow: var(--box-shadow-sm); + background-color: white; + } + + .table-responsive-card td { + border: none; + border-bottom: 1px solid var(--border-color); + position: relative; + padding-left: 50%; + text-align: right; + } + + .table-responsive-card td:last-child { + border-bottom: none; + } + + .table-responsive-card td:before { + position: absolute; + top: 1rem; + left: 1rem; + width: 45%; + padding-right: 10px; + white-space: nowrap; + text-align: left; + font-weight: 600; + content: attr(data-label); + } +} + +/* Table with horizontal scroll for mobile */ +.table-responsive { + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} + +/* Table caption */ +.table caption { + padding: 0.5rem; + caption-side: bottom; + text-align: center; + color: var(--light-text); + font-style: italic; +} + +/* Table with actions */ +.table .actions { + display: flex; + gap: 0.5rem; + justify-content: flex-end; +} + +.table .actions .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; +} + +/* Table with status indicators */ +.table .status-indicator { + width: 0.75rem; + height: 0.75rem; + border-radius: 50%; + display: inline-block; + margin-right: 0.5rem; +} + +.table .status-active { + background-color: var(--success-color); +} + +.table .status-pending { + background-color: var(--warning-color); +} + +.table .status-inactive { + background-color: var(--error-color); +} + +/* Modern list styles */ +.list-modern { + list-style: none; + padding-left: 0; + margin-bottom: 1.5rem; +} + +.list-modern li { + padding: 1rem; + margin-bottom: 0.5rem; + background-color: white; + border-radius: var(--border-radius); + box-shadow: var(--box-shadow-sm); + transition: all var(--transition-fast); + border-left: 3px solid var(--primary-color); +} + +.list-modern li:hover { + transform: translateX(5px); + box-shadow: var(--box-shadow); +} + +/* Competition list specific styles */ +.competition-list { + margin: 0; + padding-left: 0; + list-style-type: none; +} + +.competition-list li { + margin-bottom: 0.75rem; + position: relative; + padding: 0.75rem 0.75rem 0.75rem 1.5rem; + border-radius: var(--border-radius-sm); + transition: all var(--transition-fast); + background-color: var(--neutral-50); +} + +.competition-list li:hover { + background-color: white; + box-shadow: var(--box-shadow-sm); + transform: translateX(3px); +} + +.competition-list li:before { + content: ""; + position: absolute; + left: 0.5rem; + top: 50%; + transform: translateY(-50%); + width: 0.5rem; + height: 0.5rem; + background-color: var(--primary-color); + border-radius: 50%; +} + +/* Feature list */ +.feature-list { + list-style: none; + padding-left: 0; +} + +.feature-list li { + padding-left: 2rem; + position: relative; + margin-bottom: 1rem; +} + +.feature-list li:before { + content: "✓"; + position: absolute; + left: 0; + top: 0; + width: 1.5rem; + height: 1.5rem; + background-color: var(--primary-color); + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.875rem; +} diff --git a/server/src/main/resources/static/css/tournament.css b/server/src/main/resources/static/css/tournament.css new file mode 100644 index 00000000..c458b351 --- /dev/null +++ b/server/src/main/resources/static/css/tournament.css @@ -0,0 +1,481 @@ +/* Modern tournament styles with enhanced UX */ + +/* Tournament list container */ +.tournament-list { + max-width: 900px; + margin: 0 auto; + padding: 1rem 0; +} + +/* Tournament item card */ +.tournament-item { + border-radius: var(--border-radius-lg); + transition: all var(--transition); + margin-bottom: 2rem; + overflow: hidden; + background-color: white; + border: 1px solid var(--border-color); + box-shadow: var(--box-shadow); + position: relative; + padding: 1.5rem; +} + +.tournament-item:hover { + transform: translateY(-8px); + box-shadow: var(--box-shadow-lg); +} + +.tournament-item:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 5px; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + opacity: 0; + transition: opacity var(--transition); +} + +.tournament-item:hover:before { + opacity: 1; +} + +/* Tournament header */ +.tournament-header { + display: flex; + flex-direction: column; + margin-bottom: 1.5rem; +} + +.tournament-header h3 { + color: var(--primary-color); + font-size: 1.6rem; + font-weight: 700; + margin-bottom: 1rem; + position: relative; + display: inline-block; +} + +.tournament-header h3:after { + content: ''; + position: absolute; + bottom: -0.5rem; + left: 0; + width: 50px; + height: 3px; + background-color: var(--accent-color); + border-radius: 3px; + transition: width var(--transition); +} + +.tournament-item:hover .tournament-header h3:after { + width: 100px; +} + +.tournament-meta { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-top: 0.5rem; +} + +.tournament-header p { + color: var(--light-text); + font-size: 1rem; + margin: 0; + display: flex; + align-items: center; +} + +.tournament-header p i { + color: var(--primary-color); + width: 1.5rem; + height: 1.5rem; + text-align: center; + margin-right: 0.5rem; + background-color: rgba(67, 97, 238, 0.1); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.8rem; + transition: transform var(--transition-fast); +} + +.tournament-item:hover .tournament-header p i { + transform: scale(1.2); +} + +/* Tournament competitions section */ +.tournament-competitions { + background-color: var(--neutral-50); + border-radius: var(--border-radius); + padding: 1.25rem; + margin-bottom: 1.5rem; + transition: all var(--transition-fast); +} + +.tournament-item:hover .tournament-competitions { + background-color: var(--neutral-100); +} + +.tournament-competitions h4 { + font-size: 1.1rem; + color: var(--secondary-color); + margin-top: 0; + margin-bottom: 1rem; + font-weight: 600; + display: flex; + align-items: center; +} + +.tournament-competitions h4:before { + content: '\f091'; /* Trophy icon */ + font-family: 'Font Awesome 5 Free', serif; + font-weight: 900; + margin-right: 0.5rem; + font-size: 0.9em; + color: var(--accent-color); +} + +.tournament-competitions ul { + list-style: none; + padding-left: 0; + margin-bottom: 0; +} + +.tournament-competitions li { + padding: 0.5rem 0; + border-bottom: 1px dashed var(--border-color); + transition: transform var(--transition-fast); + padding-left: 1.5rem; + position: relative; +} + +.tournament-competitions li:last-child { + border-bottom: none; +} + +.tournament-competitions li:before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 8px; + height: 8px; + background-color: var(--primary-color); + border-radius: 50%; +} + +.tournament-competitions li:hover { + transform: translateX(5px); +} + +/* Tournament actions */ +.tournament-actions { + margin-top: 1.5rem; + text-align: right; +} + +.tournament-actions .button { + position: relative; + overflow: hidden; + z-index: 1; +} + +.tournament-actions .button:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + z-index: -1; + transition: opacity var(--transition); + opacity: 0; +} + +.tournament-actions .button:hover:before { + opacity: 1; +} + +/* Tournament info for registration page */ +.tournament-info { + margin-bottom: 3rem; + text-align: center; + position: relative; + padding-bottom: 2rem; +} + +.tournament-info:after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 100px; + height: 3px; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + border-radius: 3px; +} + +.tournament-info h2 { + color: var(--primary-color); + margin-bottom: 0.75rem; + font-weight: 700; + font-size: 2.2rem; +} + +.tournament-info p { + color: var(--light-text); + font-size: 1.1rem; + margin-bottom: 0.5rem; +} + +/* Registration form styles */ +.registration-form { + max-width: 800px; + margin: 0 auto; +} + +.competitions-list { + width: 100%; + margin: 0; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 1.5rem; +} + +/* Competition item */ +.competition-item { + padding: 1.25rem; + border-radius: var(--border-radius); + transition: all var(--transition-fast); + border: 1px solid var(--border-color); + background-color: white; + box-shadow: var(--box-shadow-sm); + height: 100%; + display: flex; + flex-direction: column; +} + +.competition-item:hover { + background-color: rgba(67, 97, 238, 0.03); + box-shadow: var(--box-shadow); + transform: translateY(-5px); + border-color: var(--primary-color); +} + +/* Custom form check styling */ +.competition-item .form-check { + display: flex; + align-items: flex-start; + padding-left: 0; + margin-bottom: 0; + height: 100%; +} + +.competition-item .form-check-input { + margin-right: 1rem; + margin-top: 0.25rem; + float: none; + flex-shrink: 0; + width: 1.25rem; + height: 1.25rem; + cursor: pointer; +} + +.competition-details { + flex-grow: 1; +} + +.competition-details strong { + color: var(--primary-color); + font-weight: 700; + font-size: 1.1rem; + display: block; + margin-bottom: 0.5rem; +} + +/* Participant details section */ +.participant-details { + width: 100%; +} + +.participant-details .form-label { + font-weight: 600; + margin-bottom: 0.5rem; + color: var(--text-color); +} + +/* Custom form spacing */ +.registration-form .mb-3:last-child { + margin-bottom: 0 !important; +} + +/* Confirmation page styles */ +.confirmation-box { + max-width: 700px; + margin: 0 auto; + text-align: center; + background-color: white; + border-radius: var(--border-radius-lg); + padding: 3rem 2rem; + box-shadow: var(--box-shadow); + position: relative; + overflow: hidden; +} + +.confirmation-box:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 8px; + background: linear-gradient(90deg, var(--success-color), var(--primary-color)); +} + +/* Success icon styling */ +.confirmation-icon { + font-size: 4rem; + color: var(--success-color); + margin-bottom: 2rem; + position: relative; + display: inline-block; +} + +.confirmation-icon i { + background-color: rgba(56, 176, 0, 0.1); + border-radius: 50%; + padding: 1.5rem; + display: inline-block; + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(56, 176, 0, 0.4); + } + 70% { + box-shadow: 0 0 0 15px rgba(56, 176, 0, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(56, 176, 0, 0); + } +} + +.confirmation-box h2 { + color: var(--text-color); + font-size: 2rem; + margin-bottom: 1.5rem; + font-weight: 700; +} + +/* Confirmation details card */ +.confirmation-details { + margin: 2rem 0; + background-color: var(--neutral-50); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow-sm); + overflow: hidden; + border: 1px solid var(--border-color); +} + +/* Detail items as list group */ +.detail-item { + display: flex; + padding: 1rem 1.5rem; + border-bottom: 1px solid var(--border-color); + transition: background-color var(--transition-fast); +} + +.detail-item:hover { + background-color: white; +} + +.detail-item:last-child { + border-bottom: none; +} + +.detail-label { + font-weight: 700; + width: 120px; + color: var(--secondary-color); + text-transform: uppercase; + font-size: 0.85rem; + letter-spacing: 0.05em; +} + +.detail-value { + flex: 1; + font-weight: 500; +} + +/* Confirmation messages */ +.confirmation-message { + margin: 1.5rem 0; + color: var(--text-color); + font-size: 1.2rem; + font-weight: 500; +} + +/* Action buttons container */ +.confirmation-actions { + margin-top: 2.5rem; + display: flex; + justify-content: center; + gap: 1.5rem; +} + +/* Responsive adjustments */ +@media (max-width: 992px) { + .competitions-list { + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + } +} + +@media (max-width: 768px) { + .tournament-header { + flex-direction: column; + } + + .tournament-meta { + flex-direction: column; + gap: 0.5rem; + } + + .competitions-list { + grid-template-columns: 1fr; + } + + .confirmation-box { + padding: 2rem 1.5rem; + } +} + +@media (max-width: 576px) { + .tournament-item { + padding: 1.25rem; + } + + .tournament-header h3 { + font-size: 1.4rem; + } + + .confirmation-actions { + flex-direction: column; + } + + .detail-item { + flex-direction: column; + } + + .detail-label { + width: 100%; + margin-bottom: 0.5rem; + } +} diff --git a/server/src/main/resources/static/css/variables.css b/server/src/main/resources/static/css/variables.css new file mode 100644 index 00000000..676a2d50 --- /dev/null +++ b/server/src/main/resources/static/css/variables.css @@ -0,0 +1,45 @@ +:root { + /* Modern color palette */ + --primary-color: #4361ee; + --primary-hover: #3a56d4; + --secondary-color: #7209b7; + --secondary-hover: #6008a0; + --accent-color: #f72585; + --text-color: #2b2d42; + --light-text: #6c757d; + --lighter-text: #adb5bd; + --border-color: #e9ecef; + --light-bg: #f8f9fa; + --container-bg: #fff; + --success-color: #38b000; + --warning-color: #ffbe0b; + --error-color: #d90429; + --info-color: #4cc9f0; + + /* Neutral shades for backgrounds */ + --neutral-50: #fafafa; + --neutral-100: #f5f5f5; + --neutral-200: #e5e5e5; + --neutral-300: #d4d4d4; + + /* Design tokens */ + --border-radius-sm: 0.25rem; + --border-radius: 0.5rem; + --border-radius-lg: 1rem; + --border-radius-xl: 1.5rem; + --box-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); + --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + --box-shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); + --transition-fast: 150ms ease; + --transition: 300ms ease; + --transition-slow: 500ms ease; + + /* Spacing */ + --spacer: 1rem; + --spacer-1: 0.25rem; + --spacer-2: 0.5rem; + --spacer-3: 1rem; + --spacer-4: 1.5rem; + --spacer-5: 3rem; + --spacer-6: 4.5rem; +}