From 3f3725d2779a880ad8915cf2329065b90ebd5442 Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Wed, 4 Feb 2026 12:37:05 +1100 Subject: [PATCH] Improve look of SQL explorer page, and improve validation of exercise selection in workouts --- routes/workout.py | 10 + static/css/style.css | 159 +++++++--- templates/partials/new_set_form.html | 15 +- templates/partials/sql_explorer/results.html | 69 ++-- templates/partials/sql_explorer/schema.html | 76 +++-- .../partials/sql_explorer/sql_query.html | 297 +++++++++--------- templates/sql_explorer.html | 69 +++- utils.py | 30 +- 8 files changed, 464 insertions(+), 261 deletions(-) diff --git a/routes/workout.py b/routes/workout.py index 4ac42d8..1c964c6 100644 --- a/routes/workout.py +++ b/routes/workout.py @@ -226,6 +226,16 @@ def create_topset(person_id, workout_id): exercise_id = request.form.get("exercise_id") repetitions = request.form.get("repetitions") weight = request.form.get("weight") + + # Validation: Ensure exercise_id is present and is a valid integer + if not exercise_id or not exercise_id.strip(): + return "Please select an exercise.", 400 + + try: + exercise_id = int(exercise_id) + except ValueError: + return "Invalid exercise selection.", 400 + new_topset_id = db.create_topset(workout_id, exercise_id, repetitions, weight) exercise = db.get_exercise(exercise_id) db.activityRequest.log(current_user.id, 'ADD_SET', 'topset', new_topset_id, f"Added set: {repetitions} x {weight}kg {exercise['name']} in workout {workout_id}") diff --git a/static/css/style.css b/static/css/style.css index 0317b06..20c2702 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -10,7 +10,7 @@ tr.htmx-swapping td { bottom: 0px; left: 0px; right: 0px; - background-color: rgba(0,0,0,0.9); + background-color: rgba(0, 0, 0, 0.9); z-index: 1000; /* Flexbox centers the .modal-content vertically and horizontally */ display: flex; @@ -22,42 +22,42 @@ tr.htmx-swapping td { animation-timing-function: ease; } - #modal > .modal-underlay { - /* underlay takes up the entire viewport. This is only +#modal>.modal-underlay { + /* underlay takes up the entire viewport. This is only required if you want to click to dismiss the popup */ - position: absolute; - z-index: -1; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - } + position: absolute; + z-index: -1; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; +} - #modal > .modal-content { - /* Display properties for visible dialog*/ - border: solid 1px #999; - border-radius: 8px; - box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.3); - background-color: white; - /* Animate when opening */ - animation-name: zoomIn; - animation-duration: 150ms; - animation-timing-function: ease; - } +#modal>.modal-content { + /* Display properties for visible dialog*/ + border: solid 1px #999; + border-radius: 8px; + box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.3); + background-color: white; + /* Animate when opening */ + animation-name: zoomIn; + animation-duration: 150ms; + animation-timing-function: ease; +} - #modal.closing { - /* Animate when closing */ - animation-name: fadeOut; - animation-duration: 150ms; - animation-timing-function: ease; - } +#modal.closing { + /* Animate when closing */ + animation-name: fadeOut; + animation-duration: 150ms; + animation-timing-function: ease; +} - #modal.closing > .modal-content { - /* Aniate when closing */ - animation-name: zoomOut; - animation-duration: 150ms; - animation-timing-function: ease; - } +#modal.closing>.modal-content { + /* Aniate when closing */ + animation-name: zoomOut; + animation-duration: 150ms; + animation-timing-function: ease; +} @keyframes fadeIn { 0% { @@ -99,20 +99,29 @@ tr.htmx-swapping td { } } -.loading-indicator{ - display:none; +.loading-indicator { + display: none; } -.htmx-request .loading-indicator{ - display:flex; + +.htmx-request .loading-indicator { + display: flex; } -.htmx-request.loading-indicator{ - display:flex; + +.htmx-request.loading-indicator { + display: flex; } @keyframes slideInLeft { - from { transform: translateX(-100%); opacity: 0; } - to { transform: translateX(0); opacity: 1; } + from { + transform: translateX(-100%); + opacity: 0; + } + + to { + transform: translateX(0); + opacity: 1; + } } .animate-slideInLeft { @@ -122,12 +131,76 @@ tr.htmx-swapping td { } @keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } + from { + opacity: 0; + } + + to { + opacity: 1; + } } + .animate-fadeIn { animation-name: fadeIn; animation-duration: 0.5s; animation-fill-mode: both; } + +/* SQL Explorer Custom Styles */ +.sql-editor-container { + background: #1e1e1e; + border-radius: 0.5rem; + padding: 0.5rem; + box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06); +} + +.sql-editor-textarea { + background: transparent !important; + color: #dcdcdc !important; + font-family: 'Fira Code', 'Courier New', Courier, monospace; + line-height: 1.5; + tab-size: 4; + border: none !important; + outline: none !important; + width: 100%; +} + +.glass-card { + background: rgba(255, 255, 255, 0.7); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.3); +} + +.btn-premium { + position: relative; + overflow: hidden; + transition: all 0.3s ease; +} + +.btn-premium::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, + transparent, + rgba(255, 255, 255, 0.2), + transparent); + transition: 0.5s; +} + +.btn-premium:hover::after { + left: 100%; +} + +.table-zebra tbody tr:nth-child(even) { + background-color: rgba(243, 244, 246, 0.5); +} + +.table-zebra tbody tr:hover { + background-color: rgba(229, 231, 235, 0.8); +} \ No newline at end of file diff --git a/templates/partials/new_set_form.html b/templates/partials/new_set_form.html index 6d1519a..6ba4642 100644 --- a/templates/partials/new_set_form.html +++ b/templates/partials/new_set_form.html @@ -1,13 +1,24 @@
+ +