{% macro render_metric(metric, can_edit=False, show_rename=False) %} {% set prog = metric.progress or {} %} {% set cfg = metric.config or {} %}
{# ── Заголовок ── #}
{% if metric.ai_prompt %} {{ metric.label }} {% else %} {{ metric.label }} {% endif %} {% if can_edit and show_rename %} {% endif %}
{# ── Кнопка удаления (абсолютная, правый верхний угол) ── #} {% if can_edit %} {% endif %}
{# ════════════════════════════════════════════════════════════════════════ CHECKPOINTS — контрольные точки ════════════════════════════════════════════════════════════════════════ #} {% if metric.type == 'checkpoints' %} {% set total = cfg.get('total', 0) | int %} {% set checked = prog.get('checked', []) %} {% set done = checked | length %} {% set pct = (done / total * 100) | int if total else 0 %}
{{ done }}/{{ total }}
{% for i in range(total) %} {% set lbl = cfg.get('labels', [])[i] if cfg.get('labels') and i < (cfg.get('labels') | length) else (i + 1) | string %} {% if can_edit %} {% else %} {{ lbl }} {% endif %} {% endfor %} {% if not total %}Не настроено{% endif %}
{# ════════════════════════════════════════════════════════════════════════ PROGRESS BAR — накопление баллов ════════════════════════════════════════════════════════════════════════ #} {% elif metric.type == 'progress_bar' %} {% set cur = prog.get('current', 0) %} {% set mx = cfg.get('max', 100) | float %} {% set pct = [[cur / mx * 100, 0] | max, 100] | min | int if mx else 0 %}
{{ pct }}%
{{ cur }} / {{ mx | int }}{% if cfg.get('unit') %} {{ cfg.get('unit') }}{% endif %}
{% if can_edit %}
{% endif %}
{# ════════════════════════════════════════════════════════════════════════ COUNTER — счётчик ════════════════════════════════════════════════════════════════════════ #} {% elif metric.type == 'counter' %} {% set cur = prog.get('current', 0) %} {% set mx = cfg.get('max') | int if cfg.get('max') else 0 %}
{% if can_edit %}{% endif %}
{{ cur }} {%- if mx %}/ {{ mx }}{% endif %} {%- if cfg.get('unit') %} {{ cfg.get('unit') }}{% endif -%}
{% if can_edit %}{% endif %}
{% if mx %}
{% endif %}
{# ════════════════════════════════════════════════════════════════════════ STAGES — этапы ════════════════════════════════════════════════════════════════════════ #} {% elif metric.type == 'stages' %} {% set cur_stage = prog.get('current_stage', 0) %} {% set stages = cfg.get('stages', []) %}
{% for stage in stages %} {% set idx = loop.index0 %} {% set state = 'done' if idx < cur_stage else ('active' if idx == cur_stage else '') %}
{% if can_edit %} {% else %}
{% endif %} {% if not loop.last %}
{% endif %}
{% else %} Не настроено {% endfor %}
{# ════════════════════════════════════════════════════════════════════════ CHECKLIST — чеклист ════════════════════════════════════════════════════════════════════════ #} {% elif metric.type == 'checklist' %} {% set checked = prog.get('checked', []) %} {% set items = cfg.get('items', []) %} {% set pct = (checked | length / items | length * 100) | int if items else 0 %}
{{ checked | length }}/{{ items | length }}
{% for item in items %} {% set idx = loop.index0 %} {% else %} Нет пунктов {% endfor %}
{# ════════════════════════════════════════════════════════════════════════ ATTENDANCE — посещаемость ════════════════════════════════════════════════════════════════════════ #} {% elif metric.type == 'attendance' %} {% set records = prog.get('records', []) %} {% set present = records | selectattr('status', 'equalto', 'present') | list | length %} {% set absent = records | selectattr('status', 'equalto', 'absent') | list | length %} {% set excused = records | selectattr('status', 'equalto', 'excused') | list | length %}
{{ present }} Присут.
{{ absent }} Пропуск
{{ excused }} Уваж.
{% for rec in records[-40:] %} {% endfor %}
{% if can_edit %}
{% endif %} {# ════════════════════════════════════════════════════════════════════════ GRADES — оценки ════════════════════════════════════════════════════════════════════════ #} {% elif metric.type == 'grades' %} {% set grades = prog.get('grades', {}) %} {% set works = cfg.get('works', []) %} {% set max_gr = cfg.get('max_grade', 5) | float %}
{% for work in works %} {% set val = grades.get(work.name, '') %} {% set pct = (val | float / max_gr * 100) | int if val != '' else 0 %}
{{ work.name }}
{% else %} Нет работ {% endfor %}
{% if works and can_edit %} {% endif %} {# ════════════════════════════════════════════════════════════════════════ DEADLINES — дедлайны ════════════════════════════════════════════════════════════════════════ #} {% elif metric.type == 'deadlines' %} {% set completed = prog.get('completed', []) %} {% set items = cfg.get('items', []) %} {% set pct = (completed | length / items | length * 100) | int if items else 0 %}
{{ completed | length }}/{{ items | length }}
{% for item in items %} {% set idx = loop.index0 %} {% else %} Нет дедлайнов {% endfor %}
{# ════════════════════════════════════════════════════════════════════════ STREAK — серия ════════════════════════════════════════════════════════════════════════ #} {% elif metric.type == 'streak' %} {% set count = prog.get('records', []) | length %}
🔥
{{ count }}
{{ 'день' if count % 10 == 1 and count % 100 != 11 else ('дня' if count % 10 in [2,3,4] and count % 100 not in [12,13,14] else 'дней') }} подряд
{% if can_edit %} {% endif %} {# ════════════════════════════════════════════════════════════════════════ EFFORT HOURS — трудозатраты ════════════════════════════════════════════════════════════════════════ #} {% elif metric.type == 'effort_hours' %} {% set total = prog.get('hours', 0) %} {% set goal = cfg.get('goal_hours', 0) | float %} {% set pct = [[total / goal * 100, 0] | max, 100] | min | int if goal else 0 %}
{{ total }}ч
{% if goal %}
цель: {{ goal | int }}ч
{% endif %}
{% if goal %}
{{ pct }}%
{% endif %} {% if can_edit %}
{% endif %}
{# ════════════════════════════════════════════════════════════════════════ RATING HISTORY — история оценок ════════════════════════════════════════════════════════════════════════ #} {% elif metric.type == 'rating_history' %} {% set entries = prog.get('entries', []) %} {% set scale = cfg.get('scale', 5) | float %} {% if entries %} {% set last = entries[-1] %} {% set avg = (entries | sum(attribute='grade') / entries | length) | round(1) %} {% endif %} {# gh-1: только большая оценка — центральный вид #}
{% if entries %}
{{ last.grade }}
/ {{ scale | int }}
ср. {{ avg }}
{% else %} Нет записей {% endif %}
{# gh-2+: разделённый вид #}
{% if entries %}
{{ last.grade }}
/ {{ scale | int }}
ср.
{{ avg }}
{% else %} — {% endif %}
{% for entry in entries | reverse %} {% set pct = (entry.grade / scale * 100) | int %}
{{ entry.date }}
{{ entry.grade }}
{% else %} Нет записей {% endfor %}
{% if can_edit %}
{% endif %}
{% endif %}
{# /t-metric-body #} {% endmacro %}