{% extends "base.html" %} {% block title %}{{ user.name or user.email }} - Analityka{% endblock %} {% block head_extra %} {% endblock %} {% block extra_css %} {% endblock %} {% block content %}
Powrót do Analityki
{{ user.name or 'Bez nazwy' }}
{{ user.email }}
{% if user.company %}
Firma {{ user.company.name }}
{% endif %}
Rola {{ user.role }}
Rejestracja {{ user.created_at|local_time('%d.%m.%Y') if user.created_at else 'N/A' }}
Ostatni login {{ user.last_login|local_time('%d.%m.%Y %H:%M') if user.last_login else 'Nigdy' }}
{% set eng_class = 'low' if engagement_score >= 50 else ('medium' if engagement_score >= 20 else 'high') %}
{{ engagement_score }}
Zaangażowanie
{% set prob_class = 'high' if problem_score >= 51 else ('medium' if problem_score >= 21 else 'low') %}
{{ problem_score }}
Problemy
{{ avg_sessions_week }}
Śr. sesji/tydzień
{{ (avg_session_duration / 60)|round(1) }}m
Śr. czas sesji
{{ password_resets }}
Resety hasła (30d)
{{ security_alerts_count }}
Alerty (7d)
{% if resolution %}
{% if resolution.status == 'resolved' %}✅{% elif resolution.status == 'pending' %}⏳{% elif resolution.status == 'blocked' %}🔒{% elif resolution.status == 'unresolved' %}❌{% else %}❓{% endif %}
{{ resolution.status_label }}
{% if resolution.first_symptom %}
Pierwszy objaw
{{ resolution.first_symptom|local_time('%d.%m.%Y %H:%M') }}
{% endif %}
Resety hasła wysłane
{{ resolution.resets_sent }}
{% if resolution.last_reset %}
Ostatni reset
{{ resolution.last_reset|local_time('%d.%m.%Y %H:%M') }}
{% endif %}
Zalogowano po resecie
{{ 'Tak' if resolution.login_after_reset else 'Nie' }}
Aktywny token
{{ 'Tak' if resolution.has_active_token else 'Nie' }}
Email powitalny
{{ 'Tak' if resolution.has_welcome_email else 'NIE WYSŁANO' }}
{% if resolution.duration %}
Czas do rozwiązania
{{ resolution.duration }}
{% endif %}
{% endif %}

Oś czasu aktywności

{% if timeline %} {% if timeline|length >= 150 %}

Wyświetlono ostatnie 150 zdarzeń. Starsze zdarzenia są ukryte.

{% endif %}
{% for event in timeline %}
{% if event.icon == 'key' %}🔑{% elif event.icon == 'eye' %}👁{% elif event.icon == 'search' %}🔍{% elif event.icon == 'check' %}✓{% elif event.icon == 'alert' %}⚠{% elif event.icon == 'shield' %}🛡{% elif event.icon == 'x' %}✗{% elif event.icon == 'mail' %}📧{% elif event.icon == 'monitor' %}🖥{% elif event.icon == 'logout' %}🚪{% elif event.icon == 'user' %}👤{% elif event.icon == 'info' %}ℹ{% else %}•{% endif %}
{{ event.desc }}
{% if event.detail %}
{{ event.detail }}
{% endif %}
{{ event.time|local_time('%d.%m.%Y %H:%M') }}
{% endfor %}
{% else %}

Brak zdarzeń.

{% endif %}

Ulubione strony (30d)

{% for p in fav_pages %}
{{ p.path }}
{{ p.count }}
{% endfor %} {% if not fav_pages %}

Brak danych.

{% endif %}

Wzorzec godzinowy (30d)

{% set max_h = hourly_bars|map(attribute='count')|max if hourly_bars|map(attribute='count')|max > 0 else 1 %} {% for h in hourly_bars %}
{% endfor %}
{% for h in hourly_bars %}
{% if h.hour % 3 == 0 %}{{ h.hour }}{% endif %}
{% endfor %}

Urządzenia i przeglądarki

URZĄDZENIA {% for d in devices %}
{{ d.type|capitalize }}: {{ d.count }}
{% endfor %}
PRZEGLĄDARKI {% for b in browsers %}
{{ b.name }}: {{ b.count }}
{% endfor %}

Historia problemów

Błędy JS

{% if js_errors %} {% for e in js_errors %} {% endfor %}
WiadomośćStronaData
{{ e.message[:100] }} {{ e.url[:50] if e.url else 'N/A' }} {{ e.occurred_at|local_time('%d.%m %H:%M') }}
{% else %}

Brak błędów JS.

{% endif %}

Wolne strony (> 3s)

{% if slow_pages %} {% for p in slow_pages %} {% endfor %}
StronaCzasData
{{ p.path }} {{ p.load_time_ms }}ms {{ p.viewed_at|local_time('%d.%m %H:%M') }}
{% else %}

Brak wolnych stron.

{% endif %}

Trend zaangażowania (30 dni)

{% if search_queries %}

Ostatnie wyszukiwania

{% for s in search_queries %} "{{ s.query }}" {{ s.searched_at|local_time('%d.%m') }} {% endfor %}
{% endif %}
{% endblock %} {% block extra_js %} const trendData = {{ trend_data|tojson|safe }}; const trendCtx = document.getElementById('trendChart').getContext('2d'); new Chart(trendCtx, { type: 'line', data: { labels: trendData.labels, datasets: [{ label: 'Dzienny score', data: trendData.scores, borderColor: '#6366f1', backgroundColor: 'rgba(99, 102, 241, 0.1)', fill: true, tension: 0.4, pointRadius: 2 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { grid: { display: false } }, y: { beginAtZero: true, suggestedMax: Math.max(30, ...trendData.scores) + 5 } } } }); // Password reset button document.querySelectorAll('.btn-reset-pw').forEach(function(btn) { btn.addEventListener('click', async function() { const uid = this.dataset.uid; const name = this.dataset.name; const email = this.dataset.email; if (!confirm('Wysłać reset hasła do ' + name + ' (' + email + ')?')) return; this.disabled = true; this.textContent = '⏳...'; try { const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || ''; const resp = await fetch('/admin/analytics/send-reset/' + uid, { method: 'POST', headers: {'X-CSRFToken': csrfToken} }); const data = await resp.json(); if (data.success) { this.textContent = '✓ Wysłano'; this.classList.add('sent'); } else { this.textContent = '✗ ' + (data.error || 'Błąd'); this.classList.add('error'); this.disabled = false; } } catch (e) { this.textContent = '✗ Błąd'; this.classList.add('error'); this.disabled = false; } }); }); {% endblock %}