Add intensity percentage selector with repetition hints
This commit is contained in:
@@ -45,3 +45,7 @@ Planner__UndoDeleteDurationSeconds=10
|
||||
|
||||
# Размер иконки камеры в PDF (пункты).
|
||||
Pdf__CameraSizePoints=18
|
||||
|
||||
# Пример переопределения подсказки процентов (индекс 0 — первая строка списка).
|
||||
# Planner__IntensityPresets__0__Percent=100
|
||||
# Planner__IntensityPresets__0__Repetitions=1
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
- Поиск в конфигураторе: адаптивный выпадающий список с фильтром названия и группы. Двойной клик или Enter сразу добавляет упражнение; одиночный клик выбирает его для кнопки «Добавить». Точное совпадение и начало названия выше остальных; регистр и «ё/е» не мешают поиску.
|
||||
- Удаление из библиотеки через корзину с подтверждением; упражнения в ранее составленных программах сохраняются.
|
||||
- Недели, тренировки и перемещаемые блоки. Перетаскивание за маркер или кнопки вверх/вниз.
|
||||
- Общие подходы, текстовые повторения и вес; текстовые разминка и примечание; процент от максимума вручную.
|
||||
- Общие подходы, текстовые повторения и вес; текстовые разминка и примечание; процент от максимума выбирается из компактного списка с подсказкой по повторениям. Подсказка не меняет поле повторений. Список задан в `Planner:IntensityPresets`; ранее сохранённые нестандартные значения сохраняются.
|
||||
- Отдых в минутах. Итог — количество подходов × отдых, включая последний подход. Время тренировки задаётся вручную.
|
||||
- Копирование блоков, тренировок, недель и всей программы. Каждое упражнение — отдельный блок; сетов нет.
|
||||
- Перетаскивание недель с автоматической перенумерацией и сохранением порядка.
|
||||
|
||||
@@ -115,3 +115,9 @@
|
||||
|
||||
- Имя скачиваемого файла: «Имя клиента — dd.MM.yyyy.pdf», дата создания программы без времени, в том же локальном времени сервера, что и история программ. Недопустимые символы имени файла заменяются подчёркиванием.
|
||||
- Сборка и Docker-обновление успешны. Проверен Content-Disposition с кириллическим именем клиента и форматом даты. Собственные тестовые записи удалены, скриншоты не выполнялись.
|
||||
|
||||
## Список процентов — 14 октября 2026
|
||||
|
||||
- Поле процента заменено компактным списком из девяти значений с ориентиром повторений. Значения вынесены в Planner:IntensityPresets. Сохранённые нестандартные проценты отображаются отдельным пунктом.
|
||||
- Локальная сборка и Docker-обновление выполнены. Без скриншотов проверены все девять значений, значение по умолчанию 80, сохранение 85 после перезагрузки и неизменность поля повторений. Собственные тестовые данные удалены.
|
||||
- По уточнению пользователя убран title списка (подсказка при наведении). Сервер и его данные не затрагивались, миграций нет.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TrainingPlanner.Application;
|
||||
|
||||
public class IntensityPreset
|
||||
{
|
||||
public decimal Percent { get; set; }
|
||||
public string Repetitions { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ public class PlannerOptions
|
||||
{
|
||||
public string DefaultWorkoutName { get; set; } = string.Empty;
|
||||
public string DefaultWorkoutDuration { get; set; } = string.Empty;
|
||||
public List<IntensityPreset> IntensityPresets { get; set; } = [];
|
||||
public decimal DefaultIntensityPercent { get; set; }
|
||||
public int DefaultWeekCount { get; set; }
|
||||
public int DefaultSetsCount { get; set; }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@inject PlannerOptions Options
|
||||
|
||||
<div class="exercise-editor">
|
||||
<div class="exercise-heading">
|
||||
<label class="grow">
|
||||
@@ -22,8 +24,27 @@
|
||||
<input @bind="Exercise.Weight" @bind:event="oninput" @bind:after="Changed" placeholder="60 или Подобрать" />
|
||||
</label>
|
||||
<label>От максимума, %
|
||||
<input type="number" min="0" max="100" step="any" @bind="Exercise.IntensityPercent"
|
||||
@bind:after="Changed" placeholder="—" />
|
||||
<span class="intensity-field">
|
||||
<select aria-label="От максимума, %" class="intensity-select" @bind="Exercise.IntensityPercent" @bind:after="Changed">
|
||||
<option value="">—</option>
|
||||
@if (Exercise.IntensityPercent.HasValue && Options.IntensityPresets.All(item => item.Percent != Exercise.IntensityPercent.Value))
|
||||
{
|
||||
<option value="@Exercise.IntensityPercent.Value">@Exercise.IntensityPercent.Value% — своё значение</option>
|
||||
}
|
||||
@foreach (var preset in Options.IntensityPresets)
|
||||
{
|
||||
<option value="@preset.Percent">@preset.Percent% — @preset.Repetitions повт.</option>
|
||||
}
|
||||
</select>
|
||||
<span class="intensity-value" aria-hidden="true">
|
||||
<span>@(Exercise.IntensityPercent.HasValue ? $"{Exercise.IntensityPercent.Value}%" : "—")</span>
|
||||
@if (SelectedIntensity != null)
|
||||
{
|
||||
<span class="intensity-hint">@SelectedIntensity.Repetitions повт.</span>
|
||||
}
|
||||
</span>
|
||||
<span class="intensity-arrow" aria-hidden="true">⌄</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<label>Разминочные подходы
|
||||
@@ -40,5 +61,7 @@
|
||||
[Parameter, EditorRequired] public WorkoutExercise Exercise { get; set; } = null!;
|
||||
[Parameter] public EventCallback OnChanged { get; set; }
|
||||
|
||||
private IntensityPreset? SelectedIntensity => Options.IntensityPresets.FirstOrDefault(item => item.Percent == Exercise.IntensityPercent);
|
||||
|
||||
private Task Changed() => OnChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,17 @@
|
||||
"Planner": {
|
||||
"DefaultWorkoutDuration": "1 час",
|
||||
"DefaultIntensityPercent": 80,
|
||||
"IntensityPresets": [
|
||||
{ "Percent": 100, "Repetitions": "1" },
|
||||
{ "Percent": 95, "Repetitions": "2–3" },
|
||||
{ "Percent": 90, "Repetitions": "3–5" },
|
||||
{ "Percent": 85, "Repetitions": "5–7" },
|
||||
{ "Percent": 80, "Repetitions": "6–10" },
|
||||
{ "Percent": 75, "Repetitions": "8–12" },
|
||||
{ "Percent": 70, "Repetitions": "10–15" },
|
||||
{ "Percent": 65, "Repetitions": "12–17" },
|
||||
{ "Percent": 60, "Repetitions": "15–20" }
|
||||
],
|
||||
"DefaultWeekCount": 4,
|
||||
"DefaultSetsCount": 3,
|
||||
"DefaultRepetitions": "10",
|
||||
|
||||
@@ -786,3 +786,10 @@ summary {
|
||||
.import-table td { border:1px solid var(--line); padding:4px; }
|
||||
.import-table input { width:100%; min-width:0; border-color:transparent; }
|
||||
.exercise-import .actions { margin-top:20px; }
|
||||
|
||||
.intensity-field { display:block; position:relative; width:100%; max-width:220px; }
|
||||
.intensity-select { width:100%; appearance:none; color:transparent; font-variant-numeric:tabular-nums; }
|
||||
.intensity-select option { color:var(--text, #202637); background:white; }
|
||||
.intensity-value { position:absolute; inset:0 34px 0 12px; display:flex; align-items:center; justify-content:space-between; gap:8px; pointer-events:none; color:var(--text, #202637); font-weight:400; font-variant-numeric:tabular-nums; }
|
||||
.intensity-hint { color:var(--muted); font-size:12px; white-space:nowrap; }
|
||||
.intensity-arrow { position:absolute; right:12px; top:50%; transform:translateY(-50%); color:var(--muted); pointer-events:none; }
|
||||
|
||||
Reference in New Issue
Block a user