- Django 5.2 + PostgreSQL + Gunicorn - Conversazioni, Obiettivi, Documenti PDF, Persone - Commenti e aggiornamenti con modifica/eliminazione - Agenda, ricerca live, giorni rimanenti scadenze - Bootstrap 5 + HTMX + toast notifications - Deploy: Nginx + Gunicorn + SSL
238 lines
13 KiB
HTML
238 lines
13 KiB
HTML
{% 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 %}
|
||
|
||
</div>
|
||
|
||
</div>
|
||
{% endblock %}
|
||
|