Drag-Griff statt ganzem Element + Uebungs-Reihenfolge beim Start uebernehmen
Image bauen und in die Registry schieben / build (push) Successful in 6s
Image bauen und in die Registry schieben / build (push) Successful in 6s
- Sortieren nur ueber einen kleinen Griff (Punkt-Icon) links im Uebungskopf; das ganze Element ist nicht mehr draggable (kein versehentliches Ziehen beim Tippen/Auswaehlen) - Beim Session-Start wird die Uebungs-Reihenfolge der letzten abgeschlossenen Session desselben Tags uebernommen (server-seitig in create_session, in session.fields.exerciseOrder) - Tests: 170 Checks gruen (40+18+52+32+28)
This commit is contained in:
1 parent
7c5045cec8
commit
aebd7f40e7
5 files changed
+60
-12
No files matched your search
@@ -782,17 +782,16 @@ function reorderExercise(a, model, fromKey, toKey) {
|
||||
scheduleAutosave(model);
|
||||
}
|
||||
|
||||
function attachDragReorder(a, model, ex, block) {
|
||||
block.draggable = true;
|
||||
block.title = t("reorder_hint");
|
||||
block.addEventListener("dragstart", ev => {
|
||||
function attachDragReorder(a, model, ex, block, grip) {
|
||||
grip.draggable = true;
|
||||
grip.addEventListener("dragstart", ev => {
|
||||
app.dragKey = ex.key;
|
||||
try {
|
||||
ev.dataTransfer = ev.dataTransfer || {};
|
||||
ev.dataTransfer.setData("text/plain", ex.key);
|
||||
} catch (_) {}
|
||||
});
|
||||
block.addEventListener("dragend", () => {
|
||||
grip.addEventListener("dragend", () => {
|
||||
app.dragKey = null;
|
||||
for (const b of $$(".ex-block.drag-over")) b.classList.remove("drag-over");
|
||||
});
|
||||
@@ -850,6 +849,14 @@ function buildExBlock(a, model, ex, removable) {
|
||||
model.exByKey[ex.key] = ex;
|
||||
|
||||
const head = el("div", "ex-head");
|
||||
const grip = el("span", "ex-grip", "");
|
||||
grip.innerHTML = '<svg viewBox="0 0 16 16" width="14" height="14" fill="currentColor" aria-hidden="true">'
|
||||
+ '<circle cx="6" cy="4" r="1.4"/><circle cx="10" cy="4" r="1.4"/>'
|
||||
+ '<circle cx="6" cy="8" r="1.4"/><circle cx="10" cy="8" r="1.4"/>'
|
||||
+ '<circle cx="6" cy="12" r="1.4"/><circle cx="10" cy="12" r="1.4"/></svg>';
|
||||
grip.title = t("reorder_hint");
|
||||
grip.setAttribute("aria-label", t("reorder_hint"));
|
||||
head.appendChild(grip);
|
||||
const infoBtn = el("button", "icon-btn", "?");
|
||||
infoBtn.type = "button";
|
||||
infoBtn.setAttribute("aria-label", t("info_aria") + ex.name);
|
||||
@@ -878,7 +885,7 @@ function buildExBlock(a, model, ex, removable) {
|
||||
|
||||
block.appendChild(el("div", "set-rows"));
|
||||
renderExRows(a, model, ex);
|
||||
attachDragReorder(a, model, ex, block);
|
||||
attachDragReorder(a, model, ex, block, grip);
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
@@ -434,6 +434,26 @@ def fetch_session(user_id, sid):
|
||||
return d
|
||||
|
||||
|
||||
def last_exercise_order(user_id, activity, day):
|
||||
"""Uebungs-Reihenfolge der letzten abgeschlossenen Session (Tag/Kategorie)."""
|
||||
con = db()
|
||||
clause, extra = _user_clause(user_id)
|
||||
rows = con.execute(
|
||||
f"SELECT fields FROM sessions WHERE activity=? AND day=? AND status='done' "
|
||||
f"AND {clause} ORDER BY date DESC, id DESC LIMIT 10",
|
||||
[activity, day] + extra).fetchall()
|
||||
con.close()
|
||||
for r in rows:
|
||||
try:
|
||||
f = json.loads(r["fields"] or "{}")
|
||||
except Exception:
|
||||
continue
|
||||
order = f.get("exerciseOrder")
|
||||
if isinstance(order, list) and order:
|
||||
return order
|
||||
return None
|
||||
|
||||
|
||||
def create_session(user_id, data):
|
||||
activity = _clean_str(data.get("activity"))
|
||||
if not activity:
|
||||
@@ -441,12 +461,15 @@ def create_session(user_id, data):
|
||||
sdate = _clean_str(data.get("date")) or date.today().isoformat()
|
||||
day = _clean_str(data.get("day"))
|
||||
start = _clean_str(data.get("start_time")) or datetime.now().strftime("%H:%M")
|
||||
# Reihenfolge der Uebungen aus der letzten abgeschlossenen Session uebernehmen
|
||||
order = last_exercise_order(user_id, activity, day)
|
||||
fields = json.dumps({"exerciseOrder": order}) if order else "{}"
|
||||
con = db()
|
||||
cur = con.execute(
|
||||
"INSERT INTO sessions(activity,day,date,start_time,status,created_at,user_id) "
|
||||
"VALUES(?,?,?,?,?,?,?)",
|
||||
"INSERT INTO sessions(activity,day,date,start_time,status,created_at,user_id,fields) "
|
||||
"VALUES(?,?,?,?,?,?,?,?)",
|
||||
(activity, day, sdate, start, "active",
|
||||
datetime.now().isoformat(timespec="seconds"), user_id))
|
||||
datetime.now().isoformat(timespec="seconds"), user_id, fields))
|
||||
con.commit()
|
||||
sid = cur.lastrowid
|
||||
con.close()
|
||||
|
||||
+10
-1
@@ -400,9 +400,18 @@ h3 { font-size: 15px; font-weight: 700; }
|
||||
.ex-block {
|
||||
border: 2px solid var(--border);
|
||||
margin: 0;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.ex-grip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: var(--muted);
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ex-grip:active { cursor: grabbing; }
|
||||
|
||||
.ex-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(460px, 100%), 1fr));
|
||||
|
||||
@@ -212,6 +212,15 @@ def main():
|
||||
check("Statistik: bestes Gewicht je Termin",
|
||||
s0["weight"] == 22 and s1["weight"] == 20, stats)
|
||||
|
||||
# Uebungs-Reihenfolge der letzten abgeschlossenen Session uebernehmen
|
||||
order = ["Push/Schrägbank KH (je Hand)", "Push/Bankdrücken KH (je Hand)"]
|
||||
req("PATCH", f"/api/sessions/{pid}",
|
||||
{"meta": {"fields": {"exerciseOrder": order}}})
|
||||
st, ns = req("POST", "/api/sessions", {"activity": "gym", "day": "Push"})
|
||||
check("Uebungs-Reihenfolge beim Start uebernommen",
|
||||
ns["fields"].get("exerciseOrder") == order, ns["fields"])
|
||||
req("DELETE", f"/api/sessions/{ns['id']}")
|
||||
|
||||
st, exp = req("GET", "/api/export?format=json")
|
||||
check("Export JSON Format", st == 200
|
||||
and exp["format"] == "trainingsplan-export-v2"
|
||||
|
||||
+2
-2
@@ -238,8 +238,8 @@ async function main() {
|
||||
check("Hinweiszeile ausgeblendet",
|
||||
!gymCard().querySelector(".ex-block .ex-hint"));
|
||||
|
||||
// --- Uebungen per Drag & Drop sortieren --------------------------------
|
||||
gymCard().querySelectorAll(".ex-block")[0]
|
||||
// --- Uebungen per Drag & Drop (Griff) sortieren ------------------------
|
||||
gymCard().querySelectorAll(".ex-block")[0].querySelector(".ex-grip")
|
||||
.dispatchEvent(new window.Event("dragstart", { bubbles: true }));
|
||||
const target = gymCard().querySelectorAll(".ex-block")[1];
|
||||
target.dispatchEvent(new window.Event("drop", { bubbles: true }));
|
||||
|
||||
Reference in new issue
Block a user