Convert exercise form into htmx driven
This commit is contained in:
83
app.py
83
app.py
@@ -104,14 +104,89 @@ def delete_person(person_id):
|
|||||||
@ app.route("/exercise", methods=['POST'])
|
@ app.route("/exercise", methods=['POST'])
|
||||||
def create_exercise():
|
def create_exercise():
|
||||||
name = request.form.get("name")
|
name = request.form.get("name")
|
||||||
db.create_exercise(name)
|
new_exercise_id = db.create_exercise(name)
|
||||||
return redirect(url_for('settings'))
|
return f"""
|
||||||
|
<tr>
|
||||||
|
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||||
|
{name}
|
||||||
|
</td>
|
||||||
|
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||||
|
<a hx-get="{ url_for('get_exercise_edit_form', exercise_id=new_exercise_id) }" class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
<a hx-delete="{url_for('delete_exercise', exercise_id=new_exercise_id)}" class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
||||||
|
Remove
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@ app.route("/exercise/<int:exercise_id>/delete", methods=['GET', 'POST'])
|
@ app.route("/exercise/<int:exercise_id>", methods=['GET'])
|
||||||
|
def get_exercise(exercise_id):
|
||||||
|
exercise = db.get_exercise(exercise_id)
|
||||||
|
return f"""
|
||||||
|
<tr>
|
||||||
|
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||||
|
{exercise['Name']}
|
||||||
|
</td>
|
||||||
|
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||||
|
<a hx-get="{ url_for('get_exercise_edit_form', exercise_id=exercise['ExerciseId']) }" class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
<a hx-delete="{url_for('delete_exercise', exercise_id=exercise['ExerciseId'])}" class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
||||||
|
Remove
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ app.route("/exercise/<int:exercise_id>/edit_form", methods=['GET'])
|
||||||
|
def get_exercise_edit_form(exercise_id):
|
||||||
|
exercise = db.get_exercise(exercise_id)
|
||||||
|
return f"""
|
||||||
|
<tr>
|
||||||
|
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||||
|
<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" value="{exercise['Name']}">
|
||||||
|
</td>
|
||||||
|
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||||
|
<a hx-put="{ url_for('update_exercise', exercise_id=exercise['ExerciseId']) }" hx-include="closest tr" class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
||||||
|
Update
|
||||||
|
</a>
|
||||||
|
<a hx-get="{url_for('get_exercise', exercise_id=exercise['ExerciseId'])}" class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
||||||
|
Cancel
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ app.route("/exercise/<int:exercise_id>/update", methods=['PUT'])
|
||||||
|
def update_exercise(exercise_id):
|
||||||
|
new_name = request.form.get('name')
|
||||||
|
db.update_exercise(exercise_id, new_name)
|
||||||
|
return f"""
|
||||||
|
<tr>
|
||||||
|
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||||
|
{new_name}
|
||||||
|
</td>
|
||||||
|
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||||
|
<a hx-get="{ url_for('get_exercise_edit_form', exercise_id=exercise_id) }" class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
<a hx-delete="{url_for('delete_exercise', exercise_id=exercise_id)}" class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
||||||
|
Remove
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ app.route("/exercise/<int:exercise_id>/delete", methods=['DELETE'])
|
||||||
def delete_exercise(exercise_id):
|
def delete_exercise(exercise_id):
|
||||||
db.delete_exercise(exercise_id)
|
db.delete_exercise(exercise_id)
|
||||||
return redirect(url_for('settings'))
|
return ""
|
||||||
|
|
||||||
|
|
||||||
@ app.route("/settings")
|
@ app.route("/settings")
|
||||||
|
|||||||
14
db.py
14
db.py
@@ -36,14 +36,24 @@ class DataBase():
|
|||||||
'SELECT ExerciseId AS "ExerciseId", Name AS "Name" FROM Exercise')
|
'SELECT ExerciseId AS "ExerciseId", Name AS "Name" FROM Exercise')
|
||||||
return [{"ExerciseId": e['ExerciseId'], "Name": e['Name']} for e in exercises]
|
return [{"ExerciseId": e['ExerciseId'], "Name": e['Name']} for e in exercises]
|
||||||
|
|
||||||
|
def get_exercise(self, exercise_id):
|
||||||
|
exercise = self.execute(
|
||||||
|
'SELECT ExerciseId AS "ExerciseId", Name AS "Name" FROM Exercise WHERE ExerciseId=%s LIMIT 1', [exercise_id], one=True)
|
||||||
|
return exercise
|
||||||
|
|
||||||
def create_exercise(self, name):
|
def create_exercise(self, name):
|
||||||
self.execute('INSERT INTO Exercise (Name) VALUES (%s)',
|
new_exercise = self.execute('INSERT INTO Exercise (Name) VALUES (%s) RETURNING ExerciseId AS "ExerciseId"',
|
||||||
[name], commit=True)
|
[name], commit=True, one=True)
|
||||||
|
return new_exercise['ExerciseId']
|
||||||
|
|
||||||
def delete_exercise(self, exercise_id):
|
def delete_exercise(self, exercise_id):
|
||||||
self.execute('DELETE FROM Exercise WHERE ExerciseId=%s', [
|
self.execute('DELETE FROM Exercise WHERE ExerciseId=%s', [
|
||||||
exercise_id], commit=True)
|
exercise_id], commit=True)
|
||||||
|
|
||||||
|
def update_exercise(self, exercise_id, name):
|
||||||
|
self.execute('UPDATE Exercise SET Name=%s WHERE ExerciseId=%s', [
|
||||||
|
name, exercise_id], commit=True)
|
||||||
|
|
||||||
def get_people(self):
|
def get_people(self):
|
||||||
people = self.execute(
|
people = self.execute(
|
||||||
'SELECT PersonId AS "PersonId", Name AS "Name" FROM Person')
|
'SELECT PersonId AS "PersonId", Name AS "Name" FROM Person')
|
||||||
|
|||||||
4
static/css/style.css
Normal file
4
static/css/style.css
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
tr.htmx-swapping td {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s ease-out;
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
<script src="https://unpkg.com/htmx.org"></script>
|
<script src="https://unpkg.com/htmx.org"></script>
|
||||||
<script src="https://unpkg.com/hyperscript.org"></script>
|
<script src="https://unpkg.com/hyperscript.org"></script>
|
||||||
|
|
||||||
|
<link href="/static/css/style.css" rel="stylesheet">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function debounce(func, timeout = 300) {
|
function debounce(func, timeout = 300) {
|
||||||
let timer;
|
let timer;
|
||||||
|
|||||||
@@ -97,10 +97,6 @@
|
|||||||
<table class="table-fixed min-w-full divide-y divide-gray-200">
|
<table class="table-fixed min-w-full divide-y divide-gray-200">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-50">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col"
|
|
||||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-0.5">
|
|
||||||
#
|
|
||||||
</th>
|
|
||||||
<th scope="col"
|
<th scope="col"
|
||||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-3/5">
|
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-3/5">
|
||||||
Name
|
Name
|
||||||
@@ -110,17 +106,19 @@
|
|||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="bg-white">
|
<tbody class="bg-white" id="new-exercise" hx-target="closest tr"
|
||||||
|
hx-swap="outerHTML swap:0.5s">
|
||||||
{% for e in exercises %}
|
{% for e in exercises %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="p-4 whitespace-nowrap text-sm font-normal text-gray-500">
|
|
||||||
{{ loop.index }}
|
|
||||||
</td>
|
|
||||||
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||||
{{ e['Name'] }}
|
{{ e['Name'] }}
|
||||||
</td>
|
</td>
|
||||||
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||||
<a href="{{ url_for('delete_exercise', exercise_id=e['ExerciseId']) }}"
|
<a hx-get="{{ url_for('get_exercise_edit_form', exercise_id=e['ExerciseId']) }}"
|
||||||
|
class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
<a hx-delete="{{ url_for('delete_exercise', exercise_id=e['ExerciseId']) }}"
|
||||||
class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg inline-flex items-center p-2">
|
||||||
Remove
|
Remove
|
||||||
</a>
|
</a>
|
||||||
@@ -134,7 +132,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="w-full mt-3" action="{{ url_for('create_exercise') }}" method="post">
|
<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="flex flex-wrap -mx-3 mb-2">
|
||||||
<div class="grow px-3">
|
<div class="grow px-3">
|
||||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||||
|
|||||||
Reference in New Issue
Block a user