Initial commit: Diario Conversazioni Olimpic Nastri
- 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
This commit is contained in:
164
templates/diario/agenda.html
Normal file
164
templates/diario/agenda.html
Normal file
@@ -0,0 +1,164 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% load custom_filters %}
|
||||
{% block title %}Agenda – Olimpic Nastri{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-9">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 fade-in">
|
||||
<div>
|
||||
<p class="section-title mb-0">Agenda</p>
|
||||
<small class="text-muted">Panoramica di scadenze e appuntamenti</small>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{% url 'obiettivo_nuovo' %}" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-plus-lg me-1"></i>Obiettivo
|
||||
</a>
|
||||
<a href="{% url 'conversazione_nuova' %}" class="btn btn-sm btn-primary">
|
||||
<i class="bi bi-plus-lg me-1"></i>Conversazione
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Prossimi eventi ── -->
|
||||
<div class="card mb-4 fade-in">
|
||||
<div class="card-header-accent d-flex align-items-center">
|
||||
<i class="bi bi-arrow-right-circle me-2"></i>
|
||||
<span>Prossimi eventi</span>
|
||||
<span class="badge bg-primary bg-opacity-10 text-primary ms-2">{{ eventi_futuri|length }}</span>
|
||||
</div>
|
||||
<div class="p-0">
|
||||
{% for ev in eventi_futuri %}
|
||||
<div class="d-flex align-items-center gap-3 px-4 py-3 {% if not forloop.last %}border-bottom{% endif %} agenda-row">
|
||||
<!-- Data -->
|
||||
<div class="agenda-date text-center flex-shrink-0">
|
||||
<div class="agenda-day">{{ ev.data|date:"d" }}</div>
|
||||
<div class="agenda-month">{{ ev.data|date:"M" }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Icona tipo -->
|
||||
{% if ev.tipo == 'scadenza' %}
|
||||
<div class="agenda-icon agenda-icon-scadenza">
|
||||
<i class="bi bi-bullseye"></i>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="agenda-icon agenda-icon-conv">
|
||||
<i class="bi bi-chat-quote"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Dettaglio -->
|
||||
<div class="flex-grow-1">
|
||||
{% if ev.tipo == 'scadenza' %}
|
||||
<a href="{% url 'obiettivo_dettaglio' ev.obj.pk %}" class="text-decoration-none text-dark fw-semibold d-block">
|
||||
{{ ev.obj.titolo }}
|
||||
</a>
|
||||
<div class="d-flex align-items-center gap-2 mt-1">
|
||||
<span class="badge-stato stato-{{ ev.obj.stato }}">{{ ev.obj.get_stato_display }}</span>
|
||||
<span class="pill-tipo pill-{{ ev.obj.tipo }}">{{ ev.obj.get_tipo_display }}</span>
|
||||
{% with days=ev.obj.giorni_rimanenti %}
|
||||
{% if days == 0 %}
|
||||
<span class="countdown countdown-urgent">Oggi!</span>
|
||||
{% elif days == 1 %}
|
||||
<span class="countdown countdown-urgent">Domani</span>
|
||||
{% elif days <= 7 %}
|
||||
<span class="countdown countdown-soon">{{ days }} giorni</span>
|
||||
{% else %}
|
||||
<span class="countdown countdown-ok">{{ days }} giorni</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="{% url 'conversazione_dettaglio' ev.obj.pk %}" class="text-decoration-none text-dark fw-semibold d-block">
|
||||
{{ ev.obj.titolo }}
|
||||
</a>
|
||||
<div class="d-flex align-items-center gap-2 mt-1">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-clock me-1"></i>{{ ev.obj.data|date:"H:i" }}
|
||||
</small>
|
||||
<small class="text-muted">
|
||||
{{ ev.obj.registrato_da.get_full_name|default:ev.obj.registrato_da.username }}
|
||||
</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Progress (se scadenza) -->
|
||||
{% if ev.tipo == 'scadenza' %}
|
||||
<div class="d-none d-md-block" style="width:80px;">
|
||||
<div class="progress" style="height:6px; border-radius:3px;">
|
||||
<div class="progress-bar" role="progressbar"
|
||||
style="width:{{ ev.obj.avanzamento }}%; background:var(--accent);">
|
||||
</div>
|
||||
</div>
|
||||
<small class="text-muted d-block text-center mt-1" style="font-size:.7rem;">{{ ev.obj.avanzamento }}%</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="empty-state py-4">
|
||||
<i class="bi bi-calendar-check"></i>
|
||||
<p>Nessun evento programmato. Tutto in ordine!</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Eventi passati ── -->
|
||||
<div class="card fade-in">
|
||||
<div class="card-header-accent d-flex align-items-center" style="background:#f8f9fa; color:#64748b; border-bottom-color:#e2e8f0;">
|
||||
<i class="bi bi-clock-history me-2"></i>
|
||||
<span>Ultimi 30 giorni</span>
|
||||
<span class="badge bg-secondary bg-opacity-10 text-secondary ms-2">{{ eventi_passati|length }}</span>
|
||||
</div>
|
||||
<div class="p-0">
|
||||
{% for ev in eventi_passati %}
|
||||
<div class="d-flex align-items-center gap-3 px-4 py-3 {% if not forloop.last %}border-bottom{% endif %} agenda-row {% if ev.scaduto %}agenda-row-scaduto{% endif %}">
|
||||
<!-- Data -->
|
||||
<div class="agenda-date text-center flex-shrink-0 {% if ev.scaduto %}text-danger{% endif %}">
|
||||
<div class="agenda-day">{{ ev.data|date:"d" }}</div>
|
||||
<div class="agenda-month">{{ ev.data|date:"M" }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Icona -->
|
||||
{% if ev.tipo == 'scadenza' %}
|
||||
<div class="agenda-icon {% if ev.scaduto %}agenda-icon-danger{% else %}agenda-icon-scadenza{% endif %}">
|
||||
<i class="bi {% if ev.scaduto %}bi-exclamation-triangle{% else %}bi-bullseye{% endif %}"></i>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="agenda-icon agenda-icon-conv" style="opacity:.6;">
|
||||
<i class="bi bi-chat-quote"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Dettaglio -->
|
||||
<div class="flex-grow-1">
|
||||
{% if ev.tipo == 'scadenza' %}
|
||||
<a href="{% url 'obiettivo_dettaglio' ev.obj.pk %}" class="text-decoration-none {% if ev.scaduto %}text-danger{% else %}text-dark{% endif %} fw-semibold d-block">
|
||||
{{ ev.obj.titolo }}
|
||||
{% if ev.scaduto %}<small class="text-danger fw-normal">(scaduto)</small>{% endif %}
|
||||
</a>
|
||||
<div class="d-flex align-items-center gap-2 mt-1">
|
||||
<span class="badge-stato stato-{{ ev.obj.stato }}">{{ ev.obj.get_stato_display }}</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="{% url 'conversazione_dettaglio' ev.obj.pk %}" class="text-decoration-none text-muted fw-semibold d-block">
|
||||
{{ ev.obj.titolo }}
|
||||
</a>
|
||||
<small class="text-muted">{{ ev.obj.registrato_da.get_full_name|default:ev.obj.registrato_da.username }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="empty-state py-4">
|
||||
<i class="bi bi-clock-history"></i>
|
||||
<p>Nessun evento negli ultimi 30 giorni.</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
120
templates/diario/base.html
Normal file
120
templates/diario/base.html
Normal file
@@ -0,0 +1,120 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Olimpic Nastri{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||
<link href="{% static 'css/style.css' %}" rel="stylesheet">
|
||||
</head>
|
||||
<body hx-boost="true">
|
||||
|
||||
<nav class="navbar navbar-expand-lg mb-4">
|
||||
<div class="container-lg">
|
||||
<a class="navbar-brand" href="{% url 'dashboard' %}">
|
||||
<i class="bi bi-journal-richtext me-2" style="color:#7c8ff9;"></i>Olimpic <span>Nastri</span>
|
||||
</a>
|
||||
<button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navMenu">
|
||||
<ul class="navbar-nav me-auto gap-1">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 py-1 rounded-pill" href="{% url 'dashboard' %}">
|
||||
<i class="bi bi-house-door me-1"></i>Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 py-1 rounded-pill" href="{% url 'conversazioni_lista' %}">
|
||||
<i class="bi bi-chat-quote me-1"></i>Conversazioni
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 py-1 rounded-pill" href="{% url 'obiettivi_lista' %}">
|
||||
<i class="bi bi-bullseye me-1"></i>Obiettivi
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 py-1 rounded-pill" href="{% url 'agenda' %}">
|
||||
<i class="bi bi-calendar-week me-1"></i>Agenda
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 py-1 rounded-pill" href="{% url 'documenti_lista' %}">
|
||||
<i class="bi bi-file-earmark-pdf me-1"></i>Documenti
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 py-1 rounded-pill" href="{% url 'persone_lista' %}">
|
||||
<i class="bi bi-people me-1"></i>Persone
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% if user.is_authenticated %}
|
||||
<ul class="navbar-nav align-items-center gap-2">
|
||||
<!-- Search bar -->
|
||||
<li class="nav-item">
|
||||
<div class="search-wrapper">
|
||||
<i class="bi bi-search search-icon"></i>
|
||||
<form action="{% url 'ricerca' %}" method="get" hx-boost="true">
|
||||
<input type="search" name="q" id="search-input"
|
||||
class="form-control form-control-sm"
|
||||
placeholder="Cerca..." autocomplete="off">
|
||||
</form>
|
||||
<div id="search-dropdown" class="search-results-dropdown"></div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'conversazione_nuova' %}" class="btn btn-sm btn-primary px-3">
|
||||
<i class="bi bi-plus-lg me-1"></i>Registra
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle d-flex align-items-center gap-2 px-2" href="#" data-bs-toggle="dropdown" data-bs-auto-close="true">
|
||||
<span class="avatar">{{ user.username|slice:":2"|upper }}</span>
|
||||
<span class="d-none d-sm-inline" style="color:rgba(255,255,255,.85);">{{ user.get_full_name|default:user.username }}</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end shadow border-0" style="border-radius:12px; min-width:180px;">
|
||||
{% if user.is_staff %}
|
||||
<li><a class="dropdown-item rounded-3" href="/admin/" hx-boost="false"><i class="bi bi-gear me-2 text-muted"></i>Pannello Admin</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<form method="post" action="{% url 'logout' %}" hx-boost="false">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="dropdown-item rounded-3 text-danger">
|
||||
<i class="bi bi-box-arrow-right me-2"></i>Esci
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-lg" id="main-content">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div data-toast-message="{{ message }}" data-toast-type="{% if message.tags == 'error' %}danger{% else %}{{ message.tags }}{% endif %}" class="d-none"></div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<!-- Toast container -->
|
||||
<div id="toast-container" class="toast-container"></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||
<script src="{% static 'js/app.js' %}"></script>
|
||||
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
33
templates/diario/commento_modifica.html
Normal file
33
templates/diario/commento_modifica.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}Modifica {{ tipo }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-6 fade-in">
|
||||
|
||||
<div class="d-flex align-items-center mb-4 gap-3">
|
||||
<a href="{{ back_url }}" class="btn btn-icon btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left" style="font-size:.85rem;"></i>
|
||||
</a>
|
||||
<h5 class="mb-0 fw-bold">Modifica {{ tipo }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="card p-4">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold small">Testo</label>
|
||||
{{ form.testo }}
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary px-4">
|
||||
<i class="bi bi-check-lg me-1"></i>Salva
|
||||
</button>
|
||||
<a href="{{ back_url }}" class="btn btn-outline-secondary px-4">Annulla</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
30
templates/diario/conferma_elimina.html
Normal file
30
templates/diario/conferma_elimina.html
Normal file
@@ -0,0 +1,30 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}Conferma eliminazione{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-6 fade-in">
|
||||
|
||||
<div class="card p-4 text-center">
|
||||
<div class="mb-3">
|
||||
<i class="bi bi-exclamation-triangle" style="font-size:2.5rem; color:#ef4444;"></i>
|
||||
</div>
|
||||
<h5 class="fw-bold mb-2">Elimina {{ tipo }}</h5>
|
||||
<p class="text-muted mb-4">
|
||||
Stai per eliminare <strong>{{ oggetto }}</strong>.<br>
|
||||
Questa azione è irreversibile.
|
||||
</p>
|
||||
<div class="d-flex justify-content-center gap-2">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger px-4">
|
||||
<i class="bi bi-trash me-1"></i>Elimina
|
||||
</button>
|
||||
</form>
|
||||
<a href="{% if cancel_pk %}{% url cancel_url cancel_pk %}{% else %}{% url cancel_url oggetto.pk %}{% endif %}" class="btn btn-outline-secondary px-4">Annulla</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
122
templates/diario/conversazioni/dettaglio.html
Normal file
122
templates/diario/conversazioni/dettaglio.html
Normal file
@@ -0,0 +1,122 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}{{ conv.titolo }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
|
||||
<div class="d-flex align-items-center mb-4 gap-3 fade-in">
|
||||
<a href="{% url 'conversazioni_lista' %}" class="btn btn-icon btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left" style="font-size:.85rem;"></i>
|
||||
</a>
|
||||
<h5 class="mb-0 fw-bold flex-grow-1">{{ conv.titolo }}</h5>
|
||||
{% if can_edit %}
|
||||
<a href="{% url 'conversazione_modifica' conv.pk %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-pencil me-1"></i>Modifica
|
||||
</a>
|
||||
<a href="{% url 'conversazione_elimina' conv.pk %}" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-trash me-1"></i>Elimina
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card p-4 mb-4 fade-in">
|
||||
<div class="d-flex align-items-center gap-3 mb-4">
|
||||
<span class="avatar">{{ conv.registrato_da.username|slice:":2"|upper }}</span>
|
||||
<div>
|
||||
<div class="fw-semibold small">{{ conv.registrato_da.get_full_name|default:conv.registrato_da.username }}</div>
|
||||
<small class="text-muted">{{ conv.data|date:"d/m/Y \a\l\l\e H:i" }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="white-space:pre-wrap;line-height:1.8;font-size:.93rem;">{{ conv.contenuto }}</div>
|
||||
|
||||
{% if conv.partecipanti.all %}
|
||||
<hr class="soft mt-4 mb-3">
|
||||
<div>
|
||||
<small class="text-muted fw-semibold d-block mb-2">Partecipanti</small>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
{% for p in conv.partecipanti.all %}
|
||||
<div class="d-flex align-items-center gap-1 px-2 py-1 rounded-pill" style="background:#f0f2f5; font-size:.8rem;">
|
||||
<span class="avatar" style="width:20px;height:20px;font-size:.55rem;">{{ p.username|slice:":2"|upper }}</span>
|
||||
{{ p.get_full_name|default:p.username }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Documenti allegati -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 fade-in">
|
||||
<p class="section-title mb-0"><i class="bi bi-paperclip me-1"></i>Allegati ({{ documenti|length }})</p>
|
||||
<a href="{% url 'documento_nuovo' %}?conversazione={{ conv.pk }}" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-upload me-1"></i>Allega PDF
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% for doc in documenti %}
|
||||
<div class="card mb-2 p-3 fade-in">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<i class="bi bi-file-earmark-pdf" style="font-size:1.5rem;color:#dc3545;"></i>
|
||||
<div class="flex-grow-1">
|
||||
<a href="{% url 'documento_dettaglio' doc.pk %}" class="text-decoration-none text-dark fw-semibold small d-block">
|
||||
{{ doc.titolo }}
|
||||
</a>
|
||||
<small class="text-muted">{{ doc.caricato_da.get_full_name|default:doc.caricato_da.username }} · {{ doc.data_caricamento|date:"d/m/Y" }}</small>
|
||||
</div>
|
||||
<a href="{{ doc.file.url }}" class="btn btn-sm btn-outline-secondary" hx-boost="false" target="_blank">
|
||||
<i class="bi bi-download"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="text-muted small text-center py-2 fade-in">Nessun documento allegato.</p>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Commenti -->
|
||||
<p class="section-title mt-4 fade-in"><i class="bi bi-chat-dots me-1"></i>Commenti ({{ commenti|length }})</p>
|
||||
|
||||
<!-- Form nuovo commento -->
|
||||
<div class="card p-3 mb-4 fade-in" style="border-left: 3px solid var(--accent) !important;">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<label class="form-label fw-semibold small mb-2">Aggiungi un commento</label>
|
||||
{{ comment_form.testo }}
|
||||
<button type="submit" class="btn btn-primary btn-sm mt-2 px-3">
|
||||
<i class="bi bi-send me-1"></i>Pubblica
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% for c in commenti %}
|
||||
<div class="card mb-2 p-3 fade-in">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="avatar" style="width:28px;height:28px;font-size:.65rem;">{{ c.autore.username|slice:":2"|upper }}</span>
|
||||
<span class="fw-semibold small">{{ c.autore.get_full_name|default:c.autore.username }}</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<small class="text-muted">{{ c.data|date:"d/m/Y H:i" }}</small>
|
||||
{% if user == c.autore or user.is_superuser %}
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon btn-outline-secondary" style="width:24px;height:24px;" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-three-dots" style="font-size:.7rem;"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end shadow border-0" style="border-radius:10px; min-width:140px;">
|
||||
<li><a class="dropdown-item small" href="{% url 'commento_modifica' c.pk %}"><i class="bi bi-pencil me-2 text-muted"></i>Modifica</a></li>
|
||||
<li><a class="dropdown-item small text-danger" href="{% url 'commento_elimina' c.pk %}"><i class="bi bi-trash me-2"></i>Elimina</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0 mt-2 small" style="white-space:pre-wrap;line-height:1.65;padding-left:40px;">{{ c.testo }}</p>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="text-muted small text-center py-3">Nessun commento ancora. Sii il primo!</p>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
57
templates/diario/conversazioni/form.html
Normal file
57
templates/diario/conversazioni/form.html
Normal file
@@ -0,0 +1,57 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}{{ titolo_pagina }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
<a href="{% url 'conversazioni_lista' %}" class="btn btn-sm btn-outline-secondary me-3">
|
||||
<i class="bi bi-arrow-left"></i>
|
||||
</a>
|
||||
<h4 class="mb-0">{{ titolo_pagina }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="card p-4">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">{{ form.titolo.label }}</label>
|
||||
{{ form.titolo }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">{{ form.data.label }}</label>
|
||||
{{ form.data }}
|
||||
<div class="form-text">Puoi inserire una data passata per conversazioni già avvenute.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">{{ form.contenuto.label }}</label>
|
||||
{{ form.contenuto }}
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold">{{ form.partecipanti.label }}</label>
|
||||
<div class="row row-cols-2 row-cols-sm-3 g-2 mt-1">
|
||||
{% for checkbox in form.partecipanti %}
|
||||
<div class="col">
|
||||
<div class="form-check">
|
||||
{{ checkbox.tag }}
|
||||
<label class="form-check-label" for="{{ checkbox.id_for_label }}">
|
||||
{{ checkbox.choice_label }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Salva</button>
|
||||
{% if conv %}
|
||||
<a href="{% url 'conversazione_dettaglio' conv.pk %}" class="btn btn-outline-secondary">Annulla</a>
|
||||
{% else %}
|
||||
<a href="{% url 'conversazioni_lista' %}" class="btn btn-outline-secondary">Annulla</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
44
templates/diario/conversazioni/lista.html
Normal file
44
templates/diario/conversazioni/lista.html
Normal file
@@ -0,0 +1,44 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}Conversazioni{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 fade-in">
|
||||
<p class="section-title mb-0">Cronologia conversazioni</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>
|
||||
|
||||
{% for conv in conversazioni %}
|
||||
<div class="card mb-3 p-3 fade-in">
|
||||
<div class="d-flex justify-content-between align-items-start gap-3">
|
||||
<div class="flex-grow-1">
|
||||
<a href="{% url 'conversazione_dettaglio' conv.pk %}" class="text-decoration-none text-dark fw-semibold d-block mb-1">
|
||||
{{ conv.titolo }}
|
||||
</a>
|
||||
<div class="d-flex align-items-center gap-3 flex-wrap">
|
||||
<small class="text-muted"><i class="bi bi-calendar3 me-1"></i>{{ conv.data|date:"d/m/Y H:i" }}</small>
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
<span class="avatar" style="width:20px;height:20px;font-size:.58rem;">{{ conv.registrato_da.username|slice:":2"|upper }}</span>
|
||||
<small class="text-muted">{{ conv.registrato_da.get_full_name|default:conv.registrato_da.username }}</small>
|
||||
</div>
|
||||
{% if conv.partecipanti.count > 0 %}
|
||||
<small class="text-muted"><i class="bi bi-people me-1"></i>{{ conv.partecipanti.count }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="mb-0 text-muted small mt-2">{{ conv.contenuto|truncatewords:30 }}</p>
|
||||
</div>
|
||||
{% if user == conv.registrato_da or user.is_superuser %}
|
||||
<a href="{% url 'conversazione_modifica' conv.pk %}" class="btn btn-icon btn-outline-secondary flex-shrink-0">
|
||||
<i class="bi bi-pencil" style="font-size:.75rem;"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="empty-state">
|
||||
<i class="bi bi-chat-square-dots"></i>
|
||||
<p>Nessuna conversazione registrata.</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
237
templates/diario/dashboard.html
Normal file
237
templates/diario/dashboard.html
Normal file
@@ -0,0 +1,237 @@
|
||||
{% 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 %}
|
||||
|
||||
70
templates/diario/documenti/dettaglio.html
Normal file
70
templates/diario/documenti/dettaglio.html
Normal file
@@ -0,0 +1,70 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}{{ doc.titolo }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8 fade-in">
|
||||
|
||||
<div class="d-flex align-items-center mb-4 gap-3">
|
||||
<a href="{% url 'documenti_lista' %}" class="btn btn-icon btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left" style="font-size:.85rem;"></i>
|
||||
</a>
|
||||
<h5 class="mb-0 fw-bold flex-grow-1">{{ doc.titolo }}</h5>
|
||||
{% if can_edit %}
|
||||
<a href="{% url 'documento_elimina' doc.pk %}" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-trash me-1"></i>Elimina
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card p-4 mb-4">
|
||||
<div class="d-flex align-items-center gap-3 mb-3">
|
||||
<div class="d-flex align-items-center justify-content-center rounded-3" style="width:52px;height:52px;background:#fef2f2;">
|
||||
<i class="bi bi-file-earmark-pdf" style="font-size:1.6rem;color:#ef4444;"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-semibold">{{ doc.filename }}</div>
|
||||
<small class="text-muted">Caricato il {{ doc.data_caricamento|date:"d/m/Y \a\l\l\e H:i" }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ doc.file.url }}" target="_blank" class="btn btn-primary mb-3" hx-boost="false">
|
||||
<i class="bi bi-download me-2"></i>Scarica PDF
|
||||
</a>
|
||||
|
||||
{% if doc.descrizione %}
|
||||
<hr class="soft my-3">
|
||||
<p class="mb-0" style="white-space:pre-wrap;line-height:1.7;">{{ doc.descrizione }}</p>
|
||||
{% endif %}
|
||||
|
||||
<hr class="soft my-3">
|
||||
<div class="row g-3">
|
||||
<div class="col-sm-4">
|
||||
<small class="text-muted d-block mb-1">Caricato da</small>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="avatar" style="width:22px;height:22px;font-size:.58rem;">{{ doc.caricato_da.username|slice:":2"|upper }}</span>
|
||||
<span class="small">{{ doc.caricato_da.get_full_name|default:doc.caricato_da.username }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if doc.conversazione %}
|
||||
<div class="col-sm-4">
|
||||
<small class="text-muted d-block mb-1">Conversazione</small>
|
||||
<a href="{% url 'conversazione_dettaglio' doc.conversazione.pk %}" class="small text-decoration-none" style="color:var(--accent);">
|
||||
{{ doc.conversazione.titolo }}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if doc.obiettivo %}
|
||||
<div class="col-sm-4">
|
||||
<small class="text-muted d-block mb-1">Obiettivo</small>
|
||||
<a href="{% url 'obiettivo_dettaglio' doc.obiettivo.pk %}" class="small text-decoration-none" style="color:var(--accent);">
|
||||
{{ doc.obiettivo.titolo }}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
53
templates/diario/documenti/form.html
Normal file
53
templates/diario/documenti/form.html
Normal file
@@ -0,0 +1,53 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}{{ titolo_pagina }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8 fade-in">
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
<a href="{% url 'documenti_lista' %}" class="btn btn-sm btn-outline-secondary me-3">
|
||||
<i class="bi bi-arrow-left"></i>
|
||||
</a>
|
||||
<h4 class="mb-0">{{ titolo_pagina }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="card p-4">
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">{{ form.titolo.label }}</label>
|
||||
{{ form.titolo }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">{{ form.descrizione.label }}</label>
|
||||
{{ form.descrizione }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">{{ form.file.label }}</label>
|
||||
{{ form.file }}
|
||||
<div class="form-text">Solo file PDF, massimo 10 MB.</div>
|
||||
{% if form.file.errors %}
|
||||
<div class="text-danger small mt-1">{{ form.file.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label fw-semibold">{{ form.conversazione.label }}</label>
|
||||
{{ form.conversazione }}
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label fw-semibold">{{ form.obiettivo.label }}</label>
|
||||
{{ form.obiettivo }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-upload me-1"></i>Carica
|
||||
</button>
|
||||
<a href="{% url 'documenti_lista' %}" class="btn btn-outline-secondary">Annulla</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
65
templates/diario/documenti/lista.html
Normal file
65
templates/diario/documenti/lista.html
Normal file
@@ -0,0 +1,65 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}Documenti{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 fade-in">
|
||||
<p class="section-title mb-0">Documenti</p>
|
||||
<a href="{% url 'documento_nuovo' %}" class="btn btn-primary btn-sm px-3">
|
||||
<i class="bi bi-upload me-1"></i>Carica documento
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
{% for doc in documenti %}
|
||||
<div class="col-12 col-md-6 col-xl-4 fade-in">
|
||||
<div class="card h-100 p-3">
|
||||
<div class="d-flex align-items-start gap-3">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="d-flex align-items-center justify-content-center rounded-3" style="width:42px;height:42px;background:#fef2f2;">
|
||||
<i class="bi bi-file-earmark-pdf" style="font-size:1.3rem;color:#ef4444;"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow-1 min-w-0">
|
||||
<a href="{% url 'documento_dettaglio' doc.pk %}" class="text-decoration-none text-dark fw-semibold d-block text-truncate">
|
||||
{{ doc.titolo }}
|
||||
</a>
|
||||
{% if doc.descrizione %}
|
||||
<p class="text-muted small mb-1 mt-1">{{ doc.descrizione|truncatewords:15 }}</p>
|
||||
{% endif %}
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap mt-2">
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
<span class="avatar" style="width:18px;height:18px;font-size:.5rem;">{{ doc.caricato_da.username|slice:":2"|upper }}</span>
|
||||
<small class="text-muted">{{ doc.caricato_da.get_full_name|default:doc.caricato_da.username }}</small>
|
||||
</div>
|
||||
<small class="text-muted">{{ doc.data_caricamento|date:"d/m/Y" }}</small>
|
||||
</div>
|
||||
{% if doc.conversazione %}
|
||||
<div class="mt-1">
|
||||
<a href="{% url 'conversazione_dettaglio' doc.conversazione.pk %}" class="small text-decoration-none" style="color:var(--accent);">
|
||||
<i class="bi bi-link-45deg me-1"></i>{{ doc.conversazione.titolo|truncatewords:5 }}
|
||||
</a>
|
||||
</div>
|
||||
{% elif doc.obiettivo %}
|
||||
<div class="mt-1">
|
||||
<a href="{% url 'obiettivo_dettaglio' doc.obiettivo.pk %}" class="small text-decoration-none" style="color:var(--accent);">
|
||||
<i class="bi bi-link-45deg me-1"></i>{{ doc.obiettivo.titolo|truncatewords:5 }}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="col-12">
|
||||
<div class="empty-state">
|
||||
<i class="bi bi-file-earmark-pdf"></i>
|
||||
<p>Nessun documento caricato.</p>
|
||||
<a href="{% url 'documento_nuovo' %}" class="btn btn-primary btn-sm px-3">
|
||||
<i class="bi bi-upload me-1"></i>Carica il primo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
32
templates/diario/login.html
Normal file
32
templates/diario/login.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login – Olimpic Nastri</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<div class="container" style="max-width:420px; margin-top:100px;">
|
||||
<div class="card shadow-sm p-4">
|
||||
<h3 class="mb-4 text-center fw-bold">Olimpic Progetto Nastri</h3>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger">Username o password errati.</div>
|
||||
{% endif %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Username</label>
|
||||
<input type="text" name="username" class="form-control" autofocus required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Password</label>
|
||||
<input type="password" name="password" class="form-control" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-dark w-100">Entra</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
174
templates/diario/obiettivi/dettaglio.html
Normal file
174
templates/diario/obiettivi/dettaglio.html
Normal file
@@ -0,0 +1,174 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% load custom_filters %}
|
||||
{% block title %}{{ obj.titolo }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="d-flex align-items-center mb-4 gap-3 fade-in">
|
||||
<a href="{% url 'obiettivi_lista' %}" class="btn btn-icon btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left" style="font-size:.85rem;"></i>
|
||||
</a>
|
||||
<div class="flex-grow-1">
|
||||
<h5 class="mb-0 fw-bold">{{ obj.titolo }}</h5>
|
||||
</div>
|
||||
<span class="badge-stato stato-{{ obj.stato }}">{{ obj.get_stato_display }}</span>
|
||||
{% if can_edit %}
|
||||
<a href="{% url 'obiettivo_modifica' obj.pk %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-pencil me-1"></i>Modifica
|
||||
</a>
|
||||
<a href="{% url 'obiettivo_elimina' obj.pk %}" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-trash me-1"></i>Elimina
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Card info -->
|
||||
<div class="card p-4 mb-4 fade-in">
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-6 col-sm-3">
|
||||
<small class="text-muted d-block mb-1">Tipo</small>
|
||||
<span class="pill-tipo pill-{{ obj.tipo }}">{{ obj.get_tipo_display }}</span>
|
||||
</div>
|
||||
{% if obj.assegnato_a.all %}
|
||||
<div class="col-6 col-sm-3">
|
||||
<small class="text-muted d-block mb-1">Assegnato a</small>
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
{% for p in obj.assegnato_a.all %}
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
<span class="avatar" style="width:22px;height:22px;font-size:.58rem;">{{ p.username|slice:":2"|upper }}</span>
|
||||
<span class="small fw-semibold">{{ p.get_full_name|default:p.username }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if obj.data_scadenza %}
|
||||
<div class="col-6 col-sm-3">
|
||||
<small class="text-muted d-block mb-1">Scadenza</small>
|
||||
<strong class="small">{{ obj.data_scadenza|date:"d/m/Y" }}</strong>
|
||||
{% with days=obj.giorni_rimanenti %}
|
||||
{% if days is not None %}
|
||||
<div class="mt-1">
|
||||
{% if days < 0 %}
|
||||
<span class="countdown countdown-urgent"><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">Scade oggi!</span>
|
||||
{% elif days == 1 %}
|
||||
<span class="countdown countdown-urgent">Scade domani</span>
|
||||
{% elif days <= 7 %}
|
||||
<span class="countdown countdown-soon">{{ days }} giorni rimasti</span>
|
||||
{% else %}
|
||||
<span class="countdown countdown-ok">{{ days }} giorni rimasti</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-6 col-sm-3">
|
||||
<small class="text-muted d-block mb-1">Creato da</small>
|
||||
<span class="small">{{ obj.creato_da.get_full_name|default:obj.creato_da.username }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if obj.descrizione %}
|
||||
<hr class="soft my-3">
|
||||
<p class="mb-0" style="white-space:pre-wrap;line-height:1.7;">{{ obj.descrizione }}</p>
|
||||
{% endif %}
|
||||
|
||||
<!-- Slider avanzamento AJAX -->
|
||||
<hr class="soft my-3">
|
||||
<div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<small class="fw-semibold text-muted">Avanzamento</small>
|
||||
<span class="slider-label" id="lbl-{{ obj.pk }}">{{ obj.avanzamento }}%</span>
|
||||
</div>
|
||||
<input
|
||||
type="range" min="0" max="100" step="5"
|
||||
value="{{ obj.avanzamento }}"
|
||||
class="progress-slider w-100"
|
||||
style="--val: {{ obj.avanzamento }}%;"
|
||||
data-url="{% url 'obiettivo_avanzamento_ajax' obj.pk %}"
|
||||
data-id="{{ obj.pk }}"
|
||||
>
|
||||
<span class="slider-saving d-none" id="saving-{{ obj.pk }}">salvataggio…</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Documenti allegati -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 fade-in">
|
||||
<p class="section-title mb-0"><i class="bi bi-paperclip me-1"></i>Allegati ({{ documenti|length }})</p>
|
||||
<a href="{% url 'documento_nuovo' %}?obiettivo={{ obj.pk }}" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-upload me-1"></i>Allega PDF
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% for doc in documenti %}
|
||||
<div class="card mb-2 p-3 fade-in">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<i class="bi bi-file-earmark-pdf" style="font-size:1.5rem;color:#dc3545;"></i>
|
||||
<div class="flex-grow-1">
|
||||
<a href="{% url 'documento_dettaglio' doc.pk %}" class="text-decoration-none text-dark fw-semibold small d-block">
|
||||
{{ doc.titolo }}
|
||||
</a>
|
||||
<small class="text-muted">{{ doc.caricato_da.get_full_name|default:doc.caricato_da.username }} · {{ doc.data_caricamento|date:"d/m/Y" }}</small>
|
||||
</div>
|
||||
<a href="{{ doc.file.url }}" class="btn btn-sm btn-outline-secondary" hx-boost="false" target="_blank">
|
||||
<i class="bi bi-download"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="text-muted small text-center py-2 fade-in">Nessun documento allegato.</p>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Aggiornamenti -->
|
||||
<p class="section-title mt-4 fade-in">Aggiornamenti ({{ aggiornamenti.count }})</p>
|
||||
|
||||
<!-- Form nuovo aggiornamento -->
|
||||
<div class="card p-3 mb-4 fade-in" style="border-left: 3px solid var(--accent) !important;">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<label class="form-label fw-semibold small mb-2">Aggiungi un aggiornamento</label>
|
||||
{{ agg_form.testo }}
|
||||
<button type="submit" class="btn btn-primary btn-sm mt-2 px-3">
|
||||
<i class="bi bi-send me-1"></i>Pubblica
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% for agg in aggiornamenti %}
|
||||
<div class="card mb-2 p-3 fade-in">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="avatar" style="width:28px;height:28px;font-size:.65rem;">{{ agg.autore.username|slice:":2"|upper }}</span>
|
||||
<span class="fw-semibold small">{{ agg.autore.get_full_name|default:agg.autore.username }}</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<small class="text-muted">{{ agg.data|date:"d/m/Y H:i" }}</small>
|
||||
{% if user == agg.autore or user.is_superuser %}
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon btn-outline-secondary" style="width:24px;height:24px;" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-three-dots" style="font-size:.7rem;"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end shadow border-0" style="border-radius:10px; min-width:140px;">
|
||||
<li><a class="dropdown-item small" href="{% url 'aggiornamento_modifica' agg.pk %}"><i class="bi bi-pencil me-2 text-muted"></i>Modifica</a></li>
|
||||
<li><a class="dropdown-item small text-danger" href="{% url 'aggiornamento_elimina' agg.pk %}"><i class="bi bi-trash me-2"></i>Elimina</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0 mt-2 small" style="white-space:pre-wrap;line-height:1.65;padding-left:40px;">{{ agg.testo }}</p>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="text-muted small text-center py-3">Nessun aggiornamento ancora.</p>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
67
templates/diario/obiettivi/form.html
Normal file
67
templates/diario/obiettivi/form.html
Normal file
@@ -0,0 +1,67 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}{{ titolo_pagina }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
<a href="{% url 'obiettivi_lista' %}" class="btn btn-sm btn-outline-secondary me-3">
|
||||
<i class="bi bi-arrow-left"></i>
|
||||
</a>
|
||||
<h4 class="mb-0">{{ titolo_pagina }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="card p-4">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">{{ form.titolo.label }}</label>
|
||||
{{ form.titolo }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">{{ form.descrizione.label }}</label>
|
||||
{{ form.descrizione }}
|
||||
</div>
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label fw-semibold">{{ form.tipo.label }}</label>
|
||||
{{ form.tipo }}
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label fw-semibold">{{ form.stato.label }}</label>
|
||||
{{ form.stato }}
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label fw-semibold">{{ form.data_scadenza.label }}</label>
|
||||
{{ form.data_scadenza }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold">{{ form.assegnato_a.label }}</label>
|
||||
<div class="form-text mb-2">Seleziona una o più persone (solo per obiettivi individuali).</div>
|
||||
<div class="row row-cols-2 row-cols-sm-3 g-2">
|
||||
{% for checkbox in form.assegnato_a %}
|
||||
<div class="col">
|
||||
<div class="form-check">
|
||||
{{ checkbox.tag }}
|
||||
<label class="form-check-label" for="{{ checkbox.id_for_label }}">
|
||||
{{ checkbox.choice_label }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Salva</button>
|
||||
{% if obj %}
|
||||
<a href="{% url 'obiettivo_dettaglio' obj.pk %}" class="btn btn-outline-secondary">Annulla</a>
|
||||
{% else %}
|
||||
<a href="{% url 'obiettivi_lista' %}" class="btn btn-outline-secondary">Annulla</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
101
templates/diario/obiettivi/lista.html
Normal file
101
templates/diario/obiettivi/lista.html
Normal file
@@ -0,0 +1,101 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% load custom_filters %}
|
||||
{% block title %}Obiettivi{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 fade-in">
|
||||
<div>
|
||||
<p class="section-title mb-0">Obiettivi del progetto</p>
|
||||
</div>
|
||||
<a href="{% url 'obiettivo_nuovo' %}" class="btn btn-primary btn-sm px-3">
|
||||
<i class="bi bi-plus-lg me-1"></i>Nuovo obiettivo
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Filtri -->
|
||||
<div class="mb-4 d-flex gap-2 flex-wrap fade-in">
|
||||
<a href="?filtro=tutti" class="btn btn-sm {% if filtro == 'tutti' %}btn-primary{% else %}btn-outline-secondary{% endif %}">Tutti</a>
|
||||
<a href="?filtro=collettivi" class="btn btn-sm {% if filtro == 'collettivi' %}btn-primary{% else %}btn-outline-secondary{% endif %}">Collettivi</a>
|
||||
<a href="?filtro=individuali" class="btn btn-sm {% if filtro == 'individuali' %}btn-primary{% else %}btn-outline-secondary{% endif %}">Individuali</a>
|
||||
<a href="?filtro=miei" class="btn btn-sm {% if filtro == 'miei' %}btn-primary{% else %}btn-outline-secondary{% endif %}">I miei</a>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
{% for obj in obiettivi %}
|
||||
<div class="col-12 col-md-6 col-xl-4">
|
||||
<div class="card h-100 p-3 fade-in">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex gap-2 align-items-center flex-wrap">
|
||||
<span class="badge-stato stato-{{ obj.stato }}">{{ obj.get_stato_display }}</span>
|
||||
<span class="pill-tipo pill-{{ obj.tipo }}">{{ obj.get_tipo_display }}</span>
|
||||
</div>
|
||||
{% if user == obj.creato_da or user.is_superuser %}
|
||||
<a href="{% url 'obiettivo_modifica' obj.pk %}" class="btn btn-icon btn-outline-secondary">
|
||||
<i class="bi bi-pencil" style="font-size:.75rem;"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<a href="{% url 'obiettivo_dettaglio' obj.pk %}" class="text-decoration-none text-dark fw-semibold mb-1 d-block">
|
||||
{{ obj.titolo }}
|
||||
</a>
|
||||
|
||||
{% if obj.descrizione %}
|
||||
<p class="text-muted small mb-2">{{ obj.descrizione|truncatewords:18 }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if obj.assegnato_a.all %}
|
||||
<div class="d-flex align-items-center gap-1 flex-wrap mb-2">
|
||||
{% for p in obj.assegnato_a.all %}
|
||||
<span class="avatar" style="width:22px;height:22px;font-size:.58rem;" title="{{ p.get_full_name|default:p.username }}">{{ p.username|slice:":2"|upper }}</span>
|
||||
{% endfor %}
|
||||
<small class="text-muted ms-1">{% for p in obj.assegnato_a.all %}{{ p.get_full_name|default:p.username }}{% if not forloop.last %}, {% endif %}{% endfor %}</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Progress bar + slider AJAX -->
|
||||
<div class="mt-auto pt-2">
|
||||
<div class="d-flex align-items-center gap-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>
|
||||
{% if obj.data_scadenza %}
|
||||
<div class="d-flex align-items-center gap-2 mt-1">
|
||||
<small class="text-muted"><i class="bi bi-calendar3 me-1"></i>{{ obj.data_scadenza|date:"d/m/Y" }}</small>
|
||||
{% with days=obj.giorni_rimanenti %}
|
||||
{% if days is not None %}
|
||||
{% if days < 0 %}
|
||||
<span class="countdown countdown-urgent" style="font-size:.65rem;">Scaduto</span>
|
||||
{% elif days <= 3 %}
|
||||
<span class="countdown countdown-urgent" style="font-size:.65rem;">{{ days }}gg</span>
|
||||
{% elif days <= 7 %}
|
||||
<span class="countdown countdown-soon" style="font-size:.65rem;">{{ days }}gg</span>
|
||||
{% else %}
|
||||
<span class="countdown countdown-ok" style="font-size:.65rem;">{{ days }}gg</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="col-12">
|
||||
<div class="empty-state">
|
||||
<i class="bi bi-bullseye"></i>
|
||||
<p>Nessun obiettivo trovato per questo filtro.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
104
templates/diario/persone/dettaglio.html
Normal file
104
templates/diario/persone/dettaglio.html
Normal file
@@ -0,0 +1,104 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}{{ persona.get_full_name|default:persona.username }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
|
||||
<div class="d-flex align-items-center mb-4 gap-3 fade-in">
|
||||
<a href="{% url 'persone_lista' %}" class="btn btn-icon btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left" style="font-size:.85rem;"></i>
|
||||
</a>
|
||||
<span class="avatar avatar-lg">{{ persona.username|slice:":2"|upper }}</span>
|
||||
<div>
|
||||
<h5 class="mb-0 fw-bold">{{ persona.get_full_name|default:persona.username }}</h5>
|
||||
<small class="text-muted">@{{ persona.username }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
<ul class="nav nav-pills mb-4 gap-1 fade-in" role="tablist">
|
||||
<li class="nav-item">
|
||||
<button class="nav-link active btn-sm" data-bs-toggle="pill" data-bs-target="#tab-conv">
|
||||
<i class="bi bi-chat-quote me-1"></i>Conversazioni <span class="badge bg-secondary bg-opacity-25 text-dark ms-1">{{ conversazioni|length }}</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="nav-link btn-sm" data-bs-toggle="pill" data-bs-target="#tab-obj">
|
||||
<i class="bi bi-bullseye me-1"></i>Obiettivi <span class="badge bg-secondary bg-opacity-25 text-dark ms-1">{{ obiettivi|length }}</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="nav-link btn-sm" data-bs-toggle="pill" data-bs-target="#tab-doc">
|
||||
<i class="bi bi-file-pdf me-1"></i>Documenti <span class="badge bg-secondary bg-opacity-25 text-dark ms-1">{{ documenti|length }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<!-- Conversazioni -->
|
||||
<div class="tab-pane fade show active" id="tab-conv">
|
||||
{% for conv in conversazioni %}
|
||||
<div class="card mb-2 p-3 fade-in">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<a href="{% url 'conversazione_dettaglio' conv.pk %}" class="text-decoration-none text-dark fw-semibold">{{ conv.titolo }}</a>
|
||||
<div class="text-muted small mt-1">{{ conv.data|date:"d/m/Y H:i" }}</div>
|
||||
</div>
|
||||
{% if conv.registrato_da == persona %}
|
||||
<span class="doc-badge">Autore</span>
|
||||
{% else %}
|
||||
<span class="badge-stato stato-aperto">Partecipante</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="empty-state"><i class="bi bi-chat-square-dots"></i><p>Nessuna conversazione.</p></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Obiettivi -->
|
||||
<div class="tab-pane fade" id="tab-obj">
|
||||
{% for obj in obiettivi %}
|
||||
<div class="card mb-2 p-3 fade-in">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<a href="{% url 'obiettivo_dettaglio' obj.pk %}" class="text-decoration-none text-dark fw-semibold">{{ obj.titolo }}</a>
|
||||
<div class="d-flex gap-2 mt-1">
|
||||
<span class="badge-stato stato-{{ obj.stato }}">{{ obj.get_stato_display }}</span>
|
||||
<span class="pill-tipo pill-{{ obj.tipo }}">{{ obj.get_tipo_display }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if obj.creato_da == persona %}
|
||||
<span class="doc-badge">Creato</span>
|
||||
{% else %}
|
||||
<span class="badge-stato stato-in_corso">Assegnato</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="empty-state"><i class="bi bi-bullseye"></i><p>Nessun obiettivo.</p></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Documenti -->
|
||||
<div class="tab-pane fade" id="tab-doc">
|
||||
{% for doc in documenti %}
|
||||
<div class="card mb-2 p-3 fade-in">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<i class="bi bi-file-earmark-pdf" style="font-size:1.2rem;color:#ef4444;"></i>
|
||||
<div>
|
||||
<a href="{% url 'documento_dettaglio' doc.pk %}" class="text-decoration-none text-dark fw-semibold">{{ doc.titolo }}</a>
|
||||
<div class="text-muted small">{{ doc.data_caricamento|date:"d/m/Y" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="empty-state"><i class="bi bi-file-earmark-pdf"></i><p>Nessun documento.</p></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
33
templates/diario/persone/lista.html
Normal file
33
templates/diario/persone/lista.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}Persone{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p class="section-title fade-in">Team</p>
|
||||
|
||||
<div class="row g-3">
|
||||
{% for persona in persone %}
|
||||
<div class="col-6 col-md-4 col-lg-3 fade-in">
|
||||
<a href="{% url 'persona_dettaglio' persona.pk %}" class="card-link">
|
||||
<div class="card person-card h-100">
|
||||
<span class="avatar avatar-lg mx-auto">{{ persona.username|slice:":2"|upper }}</span>
|
||||
<div class="fw-semibold small">{{ persona.get_full_name|default:persona.username }}</div>
|
||||
<div class="mt-2 d-flex justify-content-center gap-3">
|
||||
<div class="stat">
|
||||
<div class="stat-num">{{ persona.num_conversazioni }}</div>
|
||||
<i class="bi bi-chat-quote"></i>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-num">{{ persona.num_obiettivi }}</div>
|
||||
<i class="bi bi-bullseye"></i>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-num">{{ persona.num_documenti }}</div>
|
||||
<i class="bi bi-file-pdf"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
98
templates/diario/ricerca.html
Normal file
98
templates/diario/ricerca.html
Normal file
@@ -0,0 +1,98 @@
|
||||
{% extends "diario/base.html" %}
|
||||
{% block title %}Ricerca{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8 fade-in">
|
||||
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
<h4 class="mb-0"><i class="bi bi-search me-2" style="color:var(--accent);"></i>Ricerca</h4>
|
||||
</div>
|
||||
|
||||
<form method="get" class="mb-4">
|
||||
<div class="input-group">
|
||||
<input type="search" name="q" value="{{ q }}" class="form-control form-control-lg" placeholder="Cerca conversazioni, obiettivi, documenti, persone..." autofocus>
|
||||
<button type="submit" class="btn btn-primary px-4">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if q %}
|
||||
<p class="text-muted small mb-4">
|
||||
{% if total %}{{ total }} risultat{{ total|pluralize:"o,i" }} per <strong>"{{ q }}"</strong>{% else %}Nessun risultato per <strong>"{{ q }}"</strong>{% endif %}
|
||||
</p>
|
||||
|
||||
{% if risultati.persone %}
|
||||
<p class="section-title"><i class="bi bi-people me-1"></i>Persone</p>
|
||||
<div class="row g-2 mb-4">
|
||||
{% for persona in risultati.persone %}
|
||||
<div class="col-6 col-md-4 fade-in">
|
||||
<a href="{% url 'persona_dettaglio' persona.pk %}" class="card-link">
|
||||
<div class="card p-3">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="avatar" style="width:24px;height:24px;font-size:.6rem;">{{ persona.username|slice:":2"|upper }}</span>
|
||||
<span class="fw-semibold small">{{ persona.get_full_name|default:persona.username }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if risultati.conversazioni %}
|
||||
<p class="section-title"><i class="bi bi-chat-quote me-1"></i>Conversazioni</p>
|
||||
{% for conv in risultati.conversazioni %}
|
||||
<div class="card mb-2 p-3 fade-in">
|
||||
<a href="{% url 'conversazione_dettaglio' conv.pk %}" class="text-decoration-none text-dark fw-semibold">{{ conv.titolo }}</a>
|
||||
<div class="d-flex gap-2 mt-1">
|
||||
<small class="text-muted">{{ conv.data|date:"d/m/Y" }}</small>
|
||||
{% if conv.registrato_da %}
|
||||
<small class="text-muted">· {{ conv.registrato_da.get_full_name|default:conv.registrato_da.username }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="text-muted small mb-0 mt-1">{{ conv.contenuto|truncatewords:25 }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="mb-4"></div>
|
||||
{% endif %}
|
||||
|
||||
{% if risultati.obiettivi %}
|
||||
<p class="section-title"><i class="bi bi-bullseye me-1"></i>Obiettivi</p>
|
||||
{% for obj in risultati.obiettivi %}
|
||||
<div class="card mb-2 p-3 fade-in">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<a href="{% url 'obiettivo_dettaglio' obj.pk %}" class="text-decoration-none text-dark fw-semibold">{{ obj.titolo }}</a>
|
||||
{% if obj.descrizione %}
|
||||
<p class="text-muted small mb-0 mt-1">{{ obj.descrizione|truncatewords:20 }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<span class="badge-stato stato-{{ obj.stato }} ms-2">{{ obj.get_stato_display }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="mb-4"></div>
|
||||
{% endif %}
|
||||
|
||||
{% if risultati.documenti %}
|
||||
<p class="section-title"><i class="bi bi-file-earmark-pdf me-1"></i>Documenti</p>
|
||||
{% for doc in risultati.documenti %}
|
||||
<div class="card mb-2 p-3 fade-in">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<i class="bi bi-file-earmark-pdf" style="color:#ef4444;"></i>
|
||||
<div>
|
||||
<a href="{% url 'documento_dettaglio' doc.pk %}" class="text-decoration-none text-dark fw-semibold">{{ doc.titolo }}</a>
|
||||
{% if doc.descrizione %}<p class="text-muted small mb-0 mt-1">{{ doc.descrizione|truncatewords:15 }}</p>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
40
templates/diario/ricerca_risultati.html
Normal file
40
templates/diario/ricerca_risultati.html
Normal file
@@ -0,0 +1,40 @@
|
||||
{% if total == 0 and q %}
|
||||
<div class="p-3 text-center text-muted small">Nessun risultato per "{{ q }}"</div>
|
||||
{% endif %}
|
||||
|
||||
{% for persona in risultati.persone %}
|
||||
<a href="{% url 'persona_dettaglio' persona.pk %}" class="result-item">
|
||||
<div class="result-type">Persona</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="avatar" style="width:20px;height:20px;font-size:.55rem;">{{ persona.username|slice:":2"|upper }}</span>
|
||||
<span class="fw-semibold small">{{ persona.get_full_name|default:persona.username }}</span>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
{% for conv in risultati.conversazioni|slice:":5" %}
|
||||
<a href="{% url 'conversazione_dettaglio' conv.pk %}" class="result-item">
|
||||
<div class="result-type">Conversazione</div>
|
||||
<span class="small">{{ conv.titolo }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
{% for obj in risultati.obiettivi|slice:":5" %}
|
||||
<a href="{% url 'obiettivo_dettaglio' obj.pk %}" class="result-item">
|
||||
<div class="result-type">Obiettivo</div>
|
||||
<span class="small">{{ obj.titolo }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
{% for doc in risultati.documenti|slice:":5" %}
|
||||
<a href="{% url 'documento_dettaglio' doc.pk %}" class="result-item">
|
||||
<div class="result-type">Documento</div>
|
||||
<span class="small">{{ doc.titolo }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
{% if total > 0 %}
|
||||
<a href="{% url 'ricerca' %}?q={{ q|urlencode }}" class="result-item text-center" style="color:var(--accent);font-weight:600;font-size:.8rem;">
|
||||
Vedi tutti i {{ total }} risultati →
|
||||
</a>
|
||||
{% endif %}
|
||||
Reference in New Issue
Block a user