Make dashboard, person, settings page htmx driven
This commit is contained in:
16
app.py
16
app.py
@@ -4,11 +4,13 @@ import jinja_partials
|
|||||||
from decorators import validate_person, validate_topset, validate_workout
|
from decorators import validate_person, validate_topset, validate_workout
|
||||||
from db import DataBase
|
from db import DataBase
|
||||||
from utils import get_people_and_exercise_rep_maxes
|
from utils import get_people_and_exercise_rep_maxes
|
||||||
|
from flask_htmx import HTMX
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py')
|
||||||
jinja_partials.register_extensions(app)
|
jinja_partials.register_extensions(app)
|
||||||
db = DataBase(app)
|
db = DataBase(app)
|
||||||
|
htmx = HTMX(app)
|
||||||
|
|
||||||
|
|
||||||
@ app.route("/")
|
@ app.route("/")
|
||||||
@@ -16,7 +18,10 @@ def dashboard():
|
|||||||
all_topsets = db.get_all_topsets()
|
all_topsets = db.get_all_topsets()
|
||||||
people_and_exercise_rep_maxes = get_people_and_exercise_rep_maxes(
|
people_and_exercise_rep_maxes = get_people_and_exercise_rep_maxes(
|
||||||
all_topsets)
|
all_topsets)
|
||||||
return render_template('index.html', model=people_and_exercise_rep_maxes)
|
if htmx:
|
||||||
|
return render_template('partials/page/dashboard.html',
|
||||||
|
model=people_and_exercise_rep_maxes), 200, {"HX-Trigger": "updatedPeople"}
|
||||||
|
return render_template('dashboard.html', model=people_and_exercise_rep_maxes)
|
||||||
|
|
||||||
|
|
||||||
@ app.route("/person/list", methods=['GET'])
|
@ app.route("/person/list", methods=['GET'])
|
||||||
@@ -29,10 +34,14 @@ def get_person_list():
|
|||||||
@ validate_person
|
@ validate_person
|
||||||
def get_person(person_id):
|
def get_person(person_id):
|
||||||
person = db.get_person(person_id)
|
person = db.get_person(person_id)
|
||||||
|
if htmx:
|
||||||
|
return render_template('partials/page/person.html',
|
||||||
|
person=person, is_filtered=False), 200, {"HX-Trigger": "updatedPeople"}
|
||||||
|
|
||||||
return render_template('person.html', person=person)
|
return render_template('person.html', person=person)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/person/<int:person_id>/exercise_filter", methods=['POST'])
|
@ app.route("/person/<int:person_id>/exercise_filter", methods=['POST'])
|
||||||
@ validate_person
|
@ validate_person
|
||||||
def filter_exercises_for_person(person_id):
|
def filter_exercises_for_person(person_id):
|
||||||
selected_exercise_ids = [int(i)
|
selected_exercise_ids = [int(i)
|
||||||
@@ -204,6 +213,9 @@ def delete_exercise(exercise_id):
|
|||||||
def settings():
|
def settings():
|
||||||
people = db.get_people()
|
people = db.get_people()
|
||||||
exercises = db.get_exercises()
|
exercises = db.get_exercises()
|
||||||
|
if htmx:
|
||||||
|
return render_template('partials/page/settings.html',
|
||||||
|
people=people, exercises=exercises), 200, {"HX-Trigger": "updatedPeople"}
|
||||||
return render_template('settings.html', people=people, exercises=exercises)
|
return render_template('settings.html', people=people, exercises=exercises)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
Flask==2.0.1
|
Flask==2.2.2
|
||||||
gunicorn==19.7.1
|
gunicorn==19.7.1
|
||||||
Jinja2==3.0.1
|
Jinja2==3.0.1
|
||||||
jinja-partials==0.1.1
|
jinja-partials==0.1.1
|
||||||
psycopg2-binary==2.9.3
|
psycopg2-binary==2.9.3
|
||||||
|
flask-htmx==0.2.0
|
||||||
@@ -75,8 +75,8 @@
|
|||||||
<div class="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
|
<div class="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
|
||||||
<div class="flex-1 px-3 bg-white divide-y space-y-1">
|
<div class="flex-1 px-3 bg-white divide-y space-y-1">
|
||||||
<div class="space-y-2 pt-2">
|
<div class="space-y-2 pt-2">
|
||||||
<a href="{{ url_for('dashboard') }}"
|
<a hx-get="{{ url_for('dashboard') }}" hx-push-url="true" hx-target="#container"
|
||||||
class="text-base text-gray-900 font-normal rounded-lg flex items-center p-2 hover:bg-gray-100 group {{ is_selected_page(url_for('dashboard')) }}">
|
class="text-base text-gray-900 font-normal rounded-lg flex items-center p-2 hover:bg-gray-100 group cursor-pointer {{ is_selected_page(url_for('dashboard')) }}">
|
||||||
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
||||||
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"></path>
|
<path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"></path>
|
||||||
@@ -92,8 +92,8 @@
|
|||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
<div class="space-y-2 pt-2">
|
<div class="space-y-2 pt-2">
|
||||||
<a href="{{ url_for('settings') }}"
|
<a hx-get="{{ url_for('settings') }}" hx-push-url="true" hx-target="#container"
|
||||||
class="text-base text-gray-900 font-normal rounded-lg hover:bg-gray-100 group transition duration-75 flex items-center p-2 {{ is_selected_page(url_for('settings')) }}">
|
class="text-base text-gray-900 font-normal rounded-lg hover:bg-gray-100 group transition duration-75 flex items-center p-2 cursor-pointer {{ is_selected_page(url_for('settings')) }}">
|
||||||
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
||||||
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
|
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
|
||||||
data-darkreader-inline-fill="" style="--darkreader-inline-fill:currentColor;">
|
data-darkreader-inline-fill="" style="--darkreader-inline-fill:currentColor;">
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
</aside>
|
</aside>
|
||||||
<div class="h-full w-full bg-gray-50 relative overflow-y-auto lg:ml-64">
|
<div class="h-full w-full bg-gray-50 relative overflow-y-auto lg:ml-64">
|
||||||
<main>
|
<main>
|
||||||
<div class="pt-6 px-4">
|
<div class="pt-6 px-4" id="container">
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
8
templates/dashboard.html
Normal file
8
templates/dashboard.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{{ render_partial('partials/page/dashboard.html',
|
||||||
|
people=people, exercises=exercises) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let layout = {
|
let layout = {
|
||||||
margin: { t: 0 }, xaxis: { type: 'date', showgrid: false }, yaxis: {
|
margin: { t: 0 }, xaxis: { type: 'date', showgrid: false }, yaxis: {
|
||||||
@@ -92,6 +88,4 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ render_partial('partials/stats.html', stats=model['Stats']) }}
|
{{ render_partial('partials/stats.html', stats=model['Stats']) }}
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<div id="person-view">
|
<div>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="bg-white shadow rounded-lg p-4 sm:w-full xl:p-8 md:w-full lg:w-10/12 xl:w-8/12 2xl:w-6/12">
|
<div class="bg-white shadow rounded-lg p-4 sm:w-full xl:p-8 md:w-full lg:w-10/12 xl:w-8/12 2xl:w-6/12">
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
{% for e in person['Exercises'] %}
|
{% for e in person['Exercises'] %}
|
||||||
<div class="flex items-center mr-4"
|
<div class="flex items-center mr-4"
|
||||||
hx-post="{{ url_for('filter_exercises_for_person', person_id=person['PersonId']) }}"
|
hx-post="{{ url_for('filter_exercises_for_person', person_id=person['PersonId']) }}"
|
||||||
hx-include="[name='exercise_id']" hx-target="#person-view" hx-swap="outerHTML">
|
hx-include="[name='exercise_id']" hx-target="#container" hx-swap="outerHTML">
|
||||||
<input {{ is_checked(e['ExerciseId'], selected_exercise_ids) }} type="checkbox" name="exercise_id"
|
<input {{ is_checked(e['ExerciseId'], selected_exercise_ids) }} type="checkbox" name="exercise_id"
|
||||||
value="{{ e['ExerciseId'] }}"
|
value="{{ e['ExerciseId'] }}"
|
||||||
class="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
class="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
||||||
|
|||||||
133
templates/partials/page/settings.html
Normal file
133
templates/partials/page/settings.html
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
<div class="mt-4 w-full grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 gap-4">
|
||||||
|
<div class="bg-white shadow rounded-lg p-4 sm:p-6 xl:p-8 ">
|
||||||
|
<div class="mb-4 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-xl font-bold text-gray-900 mb-2">Users</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="overflow-x-auto rounded-lg">
|
||||||
|
<div class="align-middle inline-block min-w-full">
|
||||||
|
<div class="shadow overflow-hidden sm:rounded-lg">
|
||||||
|
<table class="table-fixed min-w-full divide-y divide-gray-200">
|
||||||
|
<thead class="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th scope="col"
|
||||||
|
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-3/5">
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
<th scope="col"
|
||||||
|
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="bg-white" id="new-person" hx-target="closest tr"
|
||||||
|
hx-swap="outerHTML swap:0.5s">
|
||||||
|
{% for p in people %}
|
||||||
|
{{ render_partial('partials/person.html', person_id=p['PersonId'],
|
||||||
|
name=p['Name'])}}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="w-full mt-3" hx-post="{{ url_for('create_person') }}" hx-swap="beforeend" hx-target="#new-person">
|
||||||
|
<div class="flex flex-wrap -mx-3 mb-2">
|
||||||
|
<div class="grow px-3">
|
||||||
|
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||||
|
New user
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
|
||||||
|
type="text" name="name">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row pt-6 px-3 w-36">
|
||||||
|
<button
|
||||||
|
class="w-full flex text-white bg-cyan-600 hover:bg-cyan-700 focus:ring-4 focus:ring-cyan-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center items-center h-12"
|
||||||
|
type="submit">
|
||||||
|
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
||||||
|
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
data-darkreader-inline-fill="" style="--darkreader-inline-fill:currentColor;">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z"
|
||||||
|
clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white shadow rounded-lg p-4 sm:p-6 xl:p-8 ">
|
||||||
|
<div class="mb-4 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-xl font-bold text-gray-900 mb-2">Exercises</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="overflow-x-auto rounded-lg">
|
||||||
|
<div class="align-middle inline-block min-w-full">
|
||||||
|
<div class="shadow overflow-hidden sm:rounded-lg">
|
||||||
|
<table class="table-fixed min-w-full divide-y divide-gray-200">
|
||||||
|
<thead class="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th scope="col"
|
||||||
|
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-3/5">
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
<th scope="col"
|
||||||
|
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="bg-white" id="new-exercise" hx-target="closest tr"
|
||||||
|
hx-swap="outerHTML swap:0.5s">
|
||||||
|
{% for e in exercises %}
|
||||||
|
{{ render_partial('partials/exercise.html', exercise_id=e['ExerciseId'],
|
||||||
|
name=e['Name'])}}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="w-full mt-3" hx-post="{{ url_for('create_exercise') }}" hx-swap="beforeend"
|
||||||
|
hx-target="#new-exercise">
|
||||||
|
<div class="flex flex-wrap -mx-3 mb-2">
|
||||||
|
<div class="grow px-3">
|
||||||
|
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||||
|
New exercise
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
|
||||||
|
type="text" name="name">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row pt-6 px-3 w-36">
|
||||||
|
<button
|
||||||
|
class="w-full flex text-white bg-cyan-600 hover:bg-cyan-700 focus:ring-4 focus:ring-cyan-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center items-center h-12 cursor-pointer"
|
||||||
|
type="submit">
|
||||||
|
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
||||||
|
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
data-darkreader-inline-fill="" style="--darkreader-inline-fill:currentColor;">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z"
|
||||||
|
clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{% for p in people %}
|
{% for p in people %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ url_for('get_person' ,person_id=p['PersonId']) }}"
|
<a hx-get="{{ url_for('get_person' ,person_id=p['PersonId']) }}" hx-push-url="true" hx-target="#container" class="text-base text-gray-900 font-normal rounded-lg hover:bg-gray-100 flex items-center p-2 group cursor-pointer {% if
|
||||||
class="text-base text-gray-900 font-normal rounded-lg hover:bg-gray-100 flex items-center p-2 group {% if p['IsActive']==1 %} bg-gray-200 {% endif %}">
|
p['IsActive']==1 %} bg-gray-200 {% endif %}">
|
||||||
<svg class="w-6 h-6 text-gray-500 flex-shrink-0 group-hover:text-gray-900 transition duration-75"
|
<svg class="w-6 h-6 text-gray-500 flex-shrink-0 group-hover:text-gray-900 transition duration-75"
|
||||||
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd">
|
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd">
|
||||||
|
|||||||
@@ -2,139 +2,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="mt-4 w-full grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 gap-4">
|
{{ render_partial('partials/page/settings.html',
|
||||||
<div class="bg-white shadow rounded-lg p-4 sm:p-6 xl:p-8 ">
|
model=model) }}
|
||||||
<div class="mb-4 flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h3 class="text-xl font-bold text-gray-900 mb-2">Users</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div class="overflow-x-auto rounded-lg">
|
|
||||||
<div class="align-middle inline-block min-w-full">
|
|
||||||
<div class="shadow overflow-hidden sm:rounded-lg">
|
|
||||||
<table class="table-fixed min-w-full divide-y divide-gray-200">
|
|
||||||
<thead class="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th scope="col"
|
|
||||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-3/5">
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th scope="col"
|
|
||||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="bg-white" id="new-person" hx-target="closest tr"
|
|
||||||
hx-swap="outerHTML swap:0.5s">
|
|
||||||
{% for p in people %}
|
|
||||||
{{ render_partial('partials/person.html', person_id=p['PersonId'],
|
|
||||||
name=p['Name'])}}
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form class="w-full mt-3" hx-post="{{ url_for('create_person') }}" hx-swap="beforeend" hx-target="#new-person">
|
|
||||||
<div class="flex flex-wrap -mx-3 mb-2">
|
|
||||||
<div class="grow px-3">
|
|
||||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
|
||||||
New user
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
|
|
||||||
type="text" name="name">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row pt-6 px-3 w-36">
|
|
||||||
<button
|
|
||||||
class="w-full flex text-white bg-cyan-600 hover:bg-cyan-700 focus:ring-4 focus:ring-cyan-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center items-center h-12"
|
|
||||||
type="submit">
|
|
||||||
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
|
||||||
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
|
|
||||||
data-darkreader-inline-fill="" style="--darkreader-inline-fill:currentColor;">
|
|
||||||
<path fill-rule="evenodd"
|
|
||||||
d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z"
|
|
||||||
clip-rule="evenodd"></path>
|
|
||||||
</svg>
|
|
||||||
Add
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-white shadow rounded-lg p-4 sm:p-6 xl:p-8 ">
|
|
||||||
<div class="mb-4 flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h3 class="text-xl font-bold text-gray-900 mb-2">Exercises</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div class="overflow-x-auto rounded-lg">
|
|
||||||
<div class="align-middle inline-block min-w-full">
|
|
||||||
<div class="shadow overflow-hidden sm:rounded-lg">
|
|
||||||
<table class="table-fixed min-w-full divide-y divide-gray-200">
|
|
||||||
<thead class="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th scope="col"
|
|
||||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-3/5">
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th scope="col"
|
|
||||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="bg-white" id="new-exercise" hx-target="closest tr"
|
|
||||||
hx-swap="outerHTML swap:0.5s">
|
|
||||||
{% for e in exercises %}
|
|
||||||
{{ render_partial('partials/exercise.html', exercise_id=e['ExerciseId'],
|
|
||||||
name=e['Name'])}}
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form class="w-full mt-3" hx-post="{{ url_for('create_exercise') }}" hx-swap="beforeend"
|
|
||||||
hx-target="#new-exercise">
|
|
||||||
<div class="flex flex-wrap -mx-3 mb-2">
|
|
||||||
<div class="grow px-3">
|
|
||||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
|
||||||
New exercise
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
|
|
||||||
type="text" name="name">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row pt-6 px-3 w-36">
|
|
||||||
<button
|
|
||||||
class="w-full flex text-white bg-cyan-600 hover:bg-cyan-700 focus:ring-4 focus:ring-cyan-200 font-medium rounded-lg text-sm px-5 py-2.5 text-center items-center h-12 cursor-pointer"
|
|
||||||
type="submit">
|
|
||||||
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
|
||||||
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
|
|
||||||
data-darkreader-inline-fill="" style="--darkreader-inline-fill:currentColor;">
|
|
||||||
<path fill-rule="evenodd"
|
|
||||||
d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z"
|
|
||||||
clip-rule="evenodd"></path>
|
|
||||||
</svg>
|
|
||||||
Add
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user