ui: bulk-delete tracks, HR/VR record + detail layout
- Tracks list: the long-press multi-select (used for merge) now also offers Удалить - bulk delete of the selected tracks (1+; merge still needs 2+), same selection flow. - HR/VR record screen: heart rate fills the main area (like the map on a GPS ride), Подключить/Стоп moved into a bottom bar, consistent with the GPS screen. - HR/VR workout detail: 'Удалить зарядку' pinned to the bottom (was floating in the scroll flow and stretched).
This commit is contained in:
parent
9251c1dd90
commit
eb430ffb78
2 changed files with 48 additions and 13 deletions
|
|
@ -751,7 +751,12 @@ select {
|
|||
.full-width { width: 100%; }
|
||||
|
||||
/* HR-only workout (зарядка) recording screen: big heart rate, timer, kcal, no map. */
|
||||
.workout-screen { padding: 24px 16px; display: flex; flex-direction: column; align-items: center; gap: 8px; }
|
||||
.workout-screen { display: flex; flex-direction: column; height: 100%; }
|
||||
.wk-main { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; padding: 16px; }
|
||||
.wk-panel { flex-shrink: 0; display: flex; flex-direction: column; padding: 10px 16px; padding-bottom: calc(10px + var(--safe-area-bottom, 0px)); }
|
||||
/* Workout detail (in a modal): fill the scrollable body so the delete button sits at the bottom. */
|
||||
.wkd-wrap { display: flex; flex-direction: column; min-height: 100%; }
|
||||
.wkd-del { margin-top: auto; flex: none; align-self: stretch; }
|
||||
.wk-hr-big { font-size: 92px; font-weight: 700; line-height: 1; color: var(--primary); }
|
||||
.wk-hr-lbl { color: var(--muted); margin-top: -4px; }
|
||||
.wk-stats { display: flex; gap: 18px; margin: 14px 0; flex-wrap: wrap; justify-content: center; }
|
||||
|
|
|
|||
|
|
@ -2934,6 +2934,7 @@ async function renderGpsScreen(container) {
|
|||
<div id="gps-sel-bar" class="gps-sel-bar" hidden>
|
||||
<span id="gps-sel-count">Выбрано 0</span>
|
||||
<button id="gps-sel-merge" class="btn-primary" disabled>Объединить</button>
|
||||
<button id="gps-sel-delete" class="btn-secondary" style="color:#cf6679" disabled>Удалить</button>
|
||||
<button id="gps-sel-cancel" class="btn-secondary">Отмена</button>
|
||||
</div>
|
||||
</div>`;
|
||||
|
|
@ -2991,11 +2992,13 @@ async function renderGpsScreen(container) {
|
|||
let selBar = container.querySelector("#gps-sel-bar");
|
||||
let selCount = container.querySelector("#gps-sel-count");
|
||||
let selMergeBtn = container.querySelector("#gps-sel-merge");
|
||||
let selDeleteBtn = container.querySelector("#gps-sel-delete");
|
||||
let refreshSel = () => {
|
||||
container.querySelectorAll(".gps-session-item").forEach(it => it.classList.toggle("selected", selected.has(it.dataset.sid)));
|
||||
if (selBar) selBar.hidden = !selMode;
|
||||
if (selCount) selCount.textContent = "Выбрано " + selected.size;
|
||||
if (selMergeBtn) selMergeBtn.disabled = selected.size < 2;
|
||||
if (selMergeBtn) selMergeBtn.disabled = selected.size < 2; // merge needs 2+
|
||||
if (selDeleteBtn) selDeleteBtn.disabled = selected.size < 1; // delete works from 1
|
||||
};
|
||||
let exitSel = () => { selMode = false; selected.clear(); refreshSel(); };
|
||||
let toggleSel = (sid) => {
|
||||
|
|
@ -3060,6 +3063,27 @@ async function renderGpsScreen(container) {
|
|||
selMergeBtn.disabled = false; selMergeBtn.textContent = "Объединить";
|
||||
}
|
||||
};
|
||||
if (selDeleteBtn) selDeleteBtn.onclick = async () => {
|
||||
if (selected.size < 1) return;
|
||||
let ids = [...selected];
|
||||
let n = ids.length;
|
||||
let word = (n % 10 === 1 && n % 100 !== 11) ? "трек"
|
||||
: (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) ? "трека" : "треков";
|
||||
if (!(await appConfirm(`Удалить ${n} ${word}? Это необратимо.`, "Удалить"))) return;
|
||||
try {
|
||||
selDeleteBtn.disabled = true; selDeleteBtn.textContent = "Удаляю...";
|
||||
let done = 0;
|
||||
for (let id of ids) {
|
||||
try { await DB.deleteGpsSession(id); done++; } catch (e) { console.error("[bulk delete]", id, e); }
|
||||
}
|
||||
showToast(`Удалено: ${done}`);
|
||||
exitSel();
|
||||
renderScreen("gps");
|
||||
} catch (e) {
|
||||
showToast("Ошибка удаления: " + (e.message || e));
|
||||
selDeleteBtn.disabled = false; selDeleteBtn.textContent = "Удалить";
|
||||
}
|
||||
};
|
||||
|
||||
// Pagination: "Показать ещё" loads the next page from the DB and appends it.
|
||||
let sessOffset = sessions.length;
|
||||
|
|
@ -4777,6 +4801,7 @@ function renderWorkoutDetail(session) {
|
|||
let max = hrs.length ? Math.max(...hrs) : null;
|
||||
let kcal = Math.round(session.total_kcal || 0);
|
||||
let html = `
|
||||
<div class="wkd-wrap">
|
||||
<div class="sd-section"><div class="sd-title">${session.activity_type === "vr" ? "Руки/VR" : "Зарядка"} · ${session.date} ${session.start_time ?? ""}</div>
|
||||
<div class="sd-row">
|
||||
<div class="sd-stat"><span class="sd-val">${GPS.formatDuration(durSec)}</span><span class="sd-lbl">Время</span></div>
|
||||
|
|
@ -4790,7 +4815,8 @@ function renderWorkoutDetail(session) {
|
|||
<div class="sd-section"><div class="sd-title">Пульс</div>
|
||||
<div class="chart-wrap"><canvas id="wk-hr-chart" height="150"></canvas></div>
|
||||
</div>
|
||||
<button id="wk-del" class="btn-secondary full-width" style="margin-top:8px;color:#cf6679">Удалить зарядку</button>`;
|
||||
<button id="wk-del" class="btn-secondary full-width wkd-del" style="color:#cf6679">Удалить зарядку</button>
|
||||
</div>`;
|
||||
showModal(html, () => {}, (overlay) => {
|
||||
let ctx = overlay.querySelector("#wk-hr-chart");
|
||||
if (ctx && samples.length) {
|
||||
|
|
@ -4817,17 +4843,21 @@ function renderWorkoutDetail(session) {
|
|||
function renderWorkoutInline(container) {
|
||||
container.innerHTML = `
|
||||
<div class="workout-screen">
|
||||
<div class="wk-main">
|
||||
<div class="wk-hr-big" id="wk-hr">--</div>
|
||||
<div class="wk-hr-lbl">уд/мин</div>
|
||||
<div class="wk-status" id="wk-status">Подключите пульсометр</div>
|
||||
</div>
|
||||
<div class="wk-panel">
|
||||
<div class="wk-stats">
|
||||
<div class="wk-stat"><span id="wk-time" class="wk-val">0:00</span><span class="wk-lbl">Время</span></div>
|
||||
<div class="wk-stat"><span id="wk-kcal" class="wk-val">0</span><span class="wk-lbl">ккал</span></div>
|
||||
<div class="wk-stat"><span id="wk-avg" class="wk-val">--</span><span class="wk-lbl">средний</span></div>
|
||||
<div class="wk-stat"><span id="wk-max" class="wk-val">--</span><span class="wk-lbl">макс</span></div>
|
||||
</div>
|
||||
<div class="wk-status" id="wk-status">Подключите пульсометр</div>
|
||||
<button id="wk-connect" class="btn-secondary full-width">Подключить пульсометр</button>
|
||||
<button id="wk-stop" class="btn-primary gps-rec recording full-width">Стоп</button>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
let hrEl = container.querySelector("#wk-hr"), timeEl = container.querySelector("#wk-time");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue