When you start an exercise, the Mini App now fetches the most
recent time you logged it and shows a hint line in the sets card
("Last time: 8×60, 6×60, 5×60 · 3 days ago"), plus pre-fills the
weight input with the last set's weight.
- db.get_last_exercise(user_id, name): most recent non-deleted
entry, case-insensitive name match, sets_detail parsed.
- GET /api/exercises/last?name=<name>.
- webapp: loadLastSession() on startExercise + draft restore;
hint cleared on editExercise (the set rows are the reference
there). Pre-fill only when the weight field is empty and no
sets logged yet, so it never clobbers user input.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
127 lines
5.1 KiB
HTML
127 lines
5.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
|
<title>Workout Tracker</title>
|
|
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
|
<link rel="stylesheet" href="style.css" />
|
|
</head>
|
|
<body>
|
|
<div id="app">
|
|
<nav id="tabs">
|
|
<button class="tab active" data-view="log">Log</button>
|
|
<button class="tab" data-view="history">History</button>
|
|
<button class="tab" data-view="stats">Stats</button>
|
|
<button class="tab" data-view="settings">Settings</button>
|
|
</nav>
|
|
|
|
<!-- ═══ LOG VIEW ═══ -->
|
|
<div id="view-log" class="view active">
|
|
|
|
<!-- Editing banner -->
|
|
<div id="editing-banner" class="editing-banner hidden">
|
|
<span>Editing workout</span>
|
|
<button id="btn-cancel-edit" class="btn-link">Cancel</button>
|
|
</div>
|
|
|
|
<!-- Current workout exercises list -->
|
|
<div id="workout-exercises"></div>
|
|
|
|
<!-- Current exercise card (visible after name is entered) -->
|
|
<div id="sets-section" class="card sets-section hidden">
|
|
<div class="sets-header">
|
|
<div class="sets-header-left">
|
|
<div class="section-label" id="sets-label">Sets</div>
|
|
<span id="rest-timer" class="rest-timer hidden" aria-label="Time since last set">00:00</span>
|
|
</div>
|
|
<button id="btn-delete-exercise" class="btn-link btn-danger hidden">Remove exercise</button>
|
|
</div>
|
|
<div id="last-session-hint" class="last-session-hint hidden"></div>
|
|
<div id="sets-list"></div>
|
|
<div class="set-input-row">
|
|
<input type="text" id="inp-reps" class="input input-small" placeholder="Reps" inputmode="numeric" pattern="[0-9]*" />
|
|
<span class="set-separator">x</span>
|
|
<input type="text" id="inp-weight" class="input input-small" placeholder="kg" inputmode="decimal" />
|
|
<button id="btn-weight-sign" class="btn-sign" title="Flip sign (for assisted exercises)" type="button">±</button>
|
|
<button id="btn-add-set" class="btn-icon" title="Add set">+</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add next exercise -->
|
|
<div class="card" id="add-exercise-card">
|
|
<div class="exercise-name-row">
|
|
<input type="text" id="inp-exercise-name" class="input" placeholder="Exercise name" autocomplete="off" />
|
|
<button id="btn-add-exercise" class="btn-icon" title="Add exercise">+</button>
|
|
</div>
|
|
<div id="autocomplete-list" class="autocomplete-list"></div>
|
|
</div>
|
|
|
|
<!-- Notes -->
|
|
<div id="notes-section" class="hidden">
|
|
<textarea id="inp-note" class="input" rows="2" placeholder="Notes (optional)"></textarea>
|
|
</div>
|
|
|
|
<!-- Save workout button -->
|
|
<button id="btn-save-workout" class="btn btn-primary hidden">Save Workout</button>
|
|
|
|
<!-- Raw text collapsible section -->
|
|
<details id="raw-details" class="raw-section">
|
|
<summary class="raw-toggle">Paste as text</summary>
|
|
<div class="card raw-card">
|
|
<textarea id="inp-raw" class="input" rows="6"
|
|
placeholder="Bench press: 4x8x35 Shoulder press (3032): 8x25, 5x35 Squats: 5x5x30"></textarea>
|
|
<div class="hint">Same format as the bot. Blank line = new group.</div>
|
|
<button id="btn-save-raw" class="btn btn-primary">Save Workout</button>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
<!-- ═══ HISTORY VIEW ═══ -->
|
|
<div id="view-history" class="view">
|
|
<div id="history-list"></div>
|
|
<div id="no-history" class="empty-state">
|
|
<div class="empty-icon">📋</div>
|
|
<p>No workouts yet</p>
|
|
</div>
|
|
<button id="btn-load-more" class="btn btn-secondary" style="display:none">Load more</button>
|
|
</div>
|
|
|
|
<!-- ═══ STATS VIEW ═══ -->
|
|
<div id="view-stats" class="view">
|
|
<div id="stats-content" class="empty-state">
|
|
<div class="empty-icon">📊</div>
|
|
<p>Loading...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ SETTINGS VIEW ═══ -->
|
|
<div id="view-settings" class="view">
|
|
<div class="card">
|
|
<label class="settings-row">
|
|
<div class="settings-row-label">
|
|
<div class="settings-row-title">Rest timer</div>
|
|
<div class="settings-row-hint">Show time since the last set was logged.</div>
|
|
</div>
|
|
<input type="checkbox" id="setting-rest-timer" class="settings-toggle" checked />
|
|
</label>
|
|
</div>
|
|
<div class="card">
|
|
<label class="settings-row">
|
|
<div class="settings-row-label">
|
|
<div class="settings-row-title">Negative weight button</div>
|
|
<div class="settings-row-hint">Show the ± sign-flip button next to the weight input (useful for assisted bodyweight exercises).</div>
|
|
</div>
|
|
<input type="checkbox" id="setting-weight-sign" class="settings-toggle" checked />
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<footer id="app-footer">
|
|
<span id="version-badge"></span>
|
|
</footer>
|
|
</div>
|
|
|
|
<script src="app.js"></script>
|
|
</body>
|
|
</html>
|