fix SQLite

This commit is contained in:
stefan
2025-06-05 17:34:03 +02:00
parent 77c5809e8a
commit ab1f5d0f7f
16 changed files with 2759 additions and 793 deletions
@@ -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<String>) {
@@ -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")
}
}
@@ -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") {
@@ -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;
}
"""
}
}
}
}
@@ -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" }
}
@@ -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;
}
"""
}
}
}
}