Files
diario_coversazioni/templates/diario/dashboard.html
automationkriz 09f51b1227 Tag progetto, @menzioni, appuntamenti da conversazioni
- Modello Tag con nome e colore, M2M su Conversazione
- Modello Appuntamento con luogo, note, partecipanti, link a Conversazione
- @menzioni nei commenti e aggiornamenti: @username → link al profilo
- Autocomplete JS per @menzioni nelle textarea
- Auto-data conversazioni (default=now)
- CRUD completo appuntamenti con permessi autore
- Appuntamenti in agenda, dashboard, dettaglio conversazione
- Crea riunione direttamente da una conversazione (pre-compila titolo e partecipanti)
- Admin: Tag, Appuntamento registrati
2026-04-07 14:28:47 +00:00

264 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "diario/base.html" %}
{% load custom_filters %}
{% block title %}Dashboard Olimpic Nastri{% endblock %}
{% block content %}
<div class="row g-4">
<!-- ── Colonna principale: Timeline ── -->
<div class="col-lg-7">
<div class="d-flex justify-content-between align-items-center mb-3 fade-in">
<p class="section-title mb-0">Cronologia attività</p>
<a href="{% url 'conversazione_nuova' %}" class="btn btn-primary btn-sm px-3">
<i class="bi bi-plus-lg me-1"></i>Nuova conversazione
</a>
</div>
<div class="timeline">
{% for evento in eventi %}
<div class="tl-item fade-in">
{% if evento.tipo == 'conversazione' %}
<span class="tl-dot conv"></span>
<div class="card p-3">
<div class="d-flex justify-content-between align-items-start">
<div>
<span class="badge bg-primary bg-opacity-10 text-primary pill-tipo mb-1">
<i class="bi bi-chat-quote me-1"></i>Conversazione
</span>
<div class="fw-semibold">
<a href="{% url 'conversazione_dettaglio' evento.obj.pk %}" class="text-decoration-none text-dark">
{{ evento.obj.titolo }}
</a>
</div>
</div>
<small class="text-muted text-nowrap ms-2">{{ evento.data|date:"d/m H:i" }}</small>
</div>
<p class="text-muted small mb-1 mt-2">{{ evento.obj.contenuto|truncatewords:25 }}</p>
<div class="d-flex align-items-center gap-2 mt-1">
<span class="avatar" style="width:22px;height:22px;font-size:.62rem;">
{{ evento.obj.registrato_da.username|slice:":2"|upper }}
</span>
<small class="text-muted">{{ evento.obj.registrato_da.get_full_name|default:evento.obj.registrato_da.username }}</small>
{% if evento.obj.partecipanti.count > 0 %}
<small class="text-muted">· {{ evento.obj.partecipanti.count }} partecipanti</small>
{% endif %}
</div>
</div>
{% elif evento.tipo == 'documento' %}
<span class="tl-dot doc"></span>
<div class="card p-3">
<div class="d-flex justify-content-between align-items-start">
<div>
<span class="badge mb-1 pill-tipo" style="background:#ecfdf5;color:#059669;">
<i class="bi bi-file-earmark-pdf me-1"></i>Documento
</span>
<div class="fw-semibold">
<a href="{% url 'documento_dettaglio' evento.obj.pk %}" class="text-decoration-none text-dark">
{{ evento.obj.titolo }}
</a>
</div>
</div>
<small class="text-muted text-nowrap ms-2">{{ evento.data|date:"d/m H:i" }}</small>
</div>
{% if evento.obj.descrizione %}
<p class="text-muted small mb-1 mt-2">{{ evento.obj.descrizione|truncatewords:20 }}</p>
{% endif %}
<div class="d-flex align-items-center gap-2 mt-1">
<span class="avatar" style="width:22px;height:22px;font-size:.62rem;">
{{ evento.obj.caricato_da.username|slice:":2"|upper }}
</span>
<small class="text-muted">{{ evento.obj.caricato_da.get_full_name|default:evento.obj.caricato_da.username }}</small>
</div>
</div>
{% else %}
<span class="tl-dot agg"></span>
<div class="card p-3">
<div class="d-flex justify-content-between align-items-start">
<div>
<span class="badge mb-1 pill-tipo" style="background:#ffedd5;color:#c2410c;">
<i class="bi bi-arrow-up-circle me-1"></i>Aggiornamento obiettivo
</span>
<div class="fw-semibold">
<a href="{% url 'obiettivo_dettaglio' evento.obj.obiettivo.pk %}" class="text-decoration-none text-dark">
{{ evento.obj.obiettivo.titolo }}
</a>
</div>
</div>
<small class="text-muted text-nowrap ms-2">{{ evento.data|date:"d/m H:i" }}</small>
</div>
<p class="text-muted small mb-1 mt-2">{{ evento.obj.testo|truncatewords:25 }}</p>
<div class="d-flex align-items-center gap-2 mt-1">
<span class="avatar" style="width:22px;height:22px;font-size:.62rem;">
{{ evento.obj.autore.username|slice:":2"|upper }}
</span>
<small class="text-muted">{{ evento.obj.autore.get_full_name|default:evento.obj.autore.username }}</small>
</div>
</div>
{% endif %}
</div>
{% empty %}
<div class="empty-state">
<i class="bi bi-hourglass"></i>
<p>Nessuna attività ancora. Inizia registrando una conversazione.</p>
</div>
{% endfor %}
</div>
</div>
<!-- ── Sidebar destra ── -->
<div class="col-lg-5">
<!-- Obiettivi aperti con slider AJAX -->
<div class="card mb-4 fade-in">
<div class="card-header-accent d-flex justify-content-between align-items-center">
<span><i class="bi bi-bullseye me-2"></i>Obiettivi in corso</span>
<a href="{% url 'obiettivi_lista' %}" class="small fw-normal" style="color:var(--accent);">Vedi tutti →</a>
</div>
<div class="p-3">
{% for obj in obiettivi_aperti %}
<div class="mb-3 {% if not forloop.last %}pb-3 border-bottom{% endif %}">
<div class="d-flex justify-content-between align-items-center mb-1">
<a href="{% url 'obiettivo_dettaglio' obj.pk %}" class="text-decoration-none text-dark fw-semibold small">
{{ obj.titolo }}
</a>
<span class="badge-stato stato-{{ obj.stato }}">{{ obj.get_stato_display }}</span>
</div>
{% if obj.data_scadenza %}
<div class="mb-1">
{% with days=obj.giorni_rimanenti %}
{% if days is not None %}
{% if days < 0 %}
<span class="countdown countdown-urgent" style="font-size:.65rem;"><i class="bi bi-exclamation-triangle-fill me-1"></i>Scaduto da {{ days|abs_val }} gg</span>
{% elif days == 0 %}
<span class="countdown countdown-urgent" style="font-size:.65rem;">Scade oggi!</span>
{% elif days <= 3 %}
<span class="countdown countdown-urgent" style="font-size:.65rem;">{{ days }} gg rimasti</span>
{% elif days <= 7 %}
<span class="countdown countdown-soon" style="font-size:.65rem;">{{ days }} gg rimasti</span>
{% else %}
<span class="countdown countdown-ok" style="font-size:.65rem;">{{ days }} gg · {{ obj.data_scadenza|date:"d/m" }}</span>
{% endif %}
{% endif %}
{% endwith %}
</div>
{% endif %}
<!-- Slider AJAX -->
<div class="d-flex align-items-center gap-2 mt-2">
<input
type="range" min="0" max="100" step="5"
value="{{ obj.avanzamento }}"
class="progress-slider flex-grow-1"
style="--val: {{ obj.avanzamento }}%;"
data-url="{% url 'obiettivo_avanzamento_ajax' obj.pk %}"
data-id="{{ obj.pk }}"
>
<span class="slider-label" id="lbl-{{ obj.pk }}">{{ obj.avanzamento }}%</span>
</div>
<span class="slider-saving d-none" id="saving-{{ obj.pk }}">salvataggio…</span>
</div>
{% empty %}
<p class="text-muted small text-center py-3 mb-0">Tutti gli obiettivi sono completati!</p>
{% endfor %}
<div class="mt-2">
<a href="{% url 'obiettivo_nuovo' %}" class="btn btn-sm btn-outline-secondary w-100">
<i class="bi bi-plus-lg me-1"></i>Aggiungi obiettivo
</a>
</div>
</div>
</div>
<!-- Agenda rapida -->
<div class="card mb-4 fade-in">
<div class="card-header-accent d-flex justify-content-between align-items-center" style="background:#fff7ed; color:#c2410c; border-bottom-color:#fed7aa;">
<span><i class="bi bi-calendar-week me-2"></i>Prossime scadenze</span>
<a href="{% url 'agenda' %}" class="small fw-normal" style="color:#c2410c;">Agenda →</a>
</div>
<div class="p-0">
{% for obj in scadenze_prossime %}
<div class="d-flex align-items-center gap-3 px-3 py-2 {% if not forloop.last %}border-bottom{% endif %}">
<div class="text-center flex-shrink-0" style="width:36px;">
<div style="font-size:.85rem; font-weight:700; color:var(--accent);">{{ obj.data_scadenza|date:"d" }}</div>
<div style="font-size:.6rem; text-transform:uppercase; color:#94a3b8;">{{ obj.data_scadenza|date:"M" }}</div>
</div>
<div class="flex-grow-1">
<a href="{% url 'obiettivo_dettaglio' obj.pk %}" class="text-decoration-none text-dark fw-semibold small d-block">
{{ obj.titolo|truncatewords:8 }}
</a>
{% with days=obj.giorni_rimanenti %}
{% if days <= 3 %}
<span class="countdown countdown-urgent" style="font-size:.6rem;">{{ days }} gg</span>
{% elif days <= 7 %}
<span class="countdown countdown-soon" style="font-size:.6rem;">{{ days }} gg</span>
{% else %}
<span class="countdown countdown-ok" style="font-size:.6rem;">{{ days }} gg</span>
{% endif %}
{% endwith %}
</div>
<div style="width:50px;">
<div class="progress" style="height:4px; border-radius:2px;">
<div class="progress-bar" style="width:{{ obj.avanzamento }}%; background:var(--accent);"></div>
</div>
</div>
</div>
{% empty %}
<p class="text-muted small text-center py-3 mb-0">Nessuna scadenza prossima</p>
{% endfor %}
</div>
</div>
{% if scaduti %}
<!-- Scaduti -->
<div class="card fade-in">
<div class="card-header-accent d-flex align-items-center" style="background:#fef2f2; color:#dc2626; border-bottom-color:#fecaca;">
<i class="bi bi-exclamation-triangle me-2"></i>
<span>Obiettivi scaduti</span>
<span class="badge bg-danger bg-opacity-10 text-danger ms-2">{{ scaduti|length }}</span>
</div>
<div class="p-0">
{% for obj in scaduti %}
<div class="d-flex align-items-center gap-3 px-3 py-2 {% if not forloop.last %}border-bottom{% endif %}">
<i class="bi bi-exclamation-circle text-danger"></i>
<div class="flex-grow-1">
<a href="{% url 'obiettivo_dettaglio' obj.pk %}" class="text-decoration-none text-danger fw-semibold small d-block">
{{ obj.titolo|truncatewords:8 }}
</a>
<small class="text-muted">Scaduto il {{ obj.data_scadenza|date:"d/m/Y" }}</small>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Prossimi appuntamenti -->
{% if prossimi_appuntamenti %}
<div class="card mt-4 fade-in">
<div class="card-header-accent d-flex justify-content-between align-items-center" style="background:#fef3c7; color:#d97706; border-bottom-color:#fde68a;">
<span><i class="bi bi-calendar-check me-2"></i>Prossimi appuntamenti</span>
<a href="{% url 'appuntamenti_lista' %}" class="small fw-normal" style="color:#d97706;">Tutti →</a>
</div>
<div class="p-0">
{% for app in prossimi_appuntamenti %}
<div class="d-flex align-items-center gap-3 px-3 py-2 {% if not forloop.last %}border-bottom{% endif %}">
<div class="text-center flex-shrink-0" style="width:36px;">
<div style="font-size:.85rem; font-weight:700; color:#d97706;">{{ app.data_ora|date:"d" }}</div>
<div style="font-size:.6rem; text-transform:uppercase; color:#94a3b8;">{{ app.data_ora|date:"M" }}</div>
</div>
<div class="flex-grow-1">
<a href="{% url 'appuntamento_dettaglio' app.pk %}" class="text-decoration-none text-dark fw-semibold small d-block">
{{ app.titolo|truncatewords:6 }}
</a>
<small class="text-muted">{{ app.data_ora|date:"H:i" }}{% if app.luogo %} · {{ app.luogo }}{% endif %}</small>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}